File: | select/select.c |
Warning: | line 1180, column 6 Array access (via field 'style') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * This file is part of LibCSS | |||
3 | * Licensed under the MIT License, | |||
4 | * http://www.opensource.org/licenses/mit-license.php | |||
5 | * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> | |||
6 | */ | |||
7 | ||||
8 | #include <assert.h> | |||
9 | #include <string.h> | |||
10 | ||||
11 | #include <libwapcaplet/libwapcaplet.h> | |||
12 | ||||
13 | #include <libcss/select.h> | |||
14 | ||||
15 | #include "bytecode/bytecode.h" | |||
16 | #include "bytecode/opcodes.h" | |||
17 | #include "stylesheet.h" | |||
18 | #include "select/arena.h" | |||
19 | #include "select/calc.h" | |||
20 | #include "select/computed.h" | |||
21 | #include "select/dispatch.h" | |||
22 | #include "select/hash.h" | |||
23 | #include "select/mq.h" | |||
24 | #include "select/propset.h" | |||
25 | #include "select/font_face.h" | |||
26 | #include "select/select.h" | |||
27 | #include "select/strings.h" | |||
28 | #include "select/unit.h" | |||
29 | #include "utils/parserutilserror.h" | |||
30 | #include "utils/utils.h" | |||
31 | ||||
32 | /* Define this to enable verbose messages when matching selector chains */ | |||
33 | #undef DEBUG_CHAIN_MATCHING | |||
34 | ||||
35 | /* Define this to enable verbose messages when attempting to share styles */ | |||
36 | #undef DEBUG_STYLE_SHARING | |||
37 | ||||
38 | /** | |||
39 | * Container for stylesheet selection info | |||
40 | */ | |||
41 | typedef struct css_select_sheet { | |||
42 | const css_stylesheet *sheet; /**< Stylesheet */ | |||
43 | css_origin origin; /**< Stylesheet origin */ | |||
44 | css_mq_query *media; /**< Applicable media */ | |||
45 | } css_select_sheet; | |||
46 | ||||
47 | /** | |||
48 | * CSS selection context | |||
49 | */ | |||
50 | struct css_select_ctx { | |||
51 | uint32_t n_sheets; /**< Number of sheets */ | |||
52 | ||||
53 | css_select_sheet *sheets; /**< Array of sheets */ | |||
54 | ||||
55 | void *pw; /**< Client's private selection context */ | |||
56 | ||||
57 | bool_Bool uses_revert; /**< A sheet used revert property value */ | |||
58 | ||||
59 | css_select_strings str; | |||
60 | ||||
61 | css_calculator *calc; /**< A calculator to hand off to computed styles */ | |||
62 | ||||
63 | /* Interned default style */ | |||
64 | css_computed_style *default_style; | |||
65 | }; | |||
66 | ||||
67 | /** | |||
68 | * Container for selected font faces | |||
69 | */ | |||
70 | typedef struct css_select_font_faces_list { | |||
71 | const css_font_face **font_faces; | |||
72 | size_t count; | |||
73 | } css_select_font_faces_list; | |||
74 | ||||
75 | /** | |||
76 | * Font face selection state | |||
77 | */ | |||
78 | typedef struct css_select_font_faces_state { | |||
79 | lwc_string *font_family; | |||
80 | const css_media *media; | |||
81 | const css_unit_ctx *unit_ctx; | |||
82 | ||||
83 | css_select_font_faces_list ua_font_faces; | |||
84 | css_select_font_faces_list user_font_faces; | |||
85 | css_select_font_faces_list author_font_faces; | |||
86 | } css_select_font_faces_state; | |||
87 | ||||
88 | /** | |||
89 | * CSS rule source | |||
90 | */ | |||
91 | typedef struct css_select_rule_source { | |||
92 | enum { | |||
93 | CSS_SELECT_RULE_SRC_ELEMENT, | |||
94 | CSS_SELECT_RULE_SRC_CLASS, | |||
95 | CSS_SELECT_RULE_SRC_ID, | |||
96 | CSS_SELECT_RULE_SRC_UNIVERSAL | |||
97 | } source; | |||
98 | uint32_t class; | |||
99 | } css_select_rule_source; | |||
100 | ||||
101 | ||||
102 | static css_error set_hint(css_select_state *state, css_hint *hint); | |||
103 | static css_error set_initial(css_select_state *state, | |||
104 | uint32_t prop, css_pseudo_element pseudo, | |||
105 | void *parent); | |||
106 | ||||
107 | static css_error select_from_sheet(css_select_ctx *ctx, | |||
108 | const css_stylesheet *sheet, css_origin origin, | |||
109 | css_select_state *state); | |||
110 | static css_error match_selectors_in_sheet(css_select_ctx *ctx, | |||
111 | const css_stylesheet *sheet, css_select_state *state); | |||
112 | static css_error match_selector_chain(css_select_ctx *ctx, | |||
113 | const css_selector *selector, css_select_state *state); | |||
114 | static css_error match_named_combinator(css_select_ctx *ctx, | |||
115 | css_combinator type, const css_selector *selector, | |||
116 | css_select_state *state, void *node, void **next_node); | |||
117 | static css_error match_universal_combinator(css_select_ctx *ctx, | |||
118 | css_combinator type, const css_selector *selector, | |||
119 | css_select_state *state, void *node, bool_Bool may_optimise, | |||
120 | bool_Bool *rejected_by_cache, void **next_node); | |||
121 | static css_error match_details(css_select_ctx *ctx, void *node, | |||
122 | const css_selector_detail *detail, css_select_state *state, | |||
123 | bool_Bool *match, css_pseudo_element *pseudo_element); | |||
124 | static css_error match_detail(css_select_ctx *ctx, void *node, | |||
125 | const css_selector_detail *detail, css_select_state *state, | |||
126 | bool_Bool *match, css_pseudo_element *pseudo_element); | |||
127 | static css_error cascade_style(const css_style *style, css_select_state *state); | |||
128 | ||||
129 | static css_error select_font_faces_from_sheet( | |||
130 | const css_stylesheet *sheet, | |||
131 | css_origin origin, | |||
132 | css_select_font_faces_state *state, | |||
133 | const css_select_strings *str); | |||
134 | ||||
135 | #ifdef DEBUG_CHAIN_MATCHING | |||
136 | static void dump_chain(const css_selector *selector); | |||
137 | #endif | |||
138 | ||||
139 | ||||
140 | static css_error css__create_node_data(struct css_node_data **node_data) | |||
141 | { | |||
142 | struct css_node_data *nd; | |||
143 | ||||
144 | nd = calloc(1, sizeof(struct css_node_data)); | |||
145 | if (nd == NULL((void*)0)) { | |||
146 | return CSS_NOMEM; | |||
147 | } | |||
148 | ||||
149 | *node_data = nd; | |||
150 | ||||
151 | return CSS_OK; | |||
152 | } | |||
153 | ||||
154 | static void css__destroy_node_data(struct css_node_data *node_data) | |||
155 | { | |||
156 | int i; | |||
157 | ||||
158 | assert(node_data != NULL)((node_data != ((void*)0)) ? (void) (0) : __assert_fail ("node_data != NULL" , "src/select/select.c", 158, __extension__ __PRETTY_FUNCTION__ )); | |||
159 | ||||
160 | if (node_data->bloom != NULL((void*)0)) { | |||
161 | free(node_data->bloom); | |||
162 | } | |||
163 | ||||
164 | for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++) { | |||
165 | if (node_data->partial.styles[i] != NULL((void*)0)) { | |||
166 | css_computed_style_destroy( | |||
167 | node_data->partial.styles[i]); | |||
168 | } | |||
169 | } | |||
170 | ||||
171 | free(node_data); | |||
172 | } | |||
173 | ||||
174 | ||||
175 | /* Exported function documented in public select.h header. */ | |||
176 | css_error css_libcss_node_data_handler(css_select_handler *handler, | |||
177 | css_node_data_action action, void *pw, void *node, | |||
178 | void *clone_node, void *libcss_node_data) | |||
179 | { | |||
180 | struct css_node_data *node_data = libcss_node_data; | |||
181 | css_error error; | |||
182 | ||||
183 | UNUSED(clone_node)((void)(clone_node)); | |||
184 | ||||
185 | if (handler == NULL((void*)0) || libcss_node_data == NULL((void*)0) || | |||
186 | handler->handler_version != CSS_SELECT_HANDLER_VERSION_1) { | |||
187 | return CSS_BADPARM; | |||
188 | } | |||
189 | ||||
190 | switch (action) { | |||
191 | case CSS_NODE_DELETED: | |||
192 | css__destroy_node_data(node_data); | |||
193 | break; | |||
194 | ||||
195 | case CSS_NODE_MODIFIED: | |||
196 | case CSS_NODE_ANCESTORS_MODIFIED: | |||
197 | if (node == NULL((void*)0)) { | |||
198 | return CSS_BADPARM; | |||
199 | } | |||
200 | ||||
201 | css__destroy_node_data(node_data); | |||
202 | ||||
203 | /* Don't bother rebuilding node_data, it can be done | |||
204 | * when the node is selected for. Just ensure the | |||
205 | * client drops its reference to the libcss_node_data. */ | |||
206 | error = handler->set_libcss_node_data(pw, node, NULL((void*)0)); | |||
207 | if (error != CSS_OK) { | |||
208 | return error; | |||
209 | } | |||
210 | break; | |||
211 | ||||
212 | case CSS_NODE_CLONED: | |||
213 | /* TODO: is it worth cloning libcss data? We only store | |||
214 | * data on the nodes as an optimisation, which is | |||
215 | * unlikely to be valid for most cloning cases. | |||
216 | */ | |||
217 | break; | |||
218 | ||||
219 | default: | |||
220 | return CSS_BADPARM; | |||
221 | } | |||
222 | ||||
223 | return CSS_OK; | |||
224 | } | |||
225 | ||||
226 | /** | |||
227 | * Create a selection context | |||
228 | * | |||
229 | * \param result Pointer to location to receive created context | |||
230 | * \return CSS_OK on success, appropriate error otherwise. | |||
231 | */ | |||
232 | css_error css_select_ctx_create(css_select_ctx **result) | |||
233 | { | |||
234 | css_select_ctx *c; | |||
235 | css_error error; | |||
236 | ||||
237 | if (result == NULL((void*)0)) | |||
238 | return CSS_BADPARM; | |||
239 | ||||
240 | c = calloc(1, sizeof(css_select_ctx)); | |||
241 | if (c == NULL((void*)0)) | |||
242 | return CSS_NOMEM; | |||
243 | ||||
244 | error = css_calculator_create(&c->calc); | |||
245 | if (error != CSS_OK) { | |||
246 | free(c); | |||
247 | return error; | |||
248 | } | |||
249 | ||||
250 | error = css_select_strings_intern(&c->str); | |||
251 | if (error != CSS_OK) { | |||
252 | css_calculator_unref(c->calc); | |||
253 | free(c); | |||
254 | return error; | |||
255 | } | |||
256 | ||||
257 | *result = c; | |||
258 | ||||
259 | return CSS_OK; | |||
260 | } | |||
261 | ||||
262 | /** | |||
263 | * Destroy a selection context | |||
264 | * | |||
265 | * \param ctx The context to destroy | |||
266 | * \return CSS_OK on success, appropriate error otherwise | |||
267 | */ | |||
268 | css_error css_select_ctx_destroy(css_select_ctx *ctx) | |||
269 | { | |||
270 | if (ctx == NULL((void*)0)) | |||
271 | return CSS_BADPARM; | |||
272 | ||||
273 | css_select_strings_unref(&ctx->str); | |||
274 | ||||
275 | if (ctx->default_style != NULL((void*)0)) | |||
276 | css_computed_style_destroy(ctx->default_style); | |||
277 | ||||
278 | if (ctx->sheets != NULL((void*)0)) { | |||
279 | for (uint32_t index = 0; index < ctx->n_sheets; index++) { | |||
280 | css__mq_query_destroy(ctx->sheets[index].media); | |||
281 | } | |||
282 | free(ctx->sheets); | |||
283 | } | |||
284 | ||||
285 | css_calculator_unref(ctx->calc); | |||
286 | ||||
287 | free(ctx); | |||
288 | ||||
289 | return CSS_OK; | |||
290 | } | |||
291 | ||||
292 | /** | |||
293 | * Append a stylesheet to a selection context | |||
294 | * | |||
295 | * \param ctx The context to append to | |||
296 | * \param sheet The sheet to append | |||
297 | * \param origin Origin of the sheet | |||
298 | * \param media Media string for the stylesheet | |||
299 | * \return CSS_OK on success, appropriate error otherwise | |||
300 | */ | |||
301 | css_error css_select_ctx_append_sheet(css_select_ctx *ctx, | |||
302 | const css_stylesheet *sheet, css_origin origin, | |||
303 | const char *media) | |||
304 | { | |||
305 | if (ctx == NULL((void*)0) || sheet == NULL((void*)0)) | |||
306 | return CSS_BADPARM; | |||
307 | ||||
308 | return css_select_ctx_insert_sheet(ctx, sheet, ctx->n_sheets, | |||
309 | origin, media); | |||
310 | } | |||
311 | ||||
312 | /** | |||
313 | * Insert a stylesheet into a selection context | |||
314 | * | |||
315 | * \param ctx The context to insert into | |||
316 | * \param sheet Sheet to insert | |||
317 | * \param index Index in context to insert sheet | |||
318 | * \param origin Origin of the sheet | |||
319 | * \param media Media string for the stylesheet | |||
320 | * \return CSS_OK on success, appropriate error otherwise | |||
321 | */ | |||
322 | css_error css_select_ctx_insert_sheet(css_select_ctx *ctx, | |||
323 | const css_stylesheet *sheet, uint32_t index, | |||
324 | css_origin origin, const char *media) | |||
325 | { | |||
326 | css_select_sheet *temp; | |||
327 | css_mq_query *mq; | |||
328 | css_error error; | |||
329 | ||||
330 | if (ctx == NULL((void*)0) || sheet == NULL((void*)0)) | |||
331 | return CSS_BADPARM; | |||
332 | ||||
333 | /* Inline styles cannot be inserted into a selection context */ | |||
334 | if (sheet->inline_style) | |||
335 | return CSS_INVALID; | |||
336 | ||||
337 | /* Index must be in the range [0, n_sheets] | |||
338 | * The latter being equivalent to append */ | |||
339 | if (index > ctx->n_sheets) | |||
340 | return CSS_INVALID; | |||
341 | ||||
342 | temp = realloc(ctx->sheets, | |||
343 | (ctx->n_sheets + 1) * sizeof(css_select_sheet)); | |||
344 | if (temp == NULL((void*)0)) | |||
345 | return CSS_NOMEM; | |||
346 | ||||
347 | ctx->sheets = temp; | |||
348 | ||||
349 | if (index < ctx->n_sheets) { | |||
350 | memmove(&ctx->sheets[index + 1], &ctx->sheets[index], | |||
351 | (ctx->n_sheets - index) * sizeof(css_select_sheet)); | |||
352 | } | |||
353 | ||||
354 | error = css_parse_media_query(sheet->propstrings, | |||
355 | (const uint8_t *)media, | |||
356 | (media == NULL((void*)0)) ? 0 : strlen(media), &mq); | |||
357 | if (error == CSS_NOMEM) { | |||
358 | return error; | |||
359 | } else if (error != CSS_OK) { | |||
360 | /* Fall back to default media: "all". */ | |||
361 | mq = calloc(1, sizeof(*mq)); | |||
362 | if (mq == NULL((void*)0)) { | |||
363 | return CSS_NOMEM; | |||
364 | } | |||
365 | mq->type = CSS_MEDIA_ALL; | |||
366 | } | |||
367 | ||||
368 | ctx->sheets[index].sheet = sheet; | |||
369 | ctx->sheets[index].origin = origin; | |||
370 | ctx->sheets[index].media = mq; | |||
371 | ||||
372 | ctx->uses_revert |= sheet->uses_revert; | |||
373 | ||||
374 | ctx->n_sheets++; | |||
375 | ||||
376 | return CSS_OK; | |||
377 | } | |||
378 | ||||
379 | /** | |||
380 | * Remove a sheet from a selection context | |||
381 | * | |||
382 | * \param ctx The context to remove from | |||
383 | * \param sheet Sheet to remove | |||
384 | * \return CSS_OK on success, appropriate error otherwise | |||
385 | */ | |||
386 | css_error css_select_ctx_remove_sheet(css_select_ctx *ctx, | |||
387 | const css_stylesheet *sheet) | |||
388 | { | |||
389 | uint32_t index; | |||
390 | ||||
391 | if (ctx == NULL((void*)0) || sheet == NULL((void*)0)) | |||
392 | return CSS_BADPARM; | |||
393 | ||||
394 | for (index = 0; index < ctx->n_sheets; index++) { | |||
395 | if (ctx->sheets[index].sheet == sheet) | |||
396 | break; | |||
397 | } | |||
398 | ||||
399 | if (index == ctx->n_sheets) | |||
400 | return CSS_INVALID; | |||
401 | ||||
402 | css__mq_query_destroy(ctx->sheets[index].media); | |||
403 | ||||
404 | ctx->n_sheets--; | |||
405 | ||||
406 | memmove(&ctx->sheets[index], &ctx->sheets[index + 1], | |||
407 | (ctx->n_sheets - index) * sizeof(css_select_sheet)); | |||
408 | ||||
409 | return CSS_OK; | |||
410 | ||||
411 | } | |||
412 | ||||
413 | /** | |||
414 | * Count the number of top-level sheets in a selection context | |||
415 | * | |||
416 | * \param ctx Context to consider | |||
417 | * \param count Pointer to location to receive count of sheets | |||
418 | * \return CSS_OK on success, appropriate error otherwise | |||
419 | */ | |||
420 | css_error css_select_ctx_count_sheets(css_select_ctx *ctx, uint32_t *count) | |||
421 | { | |||
422 | if (ctx == NULL((void*)0) || count == NULL((void*)0)) | |||
423 | return CSS_BADPARM; | |||
424 | ||||
425 | *count = ctx->n_sheets; | |||
426 | ||||
427 | return CSS_OK; | |||
428 | } | |||
429 | ||||
430 | /** | |||
431 | * Retrieve a sheet from a selection context | |||
432 | * | |||
433 | * \param ctx Context to look in | |||
434 | * \param index Index in context to look | |||
435 | * \param sheet Pointer to location to receive sheet | |||
436 | * \return CSS_OK on success, appropriate error otherwise | |||
437 | */ | |||
438 | css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index, | |||
439 | const css_stylesheet **sheet) | |||
440 | { | |||
441 | if (ctx == NULL((void*)0) || sheet == NULL((void*)0)) | |||
442 | return CSS_BADPARM; | |||
443 | ||||
444 | if (index > ctx->n_sheets) | |||
445 | return CSS_INVALID; | |||
446 | ||||
447 | *sheet = ctx->sheets[index].sheet; | |||
448 | ||||
449 | return CSS_OK; | |||
450 | } | |||
451 | ||||
452 | ||||
453 | /** | |||
454 | * Create a default style on the selection context | |||
455 | * | |||
456 | * \param ctx Context to create default style in | |||
457 | * \param handler Dispatch table of handler functions | |||
458 | * \param pw Client-specific private data for handler functions | |||
459 | * \return CSS_OK on success, appropriate error otherwise | |||
460 | */ | |||
461 | static css_error css__select_ctx_create_default_style(css_select_ctx *ctx, | |||
462 | css_select_handler *handler, void *pw) | |||
463 | { | |||
464 | css_computed_style *style; | |||
465 | css_error error; | |||
466 | ||||
467 | /* Need to construct the default style */ | |||
468 | error = css__computed_style_create(&style, ctx->calc); | |||
469 | if (error != CSS_OK) | |||
470 | return error; | |||
471 | ||||
472 | error = css__computed_style_initialise(style, handler, pw); | |||
473 | if (error != CSS_OK) { | |||
474 | css_computed_style_destroy(style); | |||
475 | return error; | |||
476 | } | |||
477 | ||||
478 | /* Neither create nor initialise intern the style, so intern it now */ | |||
479 | error = css__arena_intern_style(&style); | |||
480 | if (error != CSS_OK) | |||
481 | return error; | |||
482 | ||||
483 | /* Store it on the ctx */ | |||
484 | ctx->default_style = style; | |||
485 | ||||
486 | return CSS_OK; | |||
487 | } | |||
488 | ||||
489 | ||||
490 | /** | |||
491 | * Get a default style, e.g. for an implied element's anonamous box | |||
492 | * | |||
493 | * \param ctx Selection context (used to avoid recreating default) | |||
494 | * \param handler Dispatch table of handler functions | |||
495 | * \param pw Client-specific private data for handler functions | |||
496 | * \param style Pointer to location to receive default style | |||
497 | * \return CSS_OK on success, appropriate error otherwise. | |||
498 | */ | |||
499 | css_error css_select_default_style(css_select_ctx *ctx, | |||
500 | css_select_handler *handler, void *pw, | |||
501 | css_computed_style **style) | |||
502 | { | |||
503 | css_error error; | |||
504 | ||||
505 | if (ctx == NULL((void*)0) || style == NULL((void*)0) || handler == NULL((void*)0) || | |||
506 | handler->handler_version != | |||
507 | CSS_SELECT_HANDLER_VERSION_1) | |||
508 | return CSS_BADPARM; | |||
509 | ||||
510 | /* Ensure the ctx has a default style */ | |||
511 | if (ctx->default_style == NULL((void*)0)) { | |||
512 | error = css__select_ctx_create_default_style(ctx, handler, pw); | |||
513 | if (error != CSS_OK) { | |||
514 | return error; | |||
515 | } | |||
516 | } | |||
517 | ||||
518 | /* Pass a ref back to the client */ | |||
519 | *style = css__computed_style_ref(ctx->default_style); | |||
520 | return CSS_OK; | |||
521 | } | |||
522 | ||||
523 | ||||
524 | /** | |||
525 | * Get a bloom filter for the parent node | |||
526 | * | |||
527 | * \param parent Parent node to get bloom filter for | |||
528 | * \param handler Dispatch table of handler functions | |||
529 | * \param pw Client-specific private data for handler functions | |||
530 | * \param parent_bloom Updated to parent bloom to use. | |||
531 | * Note: if there's no parent, the caller must free | |||
532 | * the returned parent bloom, since it has no node to | |||
533 | * own it. | |||
534 | * \return CSS_OK on success, appropriate error otherwise. | |||
535 | */ | |||
536 | static css_error css__get_parent_bloom(void *parent, | |||
537 | css_select_handler *handler, void *pw, | |||
538 | css_bloom **parent_bloom) | |||
539 | { | |||
540 | struct css_node_data *node_data = NULL((void*)0); | |||
541 | css_bloom *bloom = NULL((void*)0); | |||
542 | css_error error; | |||
543 | ||||
544 | /* Get parent node's bloom filter */ | |||
545 | if (parent != NULL((void*)0)) { | |||
546 | /* Get parent bloom filter */ | |||
547 | struct css_node_data *node_data; | |||
548 | ||||
549 | /* Hideous casting to avoid warnings on all platforms | |||
550 | * we build for. */ | |||
551 | error = handler->get_libcss_node_data(pw, parent, | |||
552 | (void **) (void *) &node_data); | |||
553 | if (error != CSS_OK) { | |||
554 | return error; | |||
555 | } | |||
556 | if (node_data != NULL((void*)0)) { | |||
557 | bloom = node_data->bloom; | |||
558 | } | |||
559 | } | |||
560 | ||||
561 | if (bloom == NULL((void*)0)) { | |||
562 | uint32_t i; | |||
563 | /* Need to create parent bloom */ | |||
564 | ||||
565 | if (parent != NULL((void*)0)) { | |||
566 | /* TODO: | |||
567 | * Build & set the parent node's bloom properly. | |||
568 | * This will speed up the case where DOM change | |||
569 | * has caused bloom to get deleted. For now we | |||
570 | * fall back to a fully satruated bloom filter, | |||
571 | * which is slower but perfectly valid. | |||
572 | */ | |||
573 | bloom = malloc(sizeof(css_bloom) * CSS_BLOOM_SIZE4); | |||
574 | if (bloom == NULL((void*)0)) { | |||
575 | return CSS_NOMEM; | |||
576 | } | |||
577 | ||||
578 | for (i = 0; i < CSS_BLOOM_SIZE4; i++) { | |||
579 | bloom[i] = ~0; | |||
580 | } | |||
581 | ||||
582 | if (node_data == NULL((void*)0)) { | |||
583 | error = css__create_node_data(&node_data); | |||
584 | if (error != CSS_OK) { | |||
585 | free(bloom); | |||
586 | return error; | |||
587 | } | |||
588 | node_data->bloom = bloom; | |||
589 | ||||
590 | /* Set parent node bloom filter */ | |||
591 | error = handler->set_libcss_node_data(pw, | |||
592 | parent, node_data); | |||
593 | if (error != CSS_OK) { | |||
594 | css__destroy_node_data(node_data); | |||
595 | return error; | |||
596 | } | |||
597 | } | |||
598 | } else { | |||
599 | /* No ancestors; empty bloom filter */ | |||
600 | /* The parent bloom is owned by the parent node's | |||
601 | * node data. However, for the root node, there is | |||
602 | * no parent node to own the bloom filter. | |||
603 | * As such, we just use a pointer to static storage | |||
604 | * so calling code doesn't need to worry about | |||
605 | * whether the returned parent bloom is owned | |||
606 | * by something or not. | |||
607 | * Note, parent bloom is only read from, and not | |||
608 | * written to. */ | |||
609 | static css_bloom empty_bloom[CSS_BLOOM_SIZE4]; | |||
610 | bloom = empty_bloom; | |||
611 | } | |||
612 | } | |||
613 | ||||
614 | *parent_bloom = bloom; | |||
615 | return CSS_OK; | |||
616 | } | |||
617 | ||||
618 | static css_error css__create_node_bloom( | |||
619 | css_bloom **node_bloom, css_select_state *state) | |||
620 | { | |||
621 | css_error error; | |||
622 | css_bloom *bloom; | |||
623 | lwc_hash hash; | |||
624 | ||||
625 | *node_bloom = NULL((void*)0); | |||
626 | ||||
627 | /* Create the node's bloom */ | |||
628 | bloom = calloc(CSS_BLOOM_SIZE4, sizeof(css_bloom)); | |||
629 | if (bloom == NULL((void*)0)) { | |||
630 | return CSS_NOMEM; | |||
631 | } | |||
632 | ||||
633 | /* Add node name to bloom */ | |||
634 | if (lwc_string_caseless_hash_value(state->element.name, | |||
635 | &hash) != lwc_error_ok) { | |||
636 | error = CSS_NOMEM; | |||
637 | goto cleanup; | |||
638 | } | |||
639 | css_bloom_add_hash(bloom, hash); | |||
640 | ||||
641 | /* Add id name to bloom */ | |||
642 | if (state->id != NULL((void*)0)) { | |||
643 | if (lwc_string_caseless_hash_value(state->id, | |||
644 | &hash) != lwc_error_ok) { | |||
645 | error = CSS_NOMEM; | |||
646 | goto cleanup; | |||
647 | } | |||
648 | css_bloom_add_hash(bloom, hash); | |||
649 | } | |||
650 | ||||
651 | /* Add class names to bloom */ | |||
652 | if (state->classes != NULL((void*)0)) { | |||
653 | for (uint32_t i = 0; i < state->n_classes; i++) { | |||
654 | lwc_string *s = state->classes[i]; | |||
655 | if (lwc_string_caseless_hash_value(s, | |||
656 | &hash) != lwc_error_ok) { | |||
657 | error = CSS_NOMEM; | |||
658 | goto cleanup; | |||
659 | } | |||
660 | css_bloom_add_hash(bloom, hash); | |||
661 | } | |||
662 | } | |||
663 | ||||
664 | /* Merge parent bloom into node bloom */ | |||
665 | css_bloom_merge(state->node_data->bloom, bloom); | |||
666 | *node_bloom = bloom; | |||
667 | ||||
668 | return CSS_OK; | |||
669 | ||||
670 | cleanup: | |||
671 | free(bloom); | |||
672 | ||||
673 | return error; | |||
674 | } | |||
675 | ||||
676 | /** | |||
677 | * Set a node's data | |||
678 | * | |||
679 | * \param node Node to set node data for | |||
680 | * \param state Selection state for node | |||
681 | * \param handler Dispatch table of handler functions | |||
682 | * \param pw Client-specific private data for handler functions | |||
683 | * \return CSS_OK on success, appropriate error otherwise. | |||
684 | */ | |||
685 | static css_error css__set_node_data(void *node, css_select_state *state, | |||
686 | css_select_handler *handler, void *pw) | |||
687 | { | |||
688 | int i; | |||
689 | css_error error; | |||
690 | css_bloom *bloom; | |||
691 | css_select_results *results; | |||
692 | ||||
693 | struct css_node_data *node_data = state->node_data; | |||
694 | ||||
695 | /* Set node bloom filter */ | |||
696 | error = css__create_node_bloom(&bloom, state); | |||
697 | if (error != CSS_OK) { | |||
698 | return error; | |||
699 | } | |||
700 | node_data->bloom = bloom; | |||
701 | ||||
702 | /* Set selection results */ | |||
703 | results = state->results; | |||
704 | for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++) { | |||
705 | node_data->partial.styles[i] = | |||
706 | css__computed_style_ref(results->styles[i]); | |||
707 | } | |||
708 | ||||
709 | error = handler->set_libcss_node_data(pw, node, node_data); | |||
710 | if (error != CSS_OK) { | |||
711 | css__destroy_node_data(node_data); | |||
712 | state->node_data = NULL((void*)0); | |||
713 | return error; | |||
714 | } | |||
715 | ||||
716 | state->node_data = NULL((void*)0); | |||
717 | ||||
718 | return CSS_OK; | |||
719 | } | |||
720 | ||||
721 | ||||
722 | /** The releationship of a share candidate node to the selection node. */ | |||
723 | enum share_candidate_type { | |||
724 | CANDIDATE_SIBLING, | |||
725 | CANDIDATE_COUSIN, | |||
726 | }; | |||
727 | ||||
728 | ||||
729 | /** | |||
730 | * Get node_data for candidate node if we can reuse its style. | |||
731 | * | |||
732 | * \param[in] state The selection state for current node. | |||
733 | * \param[in] share_candidate_node The node to test id and classes of. | |||
734 | * \param[in] type The candidate's relation to selection node. | |||
735 | * \param[out] sharable_node_data Returns node_data or NULL. | |||
736 | * \return CSS_OK on success, appropriate error otherwise. | |||
737 | */ | |||
738 | static css_error css_select_style__get_sharable_node_data_for_candidate( | |||
739 | css_select_state *state, | |||
740 | void *share_candidate_node, | |||
741 | enum share_candidate_type type, | |||
742 | struct css_node_data **sharable_node_data) | |||
743 | { | |||
744 | css_error error; | |||
745 | lwc_string *share_candidate_id; | |||
746 | uint32_t share_candidate_n_classes; | |||
747 | lwc_string **share_candidate_classes; | |||
748 | struct css_node_data *node_data; | |||
749 | ||||
750 | UNUSED(type)((void)(type)); | |||
751 | ||||
752 | *sharable_node_data = NULL((void*)0); | |||
753 | ||||
754 | /* We get the candidate node data first, as if it has none, we can't | |||
755 | * share its data anyway. | |||
756 | * Hideous casting to avoid warnings on all platforms we build for. */ | |||
757 | error = state->handler->get_libcss_node_data(state->pw, | |||
758 | share_candidate_node, (void **) (void *) &node_data); | |||
759 | if (error != CSS_OK || node_data == NULL((void*)0)) { | |||
760 | #ifdef DEBUG_STYLE_SHARING | |||
761 | printf(" \t%s\tno share: no candidate node data\n", | |||
762 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 762 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
763 | #endif | |||
764 | return error; | |||
765 | } | |||
766 | ||||
767 | /* If one node has hints and other doesn't then can't share */ | |||
768 | if ((node_data->flags & CSS_NODE_FLAGS_HAS_HINTS) != | |||
769 | (state->node_data->flags & CSS_NODE_FLAGS_HAS_HINTS)) { | |||
770 | #ifdef DEBUG_STYLE_SHARING | |||
771 | printf(" \t%s\tno share: have hints mismatch\n", | |||
772 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 772 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
773 | #endif | |||
774 | return CSS_OK; | |||
775 | } | |||
776 | ||||
777 | /* If the node and candidate node had different pseudo classes, we | |||
778 | * can't share. */ | |||
779 | if ((node_data->flags & CSS_NODE_FLAGS__PSEUDO_CLASSES_MASK) != | |||
780 | (state->node_data->flags & | |||
781 | CSS_NODE_FLAGS__PSEUDO_CLASSES_MASK)) { | |||
782 | #ifdef DEBUG_STYLE_SHARING | |||
783 | printf(" \t%s\tno share: different pseudo classes\n", | |||
784 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 784 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
785 | #endif | |||
786 | return CSS_OK; | |||
787 | ||||
788 | } | |||
789 | ||||
790 | /* If the node was affected by attribute or pseudo class rules, | |||
791 | * or had an inline style, it's not a candidate for sharing */ | |||
792 | if (node_data->flags & ( | |||
793 | CSS_NODE_FLAGS_TAINT_PSEUDO_CLASS | | |||
794 | CSS_NODE_FLAGS_TAINT_ATTRIBUTE | | |||
795 | CSS_NODE_FLAGS_TAINT_SIBLING | | |||
796 | CSS_NODE_FLAGS_HAS_INLINE_STYLE)) { | |||
797 | #ifdef DEBUG_STYLE_SHARING | |||
798 | printf(" \t%s\tno share: candidate flags: %s%s%s%s\n", | |||
799 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 799 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);}), | |||
800 | (node_data->flags & | |||
801 | CSS_NODE_FLAGS_TAINT_PSEUDO_CLASS) ? | |||
802 | "PSEUDOCLASS" : "", | |||
803 | (node_data->flags & | |||
804 | CSS_NODE_FLAGS_TAINT_ATTRIBUTE) ? | |||
805 | " ATTRIBUTE" : "", | |||
806 | (node_data->flags & | |||
807 | CSS_NODE_FLAGS_TAINT_SIBLING) ? | |||
808 | " SIBLING" : "", | |||
809 | (node_data->flags & | |||
810 | CSS_NODE_FLAGS_HAS_INLINE_STYLE) ? | |||
811 | " INLINE_STYLE" : ""); | |||
812 | #endif | |||
813 | return CSS_OK; | |||
814 | } | |||
815 | ||||
816 | /* Check candidate ID doesn't prevent sharing */ | |||
817 | error = state->handler->node_id(state->pw, | |||
818 | share_candidate_node, | |||
819 | &share_candidate_id); | |||
820 | if (error != CSS_OK) { | |||
821 | return error; | |||
822 | ||||
823 | } else if (share_candidate_id != NULL((void*)0)) { | |||
824 | lwc_string_unref(share_candidate_id){ lwc_string *__lwc_s = (share_candidate_id); ((__lwc_s != (( void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/select/select.c" , 824, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
825 | #ifdef DEBUG_STYLE_SHARING | |||
826 | printf(" \t%s\tno share: candidate id\n", | |||
827 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 827 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
828 | #endif | |||
829 | return CSS_OK; | |||
830 | } | |||
831 | ||||
832 | /* Check candidate classes don't prevent sharing */ | |||
833 | error = state->handler->node_classes(state->pw, | |||
834 | share_candidate_node, | |||
835 | &share_candidate_classes, | |||
836 | &share_candidate_n_classes); | |||
837 | if (error != CSS_OK) { | |||
838 | return error; | |||
839 | } | |||
840 | ||||
841 | if (state->n_classes != share_candidate_n_classes) { | |||
842 | #ifdef DEBUG_STYLE_SHARING | |||
843 | printf(" \t%s\tno share: class count mismatch\n", | |||
844 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 844 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
845 | #endif | |||
846 | goto cleanup; | |||
847 | } | |||
848 | ||||
849 | /* TODO: no need to care about the order, but it's simpler to | |||
850 | * have an ordered match, and authors are more likely to be | |||
851 | * consistent than not. */ | |||
852 | for (uint32_t i = 0; i < share_candidate_n_classes; i++) { | |||
853 | bool_Bool match; | |||
854 | if (lwc_string_caseless_isequal(({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1 = (state->classes[i]); lwc_string *__lwc_str2 = (share_candidate_classes [i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1 ); } if (__lwc_err == lwc_error_ok && __lwc_str2-> insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string (__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = ( __lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err ; }) | |||
855 | state->classes[i],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1 = (state->classes[i]); lwc_string *__lwc_str2 = (share_candidate_classes [i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1 ); } if (__lwc_err == lwc_error_ok && __lwc_str2-> insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string (__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = ( __lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err ; }) | |||
856 | share_candidate_classes[i],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1 = (state->classes[i]); lwc_string *__lwc_str2 = (share_candidate_classes [i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1 ); } if (__lwc_err == lwc_error_ok && __lwc_str2-> insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string (__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = ( __lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err ; }) | |||
857 | &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1 = (state->classes[i]); lwc_string *__lwc_str2 = (share_candidate_classes [i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1 ); } if (__lwc_err == lwc_error_ok && __lwc_str2-> insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string (__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = ( __lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err ; }) == lwc_error_ok && | |||
858 | match == false0) { | |||
859 | #ifdef DEBUG_STYLE_SHARING | |||
860 | printf(" \t%s\tno share: class mismatch\n", | |||
861 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 861 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
862 | #endif | |||
863 | goto cleanup; | |||
864 | } | |||
865 | } | |||
866 | ||||
867 | if (node_data->flags & CSS_NODE_FLAGS_HAS_HINTS) { | |||
868 | /* TODO: check hints match. For now, just prevent sharing */ | |||
869 | #ifdef DEBUG_STYLE_SHARING | |||
870 | printf(" \t%s\tno share: hints\n", | |||
871 | lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 871 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);})); | |||
872 | #endif | |||
873 | goto cleanup; | |||
874 | } | |||
875 | ||||
876 | *sharable_node_data = node_data; | |||
877 | ||||
878 | cleanup: | |||
879 | if (share_candidate_classes != NULL((void*)0)) { | |||
880 | for (uint32_t i = 0; i < share_candidate_n_classes; i++) { | |||
881 | lwc_string_unref(share_candidate_classes[i]){ lwc_string *__lwc_s = (share_candidate_classes[i]); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL" , "src/select/select.c", 881, __extension__ __PRETTY_FUNCTION__ )); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); }; | |||
882 | } | |||
883 | } | |||
884 | ||||
885 | return CSS_OK; | |||
886 | } | |||
887 | ||||
888 | ||||
889 | /** | |||
890 | * Get previous named cousin node. | |||
891 | * | |||
892 | * \param[in] state The selection state for current node. | |||
893 | * \param[in] node The node to get the cousin of. | |||
894 | * \param[out] cousin_out Returns a cousin node or NULL. | |||
895 | * \return CSS_OK on success or appropriate error otherwise. | |||
896 | */ | |||
897 | static css_error css_select_style__get_named_cousin( | |||
898 | css_select_state *state, void *node, | |||
899 | void **cousin_out) | |||
900 | { | |||
901 | /* TODO: | |||
902 | * | |||
903 | * Consider cousin nodes; Go to parent's previous sibling's last child. | |||
904 | * The parent and the parent's sibling must be "similar". | |||
905 | */ | |||
906 | UNUSED(state)((void)(state)); | |||
907 | UNUSED(node)((void)(node)); | |||
908 | ||||
909 | *cousin_out = NULL((void*)0); | |||
910 | ||||
911 | return CSS_OK; | |||
912 | } | |||
913 | ||||
914 | ||||
915 | /** | |||
916 | * Get node_data for any node that we can reuse the style for. | |||
917 | * | |||
918 | * This is an optimisation to needing to perform selection for a node, | |||
919 | * by sharing the style for a previous node. | |||
920 | * | |||
921 | * \param[in] node Node we're selecting for. | |||
922 | * \param[in] state The current selection state. | |||
923 | * \param[out] sharable_node_data Returns node_data or NULL. | |||
924 | * \return CSS_OK on success or appropriate error otherwise. | |||
925 | */ | |||
926 | static css_error css_select_style__get_sharable_node_data( | |||
927 | void *node, css_select_state *state, | |||
928 | struct css_node_data **sharable_node_data) | |||
929 | { | |||
930 | css_error error; | |||
931 | enum share_candidate_type type = CANDIDATE_SIBLING; | |||
932 | ||||
933 | *sharable_node_data = NULL((void*)0); | |||
934 | ||||
935 | /* TODO: move this test to caller? */ | |||
936 | if (state->id != NULL((void*)0)) { | |||
937 | /* If the node has an ID can't share another node's style. */ | |||
938 | /* TODO: Consider whether this ID exists in the ID hash tables. | |||
939 | * (If not, the ID cannot affect the node's style.) | |||
940 | * | |||
941 | * Call css__selector_hash_find_by_id, for each sheet, | |||
942 | * and if we get a non-NULL "matched" then return. | |||
943 | * | |||
944 | * Check overhead is worth cost. */ | |||
945 | #ifdef DEBUG_STYLE_SHARING | |||
946 | printf(" \t%s\tno share: node id (%s)\n", lwc_string_data(state->element.name)({((state->element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state->element.name != NULL", "src/select/select.c", 946 , __extension__ __PRETTY_FUNCTION__)); (const char *)((state-> element.name)+1);}), lwc_string_data(state->id)({((state->id != ((void*)0)) ? (void) (0) : __assert_fail ( "state->id != NULL", "src/select/select.c", 946, __extension__ __PRETTY_FUNCTION__)); (const char *)((state->id)+1);})); | |||
947 | #endif | |||
948 | return CSS_OK; | |||
949 | } | |||
950 | if (state->node_data->flags & CSS_NODE_FLAGS_HAS_INLINE_STYLE) { | |||
951 | #ifdef DEBUG_STYLE_SHARING | |||
952 | printf(" \t%s\tno share: inline style\n"); | |||
953 | #endif | |||
954 | return CSS_OK; | |||
955 | } | |||
956 | ||||
957 | while (true1) { | |||
958 | void *share_candidate_node; | |||
959 | ||||
960 | /* Get previous sibling with same element name */ | |||
961 | error = state->handler->named_generic_sibling_node(state->pw, | |||
962 | node, &state->element, &share_candidate_node); | |||
963 | if (error != CSS_OK) { | |||
964 | return error; | |||
965 | } else { | |||
966 | if (share_candidate_node == NULL((void*)0)) { | |||
967 | error = css_select_style__get_named_cousin( | |||
968 | state, node, | |||
969 | &share_candidate_node); | |||
970 | if (error != CSS_OK) { | |||
971 | return error; | |||
972 | } else { | |||
973 | if (share_candidate_node == NULL((void*)0)) { | |||
974 | break; | |||
975 | } | |||
976 | } | |||
977 | type = CANDIDATE_COUSIN; | |||
978 | } | |||
979 | } | |||
980 | ||||
981 | /* Check whether we can share the candidate node's | |||
982 | * style. We already know the element names match, | |||
983 | * check that candidate node's ID and class won't | |||
984 | * prevent sharing. */ | |||
985 | error = css_select_style__get_sharable_node_data_for_candidate( | |||
986 | state, share_candidate_node, | |||
987 | type, sharable_node_data); | |||
988 | if (error != CSS_OK) { | |||
989 | return error; | |||
990 | } | |||
991 | ||||
992 | if (*sharable_node_data != NULL((void*)0)) { | |||
993 | /* Found style date we can share */ | |||
994 | break; | |||
995 | } | |||
996 | ||||
997 | /* Can't share with this; look for another */ | |||
998 | node = share_candidate_node; | |||
999 | } | |||
1000 | ||||
1001 | return CSS_OK; | |||
1002 | } | |||
1003 | ||||
1004 | ||||
1005 | /** | |||
1006 | * Finalise a selection state, releasing any resources it owns | |||
1007 | * | |||
1008 | * \param[in] state The selection state to finalise. | |||
1009 | */ | |||
1010 | static void css_select__finalise_selection_state( | |||
1011 | css_select_state *state) | |||
1012 | { | |||
1013 | if (state->results != NULL((void*)0)) { | |||
1014 | css_select_results_destroy(state->results); | |||
1015 | } | |||
1016 | ||||
1017 | if (state->node_data != NULL((void*)0)) { | |||
1018 | css__destroy_node_data(state->node_data); | |||
1019 | } | |||
1020 | ||||
1021 | if (state->classes != NULL((void*)0)) { | |||
1022 | for (uint32_t i = 0; i < state->n_classes; i++) { | |||
1023 | lwc_string_unref(state->classes[i]){ lwc_string *__lwc_s = (state->classes[i]); ((__lwc_s != ( (void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/select/select.c" , 1023, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
1024 | } | |||
1025 | } | |||
1026 | ||||
1027 | if (state->id != NULL((void*)0)) { | |||
1028 | lwc_string_unref(state->id){ lwc_string *__lwc_s = (state->id); ((__lwc_s != ((void*) 0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/select/select.c" , 1028, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
1029 | } | |||
1030 | ||||
1031 | if (state->element.ns != NULL((void*)0)) { | |||
1032 | lwc_string_unref(state->element.ns){ lwc_string *__lwc_s = (state->element.ns); ((__lwc_s != ( (void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/select/select.c" , 1032, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
1033 | } | |||
1034 | ||||
1035 | if (state->element.name != NULL((void*)0)){ | |||
1036 | lwc_string_unref(state->element.name){ lwc_string *__lwc_s = (state->element.name); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/select/select.c", 1036, __extension__ __PRETTY_FUNCTION__ )); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); }; | |||
1037 | } | |||
1038 | ||||
1039 | if (state->revert != NULL((void*)0)) { | |||
1040 | for (size_t i = 0; i < CSS_ORIGIN_AUTHOR; i++) { | |||
1041 | for (size_t j = 0; j < CSS_PSEUDO_ELEMENT_COUNT; j++) { | |||
1042 | if (state->revert[i].style[j] == NULL((void*)0)) { | |||
1043 | continue; | |||
1044 | } | |||
1045 | css_computed_style_destroy( | |||
1046 | state->revert[i].style[j]); | |||
1047 | } | |||
1048 | } | |||
1049 | free(state->revert); | |||
1050 | } | |||
1051 | } | |||
1052 | ||||
1053 | ||||
1054 | /** | |||
1055 | * Initialise a selection state. | |||
1056 | * | |||
1057 | * \param[in] state The selection state to initialise | |||
1058 | * \param[in] node The node we are selecting for. | |||
1059 | * \param[in] parent The node's parent node, or NULL. | |||
1060 | * \param[in] media The media specification we're selecting for. | |||
1061 | * \param[in] unit_ctx Unit conversion context. | |||
1062 | * \param[in] handler The client selection callback table. | |||
1063 | * \param[in] pw The client private data, passsed out to callbacks. | |||
1064 | * \return CSS_OK or appropriate error otherwise. | |||
1065 | */ | |||
1066 | static css_error css_select__initialise_selection_state( | |||
1067 | css_select_state *state, | |||
1068 | void *node, | |||
1069 | void *parent, | |||
1070 | const css_media *media, | |||
1071 | const css_unit_ctx *unit_ctx, | |||
1072 | css_select_handler *handler, | |||
1073 | void *pw) | |||
1074 | { | |||
1075 | css_error error; | |||
1076 | bool_Bool match; | |||
1077 | ||||
1078 | /* Set up the selection state */ | |||
1079 | memset(state, 0, sizeof(*state)); | |||
1080 | state->node = node; | |||
1081 | state->media = media; | |||
1082 | state->unit_ctx = unit_ctx; | |||
1083 | state->handler = handler; | |||
1084 | state->pw = pw; | |||
1085 | state->next_reject = state->reject_cache + | |||
1086 | (N_ELEMENTS(state->reject_cache)(sizeof((state->reject_cache)) / sizeof((state->reject_cache )[0])) - 1); | |||
1087 | ||||
1088 | /* Allocate the result set */ | |||
1089 | state->results = calloc(1, sizeof(css_select_results)); | |||
1090 | if (state->results == NULL((void*)0)) { | |||
1091 | return CSS_NOMEM; | |||
1092 | } | |||
1093 | ||||
1094 | error = css__create_node_data(&state->node_data); | |||
1095 | if (error
| |||
1096 | goto failed; | |||
1097 | } | |||
1098 | ||||
1099 | error = css__get_parent_bloom(parent, handler, pw, | |||
1100 | &state->node_data->bloom); | |||
1101 | if (error
| |||
1102 | goto failed; | |||
1103 | } | |||
1104 | ||||
1105 | /* Get node's name */ | |||
1106 | error = handler->node_name(pw, node, &state->element); | |||
1107 | if (error != CSS_OK){ | |||
1108 | goto failed; | |||
1109 | } | |||
1110 | ||||
1111 | /* Get node's ID, if any */ | |||
1112 | error = handler->node_id(pw, node, &state->id); | |||
1113 | if (error != CSS_OK){ | |||
1114 | goto failed; | |||
1115 | } | |||
1116 | ||||
1117 | /* Get node's classes, if any */ | |||
1118 | error = handler->node_classes(pw, node, | |||
1119 | &state->classes, &state->n_classes); | |||
1120 | if (error != CSS_OK){ | |||
1121 | goto failed; | |||
1122 | } | |||
1123 | ||||
1124 | /* Node pseudo classes */ | |||
1125 | error = handler->node_is_link(pw, node, &match); | |||
1126 | if (error != CSS_OK){ | |||
1127 | goto failed; | |||
1128 | } else if (match) { | |||
1129 | state->node_data->flags |= CSS_NODE_FLAGS_PSEUDO_CLASS_LINK; | |||
1130 | } | |||
1131 | ||||
1132 | error = handler->node_is_visited(pw, node, &match); | |||
1133 | if (error != CSS_OK){ | |||
1134 | goto failed; | |||
1135 | } else if (match) { | |||
1136 | state->node_data->flags |= CSS_NODE_FLAGS_PSEUDO_CLASS_VISITED; | |||
1137 | } | |||
1138 | ||||
1139 | error = handler->node_is_hover(pw, node, &match); | |||
1140 | if (error != CSS_OK){ | |||
1141 | goto failed; | |||
1142 | } else if (match) { | |||
1143 | state->node_data->flags |= CSS_NODE_FLAGS_PSEUDO_CLASS_HOVER; | |||
1144 | } | |||
1145 | ||||
1146 | error = handler->node_is_active(pw, node, &match); | |||
1147 | if (error != CSS_OK){ | |||
1148 | goto failed; | |||
1149 | } else if (match) { | |||
1150 | state->node_data->flags |= CSS_NODE_FLAGS_PSEUDO_CLASS_ACTIVE; | |||
1151 | } | |||
1152 | ||||
1153 | error = handler->node_is_focus(pw, node, &match); | |||
1154 | if (error != CSS_OK){ | |||
1155 | goto failed; | |||
1156 | } else if (match) { | |||
1157 | state->node_data->flags |= CSS_NODE_FLAGS_PSEUDO_CLASS_FOCUS; | |||
1158 | } | |||
1159 | ||||
1160 | return CSS_OK; | |||
1161 | ||||
1162 | failed: | |||
1163 | css_select__finalise_selection_state(state); | |||
1164 | return error; | |||
1165 | } | |||
1166 | ||||
1167 | static css_error css__select_revert_property_to_origin( | |||
1168 | css_select_state *select_state, | |||
1169 | prop_state *prop_state, | |||
1170 | css_origin origin, | |||
1171 | enum css_pseudo_element pseudo, | |||
1172 | enum css_properties_e property) | |||
1173 | { | |||
1174 | css_error error; | |||
1175 | ||||
1176 | if (select_state->results->styles[pseudo] == NULL((void*)0)) { | |||
1177 | return CSS_OK; | |||
1178 | } | |||
1179 | ||||
1180 | if (select_state->revert[origin].style[pseudo] == NULL((void*)0)) { | |||
| ||||
1181 | return prop_dispatch[property].initial(select_state); | |||
1182 | } | |||
1183 | ||||
1184 | error = prop_dispatch[property].copy( | |||
1185 | select_state->revert[origin].style[pseudo], | |||
1186 | select_state->results->styles[pseudo]); | |||
1187 | if (error != CSS_OK) { | |||
1188 | return error; | |||
1189 | } | |||
1190 | ||||
1191 | *prop_state = select_state->revert[origin].props[property][pseudo]; | |||
1192 | return CSS_OK; | |||
1193 | } | |||
1194 | ||||
1195 | static css_error css__select_revert_property( | |||
1196 | css_select_state *select_state, | |||
1197 | prop_state *prop_state, | |||
1198 | enum css_pseudo_element pseudo, | |||
1199 | enum css_properties_e property) | |||
1200 | { | |||
1201 | css_error error; | |||
1202 | ||||
1203 | switch (prop_state->origin) { | |||
1204 | case CSS_ORIGIN_AUTHOR: | |||
1205 | error = css__select_revert_property_to_origin( | |||
1206 | select_state, prop_state, CSS_ORIGIN_USER, | |||
1207 | pseudo, property); | |||
1208 | if (error != CSS_OK) { | |||
1209 | return error; | |||
1210 | } | |||
1211 | if (prop_state->explicit_default != FLAG_VALUE_REVERT) { | |||
1212 | break; | |||
1213 | } | |||
1214 | /* Fall-through */ | |||
1215 | case CSS_ORIGIN_USER: | |||
1216 | error = css__select_revert_property_to_origin( | |||
1217 | select_state, prop_state, CSS_ORIGIN_UA, | |||
1218 | pseudo, property); | |||
1219 | if (error != CSS_OK) { | |||
1220 | return error; | |||
1221 | } | |||
1222 | if (prop_state->explicit_default != FLAG_VALUE_REVERT) { | |||
1223 | break; | |||
1224 | } | |||
1225 | /* Fall-through */ | |||
1226 | case CSS_ORIGIN_UA: | |||
1227 | prop_state->explicit_default = FLAG_VALUE_UNSET; | |||
1228 | break; | |||
1229 | } | |||
1230 | ||||
1231 | return CSS_OK; | |||
1232 | } | |||
1233 | ||||
1234 | /** | |||
1235 | * Select a style for the given node | |||
1236 | * | |||
1237 | * \param ctx Selection context to use | |||
1238 | * \param node Node to select style for | |||
1239 | * \param unit_ctx Context for length unit conversions. | |||
1240 | * \param media Currently active media specification | |||
1241 | * \param inline_style Corresponding inline style for node, or NULL | |||
1242 | * \param handler Dispatch table of handler functions | |||
1243 | * \param pw Client-specific private data for handler functions | |||
1244 | * \param result Pointer to location to receive result set | |||
1245 | * \return CSS_OK on success, appropriate error otherwise. | |||
1246 | * | |||
1247 | * In computing the style, no reference is made to the parent node's | |||
1248 | * style. Therefore, the resultant computed style is not ready for | |||
1249 | * immediate use, as some properties may be marked as inherited. | |||
1250 | * Use css_computed_style_compose() to obtain a fully computed style. | |||
1251 | * | |||
1252 | * This two-step approach to style computation is designed to allow | |||
1253 | * the client to store the partially computed style and efficiently | |||
1254 | * update the fully computed style for a node when layout changes. | |||
1255 | */ | |||
1256 | css_error css_select_style(css_select_ctx *ctx, void *node, | |||
1257 | const css_unit_ctx *unit_ctx, | |||
1258 | const css_media *media, const css_stylesheet *inline_style, | |||
1259 | css_select_handler *handler, void *pw, | |||
1260 | css_select_results **result) | |||
1261 | { | |||
1262 | css_origin origin = CSS_ORIGIN_UA; | |||
1263 | uint32_t i, j, nhints; | |||
1264 | css_error error; | |||
1265 | css_select_state state; | |||
1266 | css_hint *hints = NULL((void*)0); | |||
1267 | void *parent = NULL((void*)0); | |||
1268 | struct css_node_data *share; | |||
1269 | ||||
1270 | if (ctx == NULL((void*)0) || node == NULL((void*)0) || result == NULL((void*)0) || handler == NULL((void*)0) || | |||
| ||||
1271 | handler->handler_version != CSS_SELECT_HANDLER_VERSION_1) | |||
1272 | return CSS_BADPARM; | |||
1273 | ||||
1274 | error = handler->parent_node(pw, node, &parent); | |||
1275 | if (error != CSS_OK) | |||
1276 | return error; | |||
1277 | ||||
1278 | error = css_select__initialise_selection_state( | |||
1279 | &state, node, parent, media, unit_ctx, handler, pw); | |||
1280 | if (error
| |||
1281 | return error; | |||
1282 | ||||
1283 | /* Fetch presentational hints */ | |||
1284 | error = handler->node_presentational_hint(pw, node, &nhints, &hints); | |||
1285 | if (error != CSS_OK) | |||
1286 | goto cleanup; | |||
1287 | if (nhints > 0) { | |||
1288 | state.node_data->flags |= CSS_NODE_FLAGS_HAS_HINTS; | |||
1289 | } | |||
1290 | ||||
1291 | if (inline_style != NULL((void*)0)) { | |||
1292 | state.node_data->flags |= CSS_NODE_FLAGS_HAS_INLINE_STYLE; | |||
1293 | } | |||
1294 | ||||
1295 | /* Check if we can share another node's style */ | |||
1296 | error = css_select_style__get_sharable_node_data(node, &state, &share); | |||
1297 | if (error
| |||
1298 | goto cleanup; | |||
1299 | } else if (share
| |||
1300 | css_computed_style **styles = share->partial.styles; | |||
1301 | for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++) { | |||
1302 | state.results->styles[i] = | |||
1303 | css__computed_style_ref(styles[i]); | |||
1304 | } | |||
1305 | #ifdef DEBUG_STYLE_SHARING | |||
1306 | printf("style:\t%s\tSHARED!\n", | |||
1307 | lwc_string_data(state.element.name)({((state.element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state.element.name != NULL", "src/select/select.c", 1307, __extension__ __PRETTY_FUNCTION__)); (const char *)((state.element.name)+1 );})); | |||
1308 | #endif | |||
1309 | goto complete; | |||
1310 | } | |||
1311 | #ifdef DEBUG_STYLE_SHARING | |||
1312 | printf("style:\t%s\tSELECTED\n", lwc_string_data(state.element.name)({((state.element.name != ((void*)0)) ? (void) (0) : __assert_fail ("state.element.name != NULL", "src/select/select.c", 1312, __extension__ __PRETTY_FUNCTION__)); (const char *)((state.element.name)+1 );})); | |||
1313 | #endif | |||
1314 | ||||
1315 | /* Not sharing; need to select. */ | |||
1316 | if (ctx->uses_revert || | |||
1317 | (inline_style
| |||
1318 | /* Need to track UA and USER origin styles for revert. */ | |||
1319 | state.revert = calloc(CSS_ORIGIN_AUTHOR, sizeof(*state.revert)); | |||
1320 | if (state.revert == NULL((void*)0)) { | |||
1321 | error = CSS_NOMEM; | |||
1322 | goto cleanup; | |||
1323 | } | |||
1324 | } | |||
1325 | ||||
1326 | /* Base element style is guaranteed to exist | |||
1327 | */ | |||
1328 | error = css__computed_style_create( | |||
1329 | &state.results->styles[CSS_PSEUDO_ELEMENT_NONE], ctx->calc); | |||
1330 | if (error != CSS_OK) { | |||
1331 | goto cleanup; | |||
1332 | } | |||
1333 | ||||
1334 | /* Apply any hints */ | |||
1335 | if (nhints
| |||
1336 | /* Ensure that the appropriate computed style exists */ | |||
1337 | struct css_computed_style *computed_style = | |||
1338 | state.results->styles[CSS_PSEUDO_ELEMENT_NONE]; | |||
1339 | state.computed = computed_style; | |||
1340 | ||||
1341 | for (i = 0; i < nhints; i++) { | |||
1342 | error = set_hint(&state, &hints[i]); | |||
1343 | if (error != CSS_OK) | |||
1344 | goto cleanup; | |||
1345 | } | |||
1346 | } | |||
1347 | ||||
1348 | /* Iterate through the top-level stylesheets, selecting styles | |||
1349 | * from those which apply to our current media requirements and | |||
1350 | * are not disabled */ | |||
1351 | if (ctx->n_sheets > 0) { | |||
1352 | origin = ctx->sheets[0].origin; | |||
1353 | } | |||
1354 | for (i = 0; i
| |||
1355 | const css_select_sheet s = ctx->sheets[i]; | |||
1356 | ||||
1357 | if (state.revert != NULL((void*)0) && s.origin != origin) { | |||
1358 | for (j = 0; j < CSS_PSEUDO_ELEMENT_COUNT; j++) { | |||
1359 | if (state.results->styles[j] == NULL((void*)0)) { | |||
1360 | continue; | |||
1361 | } | |||
1362 | error = css__computed_style_clone( | |||
1363 | state.results->styles[j], | |||
1364 | &state.revert[origin].style[j]); | |||
1365 | if (error != CSS_OK) { | |||
1366 | goto cleanup; | |||
1367 | } | |||
1368 | memcpy(state.revert[origin].props, | |||
1369 | state.props, sizeof(state.props)); | |||
1370 | } | |||
1371 | origin = s.origin; | |||
1372 | } | |||
1373 | ||||
1374 | if (mq__list_match(s.media, unit_ctx, media, &ctx->str) && | |||
1375 | s.sheet->disabled == false0) { | |||
1376 | error = select_from_sheet(ctx, s.sheet, | |||
1377 | s.origin, &state); | |||
1378 | if (error != CSS_OK) | |||
1379 | goto cleanup; | |||
1380 | } | |||
1381 | } | |||
1382 | ||||
1383 | /* Consider any inline style for the node */ | |||
1384 | if (inline_style
| |||
1385 | css_rule_selector *sel = | |||
1386 | (css_rule_selector *) inline_style->rule_list; | |||
1387 | ||||
1388 | /* Sanity check style */ | |||
1389 | if (inline_style->rule_count != 1 || | |||
1390 | inline_style->rule_list->type != CSS_RULE_SELECTOR || | |||
1391 | inline_style->rule_list->items != 0) { | |||
1392 | error = CSS_INVALID; | |||
1393 | goto cleanup; | |||
1394 | } | |||
1395 | ||||
1396 | /* No bytecode if input was empty or wholly invalid */ | |||
1397 | if (sel->style != NULL((void*)0)) { | |||
1398 | /* Inline style applies to base element only */ | |||
1399 | state.current_pseudo = CSS_PSEUDO_ELEMENT_NONE; | |||
1400 | state.computed = state.results->styles[ | |||
1401 | CSS_PSEUDO_ELEMENT_NONE]; | |||
1402 | ||||
1403 | error = cascade_style(sel->style, &state); | |||
1404 | if (error != CSS_OK) | |||
1405 | goto cleanup; | |||
1406 | } | |||
1407 | } | |||
1408 | ||||
1409 | /* Fix up any remaining unset properties. */ | |||
1410 | ||||
1411 | /* Base element */ | |||
1412 | state.current_pseudo = CSS_PSEUDO_ELEMENT_NONE; | |||
1413 | state.computed = state.results->styles[CSS_PSEUDO_ELEMENT_NONE]; | |||
1414 | for (i = 0; i < CSS_N_PROPERTIES; i++) { | |||
1415 | prop_state *prop = &state.props[i][CSS_PSEUDO_ELEMENT_NONE]; | |||
1416 | ||||
1417 | if (prop->explicit_default == FLAG_VALUE_REVERT) { | |||
1418 | error = css__select_revert_property(&state, prop, | |||
1419 | CSS_PSEUDO_ELEMENT_NONE, i); | |||
1420 | if (error != CSS_OK) { | |||
1421 | goto cleanup; | |||
1422 | } | |||
1423 | } | |||
1424 | ||||
1425 | if (prop->explicit_default == FLAG_VALUE_UNSET) { | |||
1426 | if (prop_dispatch[i].inherited == true1) { | |||
1427 | prop->explicit_default = FLAG_VALUE_INHERIT; | |||
1428 | } else { | |||
1429 | prop->explicit_default = FLAG_VALUE_INITIAL; | |||
1430 | } | |||
1431 | } | |||
1432 | ||||
1433 | /* If the property is still unset or it's set to inherit | |||
1434 | * and we're the root element, then set it to its initial | |||
1435 | * value. */ | |||
1436 | if (prop->explicit_default == FLAG_VALUE_INITIAL || | |||
1437 | prop->set == false0 || | |||
1438 | (parent == NULL((void*)0) && | |||
1439 | prop->explicit_default == FLAG_VALUE_INHERIT)) { | |||
1440 | error = set_initial(&state, i, | |||
1441 | CSS_PSEUDO_ELEMENT_NONE, parent); | |||
1442 | if (error != CSS_OK) | |||
1443 | goto cleanup; | |||
1444 | } | |||
1445 | } | |||
1446 | ||||
1447 | /* Pseudo elements, if any */ | |||
1448 | for (j = CSS_PSEUDO_ELEMENT_NONE + 1; j < CSS_PSEUDO_ELEMENT_COUNT; j++) { | |||
1449 | state.current_pseudo = j; | |||
1450 | state.computed = state.results->styles[j]; | |||
1451 | ||||
1452 | /* Skip non-existent pseudo elements */ | |||
1453 | if (state.computed == NULL((void*)0)) | |||
1454 | continue; | |||
1455 | ||||
1456 | for (i = 0; i < CSS_N_PROPERTIES; i++) { | |||
1457 | prop_state *prop = &state.props[i][j]; | |||
1458 | ||||
1459 | if (prop->explicit_default == FLAG_VALUE_REVERT) { | |||
1460 | error = css__select_revert_property(&state, | |||
1461 | prop, j, i); | |||
1462 | if (error != CSS_OK) { | |||
1463 | goto cleanup; | |||
1464 | } | |||
1465 | } | |||
1466 | ||||
1467 | if (prop->explicit_default == FLAG_VALUE_UNSET) { | |||
1468 | if (prop_dispatch[i].inherited == true1) { | |||
1469 | prop->explicit_default = FLAG_VALUE_INHERIT; | |||
1470 | } else { | |||
1471 | prop->explicit_default = FLAG_VALUE_INITIAL; | |||
1472 | } | |||
1473 | } | |||
1474 | ||||
1475 | /* If the property is still unset then set it | |||
1476 | * to its initial value. */ | |||
1477 | if (prop->explicit_default == FLAG_VALUE_INITIAL || | |||
1478 | prop->set == false0) { | |||
1479 | error = set_initial(&state, i, j, parent); | |||
1480 | if (error != CSS_OK) | |||
1481 | goto cleanup; | |||
1482 | } | |||
1483 | } | |||
1484 | } | |||
1485 | ||||
1486 | /* If this is the root element, then we must ensure that all | |||
1487 | * length values are absolute, display and float are correctly | |||
1488 | * computed, and the default border-{top,right,bottom,left}-color | |||
1489 | * is set to the computed value of color. */ | |||
1490 | if (parent == NULL((void*)0)) { | |||
1491 | /* Only compute absolute values for the base element */ | |||
1492 | error = css__compute_absolute_values(NULL((void*)0), | |||
1493 | state.results->styles[CSS_PSEUDO_ELEMENT_NONE], | |||
1494 | unit_ctx); | |||
1495 | if (error != CSS_OK) | |||
1496 | goto cleanup; | |||
1497 | } | |||
1498 | ||||
1499 | /* Intern the partial computed styles */ | |||
1500 | for (j = CSS_PSEUDO_ELEMENT_NONE; j < CSS_PSEUDO_ELEMENT_COUNT; j++) { | |||
1501 | /* Skip non-existent pseudo elements */ | |||
1502 | if (state.results->styles[j] == NULL((void*)0)) | |||
1503 | continue; | |||
1504 | ||||
1505 | error = css__arena_intern_style(&state.results->styles[j]); | |||
1506 | if (error != CSS_OK) { | |||
1507 | goto cleanup; | |||
1508 | } | |||
1509 | } | |||
1510 | ||||
1511 | complete: | |||
1512 | error = css__set_node_data(node, &state, handler, pw); | |||
1513 | if (error != CSS_OK) { | |||
1514 | goto cleanup; | |||
1515 | } | |||
1516 | ||||
1517 | /* Steal the results from the selection state, so they don't get | |||
1518 | * freed when the selection state is finalised */ | |||
1519 | *result = state.results; | |||
1520 | state.results = NULL((void*)0); | |||
1521 | ||||
1522 | error = CSS_OK; | |||
1523 | ||||
1524 | cleanup: | |||
1525 | css_select__finalise_selection_state(&state); | |||
1526 | ||||
1527 | return error; | |||
1528 | } | |||
1529 | ||||
1530 | /** | |||
1531 | * Destroy a selection result set | |||
1532 | * | |||
1533 | * \param results Result set to destroy | |||
1534 | * \return CSS_OK on success, appropriate error otherwise | |||
1535 | */ | |||
1536 | css_error css_select_results_destroy(css_select_results *results) | |||
1537 | { | |||
1538 | uint32_t i; | |||
1539 | ||||
1540 | if (results == NULL((void*)0)) | |||
1541 | return CSS_BADPARM; | |||
1542 | ||||
1543 | for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++) { | |||
1544 | if (results->styles[i] != NULL((void*)0)) | |||
1545 | css_computed_style_destroy(results->styles[i]); | |||
1546 | } | |||
1547 | ||||
1548 | free(results); | |||
1549 | ||||
1550 | return CSS_OK; | |||
1551 | } | |||
1552 | ||||
1553 | /** | |||
1554 | * Search a selection context for defined font faces | |||
1555 | * | |||
1556 | * \param ctx Selection context | |||
1557 | * \param media Currently active media spec | |||
1558 | * \param unit_ctx Current unit conversion context. | |||
1559 | * \param font_family Font family to search for | |||
1560 | * \param result Pointer to location to receive result | |||
1561 | * \return CSS_OK on success, appropriate error otherwise. | |||
1562 | */ | |||
1563 | css_error css_select_font_faces(css_select_ctx *ctx, | |||
1564 | const css_media *media, | |||
1565 | const css_unit_ctx *unit_ctx, | |||
1566 | lwc_string *font_family, | |||
1567 | css_select_font_faces_results **result) | |||
1568 | { | |||
1569 | uint32_t i; | |||
1570 | css_error error; | |||
1571 | css_select_font_faces_state state; | |||
1572 | uint32_t n_font_faces; | |||
1573 | ||||
1574 | if (ctx == NULL((void*)0) || font_family == NULL((void*)0) || result == NULL((void*)0)) | |||
1575 | return CSS_BADPARM; | |||
1576 | ||||
1577 | memset(&state, 0, sizeof(css_select_font_faces_state)); | |||
1578 | state.font_family = font_family; | |||
1579 | state.media = media; | |||
1580 | state.unit_ctx = unit_ctx; | |||
1581 | ||||
1582 | /* Iterate through the top-level stylesheets, selecting font-faces | |||
1583 | * from those which apply to our current media requirements and | |||
1584 | * are not disabled */ | |||
1585 | for (i = 0; i < ctx->n_sheets; i++) { | |||
1586 | const css_select_sheet s = ctx->sheets[i]; | |||
1587 | ||||
1588 | if (mq__list_match(s.media, unit_ctx, media, &ctx->str) && | |||
1589 | s.sheet->disabled == false0) { | |||
1590 | error = select_font_faces_from_sheet(s.sheet, | |||
1591 | s.origin, &state, &ctx->str); | |||
1592 | if (error != CSS_OK) | |||
1593 | goto cleanup; | |||
1594 | } | |||
1595 | } | |||
1596 | ||||
1597 | n_font_faces = state.ua_font_faces.count + | |||
1598 | state.user_font_faces.count + | |||
1599 | state.author_font_faces.count; | |||
1600 | ||||
1601 | ||||
1602 | if (n_font_faces > 0) { | |||
1603 | /* We found some matching faces. Make a results structure with | |||
1604 | * the font faces in priority order. */ | |||
1605 | css_select_font_faces_results *results; | |||
1606 | ||||
1607 | results = malloc(sizeof(css_select_font_faces_results)); | |||
1608 | if (results == NULL((void*)0)) { | |||
1609 | error = CSS_NOMEM; | |||
1610 | goto cleanup; | |||
1611 | } | |||
1612 | ||||
1613 | results->font_faces = malloc( | |||
1614 | n_font_faces * sizeof(css_font_face *)); | |||
1615 | if (results->font_faces == NULL((void*)0)) { | |||
1616 | free(results); | |||
1617 | error = CSS_NOMEM; | |||
1618 | goto cleanup; | |||
1619 | } | |||
1620 | ||||
1621 | results->n_font_faces = n_font_faces; | |||
1622 | ||||
1623 | i = 0; | |||
1624 | if (state.ua_font_faces.count != 0) { | |||
1625 | memcpy(results->font_faces, | |||
1626 | state.ua_font_faces.font_faces, | |||
1627 | sizeof(css_font_face *) * | |||
1628 | state.ua_font_faces.count); | |||
1629 | ||||
1630 | i += state.ua_font_faces.count; | |||
1631 | } | |||
1632 | ||||
1633 | if (state.user_font_faces.count != 0) { | |||
1634 | memcpy(results->font_faces + i, | |||
1635 | state.user_font_faces.font_faces, | |||
1636 | sizeof(css_font_face *) * | |||
1637 | state.user_font_faces.count); | |||
1638 | i += state.user_font_faces.count; | |||
1639 | } | |||
1640 | ||||
1641 | if (state.author_font_faces.count != 0) { | |||
1642 | memcpy(results->font_faces + i, | |||
1643 | state.author_font_faces.font_faces, | |||
1644 | sizeof(css_font_face *) * | |||
1645 | state.author_font_faces.count); | |||
1646 | } | |||
1647 | ||||
1648 | *result = results; | |||
1649 | } | |||
1650 | ||||
1651 | error = CSS_OK; | |||
1652 | ||||
1653 | cleanup: | |||
1654 | if (state.ua_font_faces.count != 0) | |||
1655 | free(state.ua_font_faces.font_faces); | |||
1656 | ||||
1657 | if (state.user_font_faces.count != 0) | |||
1658 | free(state.user_font_faces.font_faces); | |||
1659 | ||||
1660 | if (state.author_font_faces.count != 0) | |||
1661 | free(state.author_font_faces.font_faces); | |||
1662 | ||||
1663 | return error; | |||
1664 | } | |||
1665 | ||||
1666 | /** | |||
1667 | * Destroy a font-face result set | |||
1668 | * | |||
1669 | * \param results Result set to destroy | |||
1670 | * \return CSS_OK on success, appropriate error otherwise | |||
1671 | */ | |||
1672 | css_error css_select_font_faces_results_destroy( | |||
1673 | css_select_font_faces_results *results) | |||
1674 | { | |||
1675 | if (results == NULL((void*)0)) | |||
1676 | return CSS_BADPARM; | |||
1677 | ||||
1678 | if (results->font_faces != NULL((void*)0)) { | |||
1679 | /* Don't destroy the individual css_font_faces, they're owned | |||
1680 | by their respective sheets */ | |||
1681 | free(results->font_faces); | |||
1682 | } | |||
1683 | ||||
1684 | free(results); | |||
1685 | ||||
1686 | return CSS_OK; | |||
1687 | } | |||
1688 | ||||
1689 | /****************************************************************************** | |||
1690 | * Selection engine internals below here * | |||
1691 | ******************************************************************************/ | |||
1692 | ||||
1693 | ||||
1694 | css_error set_hint(css_select_state *state, css_hint *hint) | |||
1695 | { | |||
1696 | uint32_t prop = hint->prop; | |||
1697 | prop_state *existing = &state->props[prop][CSS_PSEUDO_ELEMENT_NONE]; | |||
1698 | css_error error; | |||
1699 | ||||
1700 | /* Hint defined -- set it in the result */ | |||
1701 | error = prop_dispatch[prop].set_from_hint(hint, state->computed); | |||
1702 | if (error != CSS_OK) | |||
1703 | return error; | |||
1704 | ||||
1705 | /* Keep selection state in sync with reality */ | |||
1706 | existing->set = 1; | |||
1707 | existing->specificity = 0; | |||
1708 | existing->origin = CSS_ORIGIN_AUTHOR; | |||
1709 | existing->important = 0; | |||
1710 | existing->explicit_default = (hint->status == 0) ? | |||
1711 | FLAG_VALUE_INHERIT : FLAG_VALUE__NONE; | |||
1712 | ||||
1713 | return CSS_OK; | |||
1714 | } | |||
1715 | ||||
1716 | css_error set_initial(css_select_state *state, | |||
1717 | uint32_t prop, css_pseudo_element pseudo, | |||
1718 | void *parent) | |||
1719 | { | |||
1720 | css_error error; | |||
1721 | ||||
1722 | /* Do nothing if this property is inherited (the default state | |||
1723 | * of a clean computed style is for everything to be set to inherit) | |||
1724 | * | |||
1725 | * If the node is tree root and we're dealing with the base element, | |||
1726 | * everything should be defaulted. | |||
1727 | */ | |||
1728 | if (state->props[prop][pseudo].explicit_default == FLAG_VALUE_INITIAL || | |||
1729 | prop_dispatch[prop].inherited == false0 || | |||
1730 | (pseudo == CSS_PSEUDO_ELEMENT_NONE && parent == NULL((void*)0))) { | |||
1731 | error = prop_dispatch[prop].initial(state); | |||
1732 | if (error != CSS_OK) | |||
1733 | return error; | |||
1734 | } | |||
1735 | ||||
1736 | return CSS_OK; | |||
1737 | } | |||
1738 | ||||
1739 | #define IMPORT_STACK_SIZE 256 | |||
1740 | ||||
1741 | css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, | |||
1742 | css_origin origin, css_select_state *state) | |||
1743 | { | |||
1744 | const css_stylesheet *s = sheet; | |||
1745 | const css_rule *rule = s->rule_list; | |||
1746 | uint32_t sp = 0; | |||
1747 | const css_rule *import_stack[IMPORT_STACK_SIZE]; | |||
1748 | ||||
1749 | do { | |||
1750 | /* Find first non-charset rule, if we're at the list head */ | |||
1751 | if (rule == s->rule_list) { | |||
1752 | while (rule != NULL((void*)0) && rule->type == CSS_RULE_CHARSET) | |||
1753 | rule = rule->next; | |||
1754 | } | |||
1755 | ||||
1756 | if (rule != NULL((void*)0) && rule->type == CSS_RULE_IMPORT) { | |||
1757 | /* Current rule is an import */ | |||
1758 | const css_rule_import *import = | |||
1759 | (const css_rule_import *) rule; | |||
1760 | ||||
1761 | if (import->sheet != NULL((void*)0) && | |||
1762 | mq__list_match(import->media, | |||
1763 | state->unit_ctx, | |||
1764 | state->media, | |||
1765 | &ctx->str)) { | |||
1766 | /* It's applicable, so process it */ | |||
1767 | if (sp >= IMPORT_STACK_SIZE) | |||
1768 | return CSS_NOMEM; | |||
1769 | ||||
1770 | import_stack[sp++] = rule; | |||
1771 | ||||
1772 | s = import->sheet; | |||
1773 | rule = s->rule_list; | |||
1774 | } else { | |||
1775 | /* Not applicable; skip over it */ | |||
1776 | rule = rule->next; | |||
1777 | } | |||
1778 | } else { | |||
1779 | /* Gone past import rules in this sheet */ | |||
1780 | css_error error; | |||
1781 | ||||
1782 | /* Process this sheet */ | |||
1783 | state->sheet = s; | |||
1784 | state->current_origin = origin; | |||
1785 | ||||
1786 | error = match_selectors_in_sheet(ctx, s, state); | |||
1787 | if (error != CSS_OK) | |||
1788 | return error; | |||
1789 | ||||
1790 | /* Find next sheet to process */ | |||
1791 | if (sp > 0) { | |||
1792 | sp--; | |||
1793 | rule = import_stack[sp]->next; | |||
1794 | s = import_stack[sp]->parent; | |||
1795 | } else { | |||
1796 | s = NULL((void*)0); | |||
1797 | } | |||
1798 | } | |||
1799 | } while (s != NULL((void*)0)); | |||
1800 | ||||
1801 | return CSS_OK; | |||
1802 | } | |||
1803 | ||||
1804 | static css_error _select_font_face_from_rule( | |||
1805 | const css_rule_font_face *rule, css_origin origin, | |||
1806 | css_select_font_faces_state *state, | |||
1807 | const css_select_strings *str) | |||
1808 | { | |||
1809 | if (mq_rule_good_for_media((const css_rule *) rule, | |||
1810 | state->unit_ctx, state->media, str)) { | |||
1811 | bool_Bool correct_family = false0; | |||
1812 | ||||
1813 | if (lwc_string_isequal(((*(&correct_family) = ((rule->font_face->font_family ) == (state->font_family))), lwc_error_ok) | |||
1814 | rule->font_face->font_family,((*(&correct_family) = ((rule->font_face->font_family ) == (state->font_family))), lwc_error_ok) | |||
1815 | state->font_family,((*(&correct_family) = ((rule->font_face->font_family ) == (state->font_family))), lwc_error_ok) | |||
1816 | &correct_family)((*(&correct_family) = ((rule->font_face->font_family ) == (state->font_family))), lwc_error_ok) == lwc_error_ok && | |||
1817 | correct_family) { | |||
1818 | css_select_font_faces_list *faces = NULL((void*)0); | |||
1819 | const css_font_face **new_faces; | |||
1820 | uint32_t index; | |||
1821 | size_t new_size; | |||
1822 | ||||
1823 | switch (origin) { | |||
1824 | case CSS_ORIGIN_UA: | |||
1825 | faces = &state->ua_font_faces; | |||
1826 | break; | |||
1827 | case CSS_ORIGIN_USER: | |||
1828 | faces = &state->user_font_faces; | |||
1829 | break; | |||
1830 | case CSS_ORIGIN_AUTHOR: | |||
1831 | faces = &state->author_font_faces; | |||
1832 | break; | |||
1833 | } | |||
1834 | ||||
1835 | index = faces->count++; | |||
1836 | new_size = faces->count * sizeof(css_font_face *); | |||
1837 | ||||
1838 | new_faces = realloc(faces->font_faces, new_size); | |||
1839 | if (new_faces == NULL((void*)0)) { | |||
1840 | return CSS_NOMEM; | |||
1841 | } | |||
1842 | faces->font_faces = new_faces; | |||
1843 | ||||
1844 | faces->font_faces[index] = rule->font_face; | |||
1845 | } | |||
1846 | } | |||
1847 | ||||
1848 | return CSS_OK; | |||
1849 | } | |||
1850 | ||||
1851 | static css_error select_font_faces_from_sheet( | |||
1852 | const css_stylesheet *sheet, | |||
1853 | css_origin origin, | |||
1854 | css_select_font_faces_state *state, | |||
1855 | const css_select_strings *str) | |||
1856 | { | |||
1857 | const css_stylesheet *s = sheet; | |||
1858 | const css_rule *rule = s->rule_list; | |||
1859 | uint32_t sp = 0; | |||
1860 | const css_rule *import_stack[IMPORT_STACK_SIZE]; | |||
1861 | ||||
1862 | do { | |||
1863 | /* Find first non-charset rule, if we're at the list head */ | |||
1864 | if (rule == s->rule_list) { | |||
1865 | while (rule != NULL((void*)0) && rule->type == CSS_RULE_CHARSET) | |||
1866 | rule = rule->next; | |||
1867 | } | |||
1868 | ||||
1869 | if (rule != NULL((void*)0) && rule->type == CSS_RULE_IMPORT) { | |||
1870 | /* Current rule is an import */ | |||
1871 | const css_rule_import *import = | |||
1872 | (const css_rule_import *) rule; | |||
1873 | ||||
1874 | if (import->sheet != NULL((void*)0) && | |||
1875 | mq__list_match(import->media, | |||
1876 | state->unit_ctx, | |||
1877 | state->media, | |||
1878 | str)) { | |||
1879 | /* It's applicable, so process it */ | |||
1880 | if (sp >= IMPORT_STACK_SIZE) | |||
1881 | return CSS_NOMEM; | |||
1882 | ||||
1883 | import_stack[sp++] = rule; | |||
1884 | ||||
1885 | s = import->sheet; | |||
1886 | rule = s->rule_list; | |||
1887 | } else { | |||
1888 | /* Not applicable; skip over it */ | |||
1889 | rule = rule->next; | |||
1890 | } | |||
1891 | } else if (rule != NULL((void*)0) && rule->type == CSS_RULE_FONT_FACE) { | |||
1892 | css_error error; | |||
1893 | ||||
1894 | error = _select_font_face_from_rule( | |||
1895 | (const css_rule_font_face *) rule, | |||
1896 | origin, state, str); | |||
1897 | ||||
1898 | if (error != CSS_OK) | |||
1899 | return error; | |||
1900 | ||||
1901 | rule = rule->next; | |||
1902 | } else if (rule == NULL((void*)0)) { | |||
1903 | /* Find next sheet to process */ | |||
1904 | if (sp > 0) { | |||
1905 | sp--; | |||
1906 | rule = import_stack[sp]->next; | |||
1907 | s = import_stack[sp]->parent; | |||
1908 | } else { | |||
1909 | s = NULL((void*)0); | |||
1910 | } | |||
1911 | } else { | |||
1912 | rule = rule->next; | |||
1913 | } | |||
1914 | } while (s != NULL((void*)0)); | |||
1915 | ||||
1916 | return CSS_OK; | |||
1917 | } | |||
1918 | ||||
1919 | #undef IMPORT_STACK_SIZE | |||
1920 | ||||
1921 | static inline bool_Bool _selectors_pending(const css_selector **node, | |||
1922 | const css_selector **id, const css_selector ***classes, | |||
1923 | uint32_t n_classes, const css_selector **univ) | |||
1924 | { | |||
1925 | bool_Bool pending = false0; | |||
1926 | uint32_t i; | |||
1927 | ||||
1928 | pending |= *node != NULL((void*)0); | |||
1929 | pending |= *id != NULL((void*)0); | |||
1930 | pending |= *univ != NULL((void*)0); | |||
1931 | ||||
1932 | if (classes != NULL((void*)0) && n_classes > 0) { | |||
1933 | for (i = 0; i < n_classes; i++) | |||
1934 | pending |= *(classes[i]) != NULL((void*)0); | |||
1935 | } | |||
1936 | ||||
1937 | return pending; | |||
1938 | } | |||
1939 | ||||
1940 | static inline bool_Bool _selector_less_specific(const css_selector *ref, | |||
1941 | const css_selector *cand) | |||
1942 | { | |||
1943 | bool_Bool result = true1; | |||
1944 | ||||
1945 | if (cand == NULL((void*)0)) | |||
1946 | return false0; | |||
1947 | ||||
1948 | if (ref == NULL((void*)0)) | |||
1949 | return true1; | |||
1950 | ||||
1951 | /* Sort by specificity */ | |||
1952 | if (cand->specificity < ref->specificity) { | |||
1953 | result = true1; | |||
1954 | } else if (ref->specificity < cand->specificity) { | |||
1955 | result = false0; | |||
1956 | } else { | |||
1957 | /* Then by rule index -- earliest wins */ | |||
1958 | if (cand->rule->index < ref->rule->index) | |||
1959 | result = true1; | |||
1960 | else | |||
1961 | result = false0; | |||
1962 | } | |||
1963 | ||||
1964 | return result; | |||
1965 | } | |||
1966 | ||||
1967 | static const css_selector *_selector_next(const css_selector **node, | |||
1968 | const css_selector **id, const css_selector ***classes, | |||
1969 | uint32_t n_classes, const css_selector **univ, | |||
1970 | css_select_rule_source *src) | |||
1971 | { | |||
1972 | const css_selector *ret = NULL((void*)0); | |||
1973 | ||||
1974 | if (_selector_less_specific(ret, *node)) { | |||
1975 | ret = *node; | |||
1976 | src->source = CSS_SELECT_RULE_SRC_ELEMENT; | |||
1977 | } | |||
1978 | ||||
1979 | if (_selector_less_specific(ret, *id)) { | |||
1980 | ret = *id; | |||
1981 | src->source = CSS_SELECT_RULE_SRC_ID; | |||
1982 | } | |||
1983 | ||||
1984 | if (_selector_less_specific(ret, *univ)) { | |||
1985 | ret = *univ; | |||
1986 | src->source = CSS_SELECT_RULE_SRC_UNIVERSAL; | |||
1987 | } | |||
1988 | ||||
1989 | if (classes != NULL((void*)0) && n_classes > 0) { | |||
1990 | uint32_t i; | |||
1991 | ||||
1992 | for (i = 0; i < n_classes; i++) { | |||
1993 | if (_selector_less_specific(ret, *(classes[i]))) { | |||
1994 | ret = *(classes[i]); | |||
1995 | src->source = CSS_SELECT_RULE_SRC_CLASS; | |||
1996 | src->class = i; | |||
1997 | } | |||
1998 | } | |||
1999 | } | |||
2000 | ||||
2001 | return ret; | |||
2002 | } | |||
2003 | ||||
2004 | css_error match_selectors_in_sheet(css_select_ctx *ctx, | |||
2005 | const css_stylesheet *sheet, css_select_state *state) | |||
2006 | { | |||
2007 | static const css_selector *empty_selector = NULL((void*)0); | |||
2008 | const uint32_t n_classes = state->n_classes; | |||
2009 | uint32_t i = 0; | |||
2010 | const css_selector **node_selectors = &empty_selector; | |||
2011 | css_selector_hash_iterator node_iterator; | |||
2012 | const css_selector **id_selectors = &empty_selector; | |||
2013 | css_selector_hash_iterator id_iterator; | |||
2014 | const css_selector ***class_selectors = NULL((void*)0); | |||
2015 | css_selector_hash_iterator class_iterator; | |||
2016 | const css_selector **univ_selectors = &empty_selector; | |||
2017 | css_selector_hash_iterator univ_iterator; | |||
2018 | css_select_rule_source src = { CSS_SELECT_RULE_SRC_ELEMENT, 0 }; | |||
2019 | struct css_hash_selection_requirments req; | |||
2020 | css_error error; | |||
2021 | ||||
2022 | /* Set up general selector chain requirments */ | |||
2023 | req.media = state->media; | |||
2024 | req.unit_ctx = state->unit_ctx; | |||
2025 | req.node_bloom = state->node_data->bloom; | |||
2026 | req.str = &ctx->str; | |||
2027 | ||||
2028 | /* Find hash chain that applies to current node */ | |||
2029 | req.qname = state->element; | |||
2030 | error = css__selector_hash_find(sheet->selectors, | |||
2031 | &req, &node_iterator, | |||
2032 | &node_selectors); | |||
2033 | if (error != CSS_OK) | |||
2034 | goto cleanup; | |||
2035 | ||||
2036 | if (state->classes != NULL((void*)0) && n_classes > 0) { | |||
2037 | /* Find hash chains for node classes */ | |||
2038 | class_selectors = malloc(n_classes * sizeof(css_selector **)); | |||
2039 | if (class_selectors == NULL((void*)0)) { | |||
2040 | error = CSS_NOMEM; | |||
2041 | goto cleanup; | |||
2042 | } | |||
2043 | ||||
2044 | for (i = 0; i < n_classes; i++) { | |||
2045 | req.class = state->classes[i]; | |||
2046 | error = css__selector_hash_find_by_class( | |||
2047 | sheet->selectors, &req, | |||
2048 | &class_iterator, &class_selectors[i]); | |||
2049 | if (error != CSS_OK) | |||
2050 | goto cleanup; | |||
2051 | } | |||
2052 | } | |||
2053 | ||||
2054 | if (state->id != NULL((void*)0)) { | |||
2055 | /* Find hash chain for node ID */ | |||
2056 | req.id = state->id; | |||
2057 | error = css__selector_hash_find_by_id(sheet->selectors, | |||
2058 | &req, &id_iterator, &id_selectors); | |||
2059 | if (error != CSS_OK) | |||
2060 | goto cleanup; | |||
2061 | } | |||
2062 | ||||
2063 | /* Find hash chain for universal selector */ | |||
2064 | error = css__selector_hash_find_universal(sheet->selectors, &req, | |||
2065 | &univ_iterator, &univ_selectors); | |||
2066 | if (error != CSS_OK) | |||
2067 | goto cleanup; | |||
2068 | ||||
2069 | /* Process matching selectors, if any */ | |||
2070 | while (_selectors_pending(node_selectors, id_selectors, | |||
2071 | class_selectors, n_classes, univ_selectors)) { | |||
2072 | const css_selector *selector; | |||
2073 | ||||
2074 | /* Selectors must be matched in ascending order of specificity | |||
2075 | * and rule index. (c.f. css__outranks_existing()) | |||
2076 | * | |||
2077 | * Pick the least specific/earliest occurring selector. | |||
2078 | */ | |||
2079 | selector = _selector_next(node_selectors, id_selectors, | |||
2080 | class_selectors, n_classes, univ_selectors, | |||
2081 | &src); | |||
2082 | ||||
2083 | /* We know there are selectors pending, so should have a | |||
2084 | * selector here */ | |||
2085 | assert(selector != NULL)((selector != ((void*)0)) ? (void) (0) : __assert_fail ("selector != NULL" , "src/select/select.c", 2085, __extension__ __PRETTY_FUNCTION__ )); | |||
2086 | ||||
2087 | /* Match and handle the selector chain */ | |||
2088 | error = match_selector_chain(ctx, selector, state); | |||
2089 | if (error != CSS_OK) | |||
2090 | goto cleanup; | |||
2091 | ||||
2092 | /* Advance to next selector in whichever chain we extracted | |||
2093 | * the processed selector from. */ | |||
2094 | switch (src.source) { | |||
2095 | case CSS_SELECT_RULE_SRC_ELEMENT: | |||
2096 | error = node_iterator(&req, node_selectors, | |||
2097 | &node_selectors); | |||
2098 | break; | |||
2099 | ||||
2100 | case CSS_SELECT_RULE_SRC_ID: | |||
2101 | error = id_iterator(&req, id_selectors, | |||
2102 | &id_selectors); | |||
2103 | break; | |||
2104 | ||||
2105 | case CSS_SELECT_RULE_SRC_UNIVERSAL: | |||
2106 | error = univ_iterator(&req, univ_selectors, | |||
2107 | &univ_selectors); | |||
2108 | break; | |||
2109 | ||||
2110 | case CSS_SELECT_RULE_SRC_CLASS: | |||
2111 | req.class = state->classes[src.class]; | |||
2112 | error = class_iterator(&req, class_selectors[src.class], | |||
2113 | &class_selectors[src.class]); | |||
2114 | break; | |||
2115 | } | |||
2116 | ||||
2117 | if (error != CSS_OK) | |||
2118 | goto cleanup; | |||
2119 | } | |||
2120 | ||||
2121 | error = CSS_OK; | |||
2122 | cleanup: | |||
2123 | if (class_selectors != NULL((void*)0)) | |||
2124 | free(class_selectors); | |||
2125 | ||||
2126 | return error; | |||
2127 | } | |||
2128 | ||||
2129 | static void update_reject_cache(css_select_state *state, | |||
2130 | css_combinator comb, const css_selector *s) | |||
2131 | { | |||
2132 | const css_selector_detail *detail = &s->data; | |||
2133 | const css_selector_detail *next_detail = NULL((void*)0); | |||
2134 | ||||
2135 | if (detail->next) | |||
2136 | next_detail = detail + 1; | |||
2137 | ||||
2138 | if (state->next_reject < state->reject_cache || | |||
2139 | comb != CSS_COMBINATOR_ANCESTOR || | |||
2140 | next_detail == NULL((void*)0) || | |||
2141 | next_detail->next != 0 || | |||
2142 | (next_detail->type != CSS_SELECTOR_CLASS && | |||
2143 | next_detail->type != CSS_SELECTOR_ID)) | |||
2144 | return; | |||
2145 | ||||
2146 | /* Insert */ | |||
2147 | state->next_reject->type = next_detail->type; | |||
2148 | state->next_reject->value = next_detail->qname.name; | |||
2149 | state->next_reject--; | |||
2150 | } | |||
2151 | ||||
2152 | css_error match_selector_chain(css_select_ctx *ctx, | |||
2153 | const css_selector *selector, css_select_state *state) | |||
2154 | { | |||
2155 | const css_selector *s = selector; | |||
2156 | void *node = state->node; | |||
2157 | const css_selector_detail *detail = &s->data; | |||
2158 | bool_Bool match = false0, may_optimise = true1; | |||
2159 | bool_Bool rejected_by_cache; | |||
2160 | css_pseudo_element pseudo; | |||
2161 | css_error error; | |||
2162 | ||||
2163 | #ifdef DEBUG_CHAIN_MATCHING | |||
2164 | fprintf(stderrstderr, "matching: "); | |||
2165 | dump_chain(selector); | |||
2166 | fprintf(stderrstderr, "\n"); | |||
2167 | #endif | |||
2168 | ||||
2169 | /* Match the details of the first selector in the chain. | |||
2170 | * | |||
2171 | * Note that pseudo elements will only appear as details of | |||
2172 | * the first selector in the chain, as the parser will reject | |||
2173 | * any selector chains containing pseudo elements anywhere | |||
2174 | * else. | |||
2175 | */ | |||
2176 | error = match_details(ctx, node, detail, state, &match, &pseudo); | |||
2177 | if (error != CSS_OK) | |||
2178 | return error; | |||
2179 | ||||
2180 | /* Details don't match, so reject selector chain */ | |||
2181 | if (match == false0) | |||
2182 | return CSS_OK; | |||
2183 | ||||
2184 | /* Iterate up the selector chain, matching combinators */ | |||
2185 | do { | |||
2186 | void *next_node = NULL((void*)0); | |||
2187 | ||||
2188 | /* Consider any combinator on this selector */ | |||
2189 | if (s->data.comb != CSS_COMBINATOR_NONE && | |||
2190 | s->combinator->data.qname.name != | |||
2191 | ctx->str.universal) { | |||
2192 | /* Named combinator */ | |||
2193 | may_optimise &= | |||
2194 | (s->data.comb == CSS_COMBINATOR_ANCESTOR || | |||
2195 | s->data.comb == CSS_COMBINATOR_PARENT); | |||
2196 | ||||
2197 | error = match_named_combinator(ctx, s->data.comb, | |||
2198 | s->combinator, state, node, &next_node); | |||
2199 | if (error != CSS_OK) | |||
2200 | return error; | |||
2201 | ||||
2202 | /* No match for combinator, so reject selector chain */ | |||
2203 | if (next_node == NULL((void*)0)) | |||
2204 | return CSS_OK; | |||
2205 | } else if (s->data.comb != CSS_COMBINATOR_NONE) { | |||
2206 | /* Universal combinator */ | |||
2207 | may_optimise &= | |||
2208 | (s->data.comb == CSS_COMBINATOR_ANCESTOR || | |||
2209 | s->data.comb == CSS_COMBINATOR_PARENT); | |||
2210 | ||||
2211 | error = match_universal_combinator(ctx, s->data.comb, | |||
2212 | s->combinator, state, node, | |||
2213 | may_optimise, &rejected_by_cache, | |||
2214 | &next_node); | |||
2215 | if (error != CSS_OK) | |||
2216 | return error; | |||
2217 | ||||
2218 | /* No match for combinator, so reject selector chain */ | |||
2219 | if (next_node == NULL((void*)0)) { | |||
2220 | if (may_optimise && s == selector && | |||
2221 | rejected_by_cache == false0) { | |||
2222 | update_reject_cache(state, s->data.comb, | |||
2223 | s->combinator); | |||
2224 | } | |||
2225 | ||||
2226 | return CSS_OK; | |||
2227 | } | |||
2228 | } | |||
2229 | ||||
2230 | /* Details matched, so progress to combining selector */ | |||
2231 | s = s->combinator; | |||
2232 | node = next_node; | |||
2233 | } while (s != NULL((void*)0)); | |||
2234 | ||||
2235 | /* If we got here, then the entire selector chain matched, so cascade */ | |||
2236 | state->current_specificity = selector->specificity; | |||
2237 | ||||
2238 | /* Ensure that the appropriate computed style exists */ | |||
2239 | if (state->results->styles[pseudo] == NULL((void*)0)) { | |||
2240 | error = css__computed_style_create( | |||
2241 | &state->results->styles[pseudo], ctx->calc); | |||
2242 | if (error != CSS_OK) | |||
2243 | return error; | |||
2244 | } | |||
2245 | ||||
2246 | state->current_pseudo = pseudo; | |||
2247 | state->computed = state->results->styles[pseudo]; | |||
2248 | ||||
2249 | return cascade_style(((css_rule_selector *) selector->rule)->style, | |||
2250 | state); | |||
2251 | } | |||
2252 | ||||
2253 | css_error match_named_combinator(css_select_ctx *ctx, css_combinator type, | |||
2254 | const css_selector *selector, css_select_state *state, | |||
2255 | void *node, void **next_node) | |||
2256 | { | |||
2257 | const css_selector_detail *detail = &selector->data; | |||
2258 | void *n = node; | |||
2259 | css_error error; | |||
2260 | ||||
2261 | do { | |||
2262 | bool_Bool match = false0; | |||
2263 | ||||
2264 | /* Find candidate node */ | |||
2265 | switch (type) { | |||
2266 | case CSS_COMBINATOR_ANCESTOR: | |||
2267 | error = state->handler->named_ancestor_node(state->pw, | |||
2268 | n, &selector->data.qname, &n); | |||
2269 | if (error != CSS_OK) | |||
2270 | return error; | |||
2271 | break; | |||
2272 | case CSS_COMBINATOR_PARENT: | |||
2273 | error = state->handler->named_parent_node(state->pw, | |||
2274 | n, &selector->data.qname, &n); | |||
2275 | if (error != CSS_OK) | |||
2276 | return error; | |||
2277 | break; | |||
2278 | case CSS_COMBINATOR_SIBLING: | |||
2279 | error = state->handler->named_sibling_node(state->pw, | |||
2280 | n, &selector->data.qname, &n); | |||
2281 | if (error != CSS_OK) | |||
2282 | return error; | |||
2283 | if (node == state->node) { | |||
2284 | state->node_data->flags |= | |||
2285 | CSS_NODE_FLAGS_TAINT_SIBLING; | |||
2286 | } | |||
2287 | break; | |||
2288 | case CSS_COMBINATOR_GENERIC_SIBLING: | |||
2289 | error = state->handler->named_generic_sibling_node( | |||
2290 | state->pw, n, &selector->data.qname, | |||
2291 | &n); | |||
2292 | if (error != CSS_OK) | |||
2293 | return error; | |||
2294 | if (node == state->node) { | |||
2295 | state->node_data->flags |= | |||
2296 | CSS_NODE_FLAGS_TAINT_SIBLING; | |||
2297 | } | |||
2298 | case CSS_COMBINATOR_NONE: | |||
2299 | break; | |||
2300 | } | |||
2301 | ||||
2302 | if (n != NULL((void*)0)) { | |||
2303 | /* Match its details */ | |||
2304 | error = match_details(ctx, n, detail, state, | |||
2305 | &match, NULL((void*)0)); | |||
2306 | if (error != CSS_OK) | |||
2307 | return error; | |||
2308 | ||||
2309 | /* If we found a match, use it */ | |||
2310 | if (match == true1) | |||
2311 | break; | |||
2312 | ||||
2313 | /* For parent and sibling selectors, only adjacent | |||
2314 | * nodes are valid. Thus, if we failed to match, | |||
2315 | * give up. */ | |||
2316 | if (type == CSS_COMBINATOR_PARENT || | |||
2317 | type == CSS_COMBINATOR_SIBLING) | |||
2318 | n = NULL((void*)0); | |||
2319 | } | |||
2320 | } while (n != NULL((void*)0)); | |||
2321 | ||||
2322 | *next_node = n; | |||
2323 | ||||
2324 | return CSS_OK; | |||
2325 | } | |||
2326 | ||||
2327 | static inline void add_node_flags(const void *node, | |||
2328 | const css_select_state *state, css_node_flags flags) | |||
2329 | { | |||
2330 | /* If the node in question is the node we're selecting for then its | |||
2331 | * style has been tainted by particular rules that affect whether the | |||
2332 | * node's style can be shared. We don't care whether the rule matched | |||
2333 | * or not, just that such a rule has been considered. */ | |||
2334 | if (node == state->node) { | |||
2335 | state->node_data->flags |= flags; | |||
2336 | } | |||
2337 | } | |||
2338 | ||||
2339 | css_error match_universal_combinator(css_select_ctx *ctx, css_combinator type, | |||
2340 | const css_selector *selector, css_select_state *state, | |||
2341 | void *node, bool_Bool may_optimise, bool_Bool *rejected_by_cache, | |||
2342 | void **next_node) | |||
2343 | { | |||
2344 | const css_selector_detail *detail = &selector->data; | |||
2345 | const css_selector_detail *next_detail = NULL((void*)0); | |||
2346 | void *n = node; | |||
2347 | css_error error; | |||
2348 | ||||
2349 | if (detail->next) | |||
2350 | next_detail = detail + 1; | |||
2351 | ||||
2352 | *rejected_by_cache = false0; | |||
2353 | ||||
2354 | /* Consult reject cache first */ | |||
2355 | if (may_optimise && (type == CSS_COMBINATOR_ANCESTOR || | |||
2356 | type == CSS_COMBINATOR_PARENT) && | |||
2357 | next_detail != NULL((void*)0) && | |||
2358 | (next_detail->type == CSS_SELECTOR_CLASS || | |||
2359 | next_detail->type == CSS_SELECTOR_ID)) { | |||
2360 | reject_item *reject = state->next_reject + 1; | |||
2361 | reject_item *last = state->reject_cache + | |||
2362 | N_ELEMENTS(state->reject_cache)(sizeof((state->reject_cache)) / sizeof((state->reject_cache )[0])) - 1; | |||
2363 | bool_Bool match = false0; | |||
2364 | ||||
2365 | while (reject <= last) { | |||
2366 | /* Perform pessimistic matching (may hurt quirks) */ | |||
2367 | if (reject->type == next_detail->type && | |||
2368 | lwc_string_isequal(reject->value,((*(&match) = ((reject->value) == (next_detail->qname .name))), lwc_error_ok) | |||
2369 | next_detail->qname.name,((*(&match) = ((reject->value) == (next_detail->qname .name))), lwc_error_ok) | |||
2370 | &match)((*(&match) = ((reject->value) == (next_detail->qname .name))), lwc_error_ok) == lwc_error_ok && | |||
2371 | match) { | |||
2372 | /* Found it: can't match */ | |||
2373 | *next_node = NULL((void*)0); | |||
2374 | *rejected_by_cache = true1; | |||
2375 | return CSS_OK; | |||
2376 | } | |||
2377 | ||||
2378 | reject++; | |||
2379 | } | |||
2380 | } | |||
2381 | ||||
2382 | do { | |||
2383 | bool_Bool match = false0; | |||
2384 | ||||
2385 | /* Find candidate node */ | |||
2386 | switch (type) { | |||
2387 | case CSS_COMBINATOR_ANCESTOR: | |||
2388 | case CSS_COMBINATOR_PARENT: | |||
2389 | error = state->handler->parent_node(state->pw, n, &n); | |||
2390 | if (error != CSS_OK) | |||
2391 | return error; | |||
2392 | break; | |||
2393 | case CSS_COMBINATOR_SIBLING: | |||
2394 | case CSS_COMBINATOR_GENERIC_SIBLING: | |||
2395 | error = state->handler->sibling_node(state->pw, n, &n); | |||
2396 | if (error != CSS_OK) | |||
2397 | return error; | |||
2398 | add_node_flags(node, state, | |||
2399 | CSS_NODE_FLAGS_TAINT_SIBLING); | |||
2400 | break; | |||
2401 | case CSS_COMBINATOR_NONE: | |||
2402 | break; | |||
2403 | } | |||
2404 | ||||
2405 | if (n != NULL((void*)0)) { | |||
2406 | /* Match its details */ | |||
2407 | error = match_details(ctx, n, detail, state, | |||
2408 | &match, NULL((void*)0)); | |||
2409 | if (error != CSS_OK) | |||
2410 | return error; | |||
2411 | ||||
2412 | /* If we found a match, use it */ | |||
2413 | if (match == true1) | |||
2414 | break; | |||
2415 | ||||
2416 | /* For parent and sibling selectors, only adjacent | |||
2417 | * nodes are valid. Thus, if we failed to match, | |||
2418 | * give up. */ | |||
2419 | if (type == CSS_COMBINATOR_PARENT || | |||
2420 | type == CSS_COMBINATOR_SIBLING) | |||
2421 | n = NULL((void*)0); | |||
2422 | } | |||
2423 | } while (n != NULL((void*)0)); | |||
2424 | ||||
2425 | *next_node = n; | |||
2426 | ||||
2427 | return CSS_OK; | |||
2428 | } | |||
2429 | ||||
2430 | css_error match_details(css_select_ctx *ctx, void *node, | |||
2431 | const css_selector_detail *detail, css_select_state *state, | |||
2432 | bool_Bool *match, css_pseudo_element *pseudo_element) | |||
2433 | { | |||
2434 | css_error error; | |||
2435 | css_pseudo_element pseudo = CSS_PSEUDO_ELEMENT_NONE; | |||
2436 | ||||
2437 | /* Skip the element selector detail, which is always first. | |||
2438 | * (Named elements are handled by match_named_combinator, so the | |||
2439 | * element selector detail always matches here.) */ | |||
2440 | if (detail->next) | |||
2441 | detail++; | |||
2442 | else | |||
2443 | detail = NULL((void*)0); | |||
2444 | ||||
2445 | /* We match by default (if there are no details other than the element | |||
2446 | * selector, then we must match) */ | |||
2447 | *match = true1; | |||
2448 | ||||
2449 | /** \todo Some details are easier to test than others (e.g. dashmatch | |||
2450 | * actually requires looking at data rather than simply comparing | |||
2451 | * pointers). Should we consider sorting the detail list such that the | |||
2452 | * simpler details come first (and thus the expensive match routines | |||
2453 | * can be avoided unless absolutely necessary)? */ | |||
2454 | ||||
2455 | while (detail != NULL((void*)0)) { | |||
2456 | error = match_detail(ctx, node, detail, state, match, &pseudo); | |||
2457 | if (error != CSS_OK) | |||
2458 | return error; | |||
2459 | ||||
2460 | /* Detail doesn't match, so reject selector chain */ | |||
2461 | if (*match == false0) | |||
2462 | return CSS_OK; | |||
2463 | ||||
2464 | if (detail->next) | |||
2465 | detail++; | |||
2466 | else | |||
2467 | detail = NULL((void*)0); | |||
2468 | } | |||
2469 | ||||
2470 | /* Return the applicable pseudo element, if required */ | |||
2471 | if (pseudo_element != NULL((void*)0)) | |||
2472 | *pseudo_element = pseudo; | |||
2473 | ||||
2474 | return CSS_OK; | |||
2475 | } | |||
2476 | ||||
2477 | static inline bool_Bool match_nth(int32_t a, int32_t b, int32_t count) | |||
2478 | { | |||
2479 | if (a == 0) { | |||
2480 | return count == b; | |||
2481 | } else { | |||
2482 | const int32_t delta = count - b; | |||
2483 | ||||
2484 | /* (count - b) / a is positive or (count - b) is 0 */ | |||
2485 | if (((delta > 0) == (a > 0)) || delta == 0) { | |||
2486 | /* (count - b) / a is integer */ | |||
2487 | return (delta % a == 0); | |||
2488 | } | |||
2489 | ||||
2490 | return false0; | |||
2491 | } | |||
2492 | } | |||
2493 | ||||
2494 | css_error match_detail(css_select_ctx *ctx, void *node, | |||
2495 | const css_selector_detail *detail, css_select_state *state, | |||
2496 | bool_Bool *match, css_pseudo_element *pseudo_element) | |||
2497 | { | |||
2498 | bool_Bool is_root = false0; | |||
2499 | css_error error = CSS_OK; | |||
2500 | css_node_flags flags = CSS_NODE_FLAGS_TAINT_PSEUDO_CLASS; | |||
2501 | ||||
2502 | switch (detail->type) { | |||
2503 | case CSS_SELECTOR_ELEMENT: | |||
2504 | if (detail->negate != 0) { | |||
2505 | /* Only need to test this inside not(), since | |||
2506 | * it will have been considered as a named node | |||
2507 | * otherwise. */ | |||
2508 | error = state->handler->node_has_name(state->pw, node, | |||
2509 | &detail->qname, match); | |||
2510 | } | |||
2511 | break; | |||
2512 | case CSS_SELECTOR_CLASS: | |||
2513 | error = state->handler->node_has_class(state->pw, node, | |||
2514 | detail->qname.name, match); | |||
2515 | break; | |||
2516 | case CSS_SELECTOR_ID: | |||
2517 | error = state->handler->node_has_id(state->pw, node, | |||
2518 | detail->qname.name, match); | |||
2519 | break; | |||
2520 | case CSS_SELECTOR_PSEUDO_CLASS: | |||
2521 | error = state->handler->node_is_root(state->pw, node, &is_root); | |||
2522 | if (error != CSS_OK) | |||
2523 | return error; | |||
2524 | ||||
2525 | if (is_root == false0 && | |||
2526 | detail->qname.name == ctx->str.first_child) { | |||
2527 | int32_t num_before = 0; | |||
2528 | ||||
2529 | error = state->handler->node_count_siblings(state->pw, | |||
2530 | node, false0, false0, &num_before); | |||
2531 | if (error == CSS_OK) | |||
2532 | *match = (num_before == 0); | |||
2533 | } else if (is_root == false0 && | |||
2534 | detail->qname.name == ctx->str.nth_child) { | |||
2535 | int32_t num_before = 0; | |||
2536 | ||||
2537 | error = state->handler->node_count_siblings(state->pw, | |||
2538 | node, false0, false0, &num_before); | |||
2539 | if (error == CSS_OK) { | |||
2540 | int32_t a = detail->value.nth.a; | |||
2541 | int32_t b = detail->value.nth.b; | |||
2542 | ||||
2543 | *match = match_nth(a, b, num_before + 1); | |||
2544 | } | |||
2545 | } else if (is_root == false0 && | |||
2546 | detail->qname.name == ctx->str.nth_last_child) { | |||
2547 | int32_t num_after = 0; | |||
2548 | ||||
2549 | error = state->handler->node_count_siblings(state->pw, | |||
2550 | node, false0, true1, &num_after); | |||
2551 | if (error == CSS_OK) { | |||
2552 | int32_t a = detail->value.nth.a; | |||
2553 | int32_t b = detail->value.nth.b; | |||
2554 | ||||
2555 | *match = match_nth(a, b, num_after + 1); | |||
2556 | } | |||
2557 | } else if (is_root == false0 && | |||
2558 | detail->qname.name == ctx->str.nth_of_type) { | |||
2559 | int32_t num_before = 0; | |||
2560 | ||||
2561 | error = state->handler->node_count_siblings(state->pw, | |||
2562 | node, true1, false0, &num_before); | |||
2563 | if (error == CSS_OK) { | |||
2564 | int32_t a = detail->value.nth.a; | |||
2565 | int32_t b = detail->value.nth.b; | |||
2566 | ||||
2567 | *match = match_nth(a, b, num_before + 1); | |||
2568 | } | |||
2569 | } else if (is_root == false0 && | |||
2570 | detail->qname.name == ctx->str.nth_last_of_type) { | |||
2571 | int32_t num_after = 0; | |||
2572 | ||||
2573 | error = state->handler->node_count_siblings(state->pw, | |||
2574 | node, true1, true1, &num_after); | |||
2575 | if (error == CSS_OK) { | |||
2576 | int32_t a = detail->value.nth.a; | |||
2577 | int32_t b = detail->value.nth.b; | |||
2578 | ||||
2579 | *match = match_nth(a, b, num_after + 1); | |||
2580 | } | |||
2581 | } else if (is_root == false0 && | |||
2582 | detail->qname.name == ctx->str.last_child) { | |||
2583 | int32_t num_after = 0; | |||
2584 | ||||
2585 | error = state->handler->node_count_siblings(state->pw, | |||
2586 | node, false0, true1, &num_after); | |||
2587 | if (error == CSS_OK) | |||
2588 | *match = (num_after == 0); | |||
2589 | } else if (is_root == false0 && | |||
2590 | detail->qname.name == ctx->str.first_of_type) { | |||
2591 | int32_t num_before = 0; | |||
2592 | ||||
2593 | error = state->handler->node_count_siblings(state->pw, | |||
2594 | node, true1, false0, &num_before); | |||
2595 | if (error == CSS_OK) | |||
2596 | *match = (num_before == 0); | |||
2597 | } else if (is_root == false0 && | |||
2598 | detail->qname.name == ctx->str.last_of_type) { | |||
2599 | int32_t num_after = 0; | |||
2600 | ||||
2601 | error = state->handler->node_count_siblings(state->pw, | |||
2602 | node, true1, true1, &num_after); | |||
2603 | if (error == CSS_OK) | |||
2604 | *match = (num_after == 0); | |||
2605 | } else if (is_root == false0 && | |||
2606 | detail->qname.name == ctx->str.only_child) { | |||
2607 | int32_t num_before = 0, num_after = 0; | |||
2608 | ||||
2609 | error = state->handler->node_count_siblings(state->pw, | |||
2610 | node, false0, false0, &num_before); | |||
2611 | if (error == CSS_OK) { | |||
2612 | error = state->handler->node_count_siblings( | |||
2613 | state->pw, node, false0, true1, | |||
2614 | &num_after); | |||
2615 | if (error == CSS_OK) | |||
2616 | *match = (num_before == 0) && | |||
2617 | (num_after == 0); | |||
2618 | } | |||
2619 | } else if (is_root == false0 && | |||
2620 | detail->qname.name == ctx->str.only_of_type) { | |||
2621 | int32_t num_before = 0, num_after = 0; | |||
2622 | ||||
2623 | error = state->handler->node_count_siblings(state->pw, | |||
2624 | node, true1, false0, &num_before); | |||
2625 | if (error == CSS_OK) { | |||
2626 | error = state->handler->node_count_siblings( | |||
2627 | state->pw, node, true1, true1, | |||
2628 | &num_after); | |||
2629 | if (error == CSS_OK) | |||
2630 | *match = (num_before == 0) && | |||
2631 | (num_after == 0); | |||
2632 | } | |||
2633 | } else if (detail->qname.name == ctx->str.root) { | |||
2634 | *match = is_root; | |||
2635 | } else if (detail->qname.name == ctx->str.empty) { | |||
2636 | error = state->handler->node_is_empty(state->pw, | |||
2637 | node, match); | |||
2638 | } else if (detail->qname.name == ctx->str.link) { | |||
2639 | error = state->handler->node_is_link(state->pw, | |||
2640 | node, match); | |||
2641 | flags = CSS_NODE_FLAGS_NONE; | |||
2642 | } else if (detail->qname.name == ctx->str.visited) { | |||
2643 | error = state->handler->node_is_visited(state->pw, | |||
2644 | node, match); | |||
2645 | flags = CSS_NODE_FLAGS_NONE; | |||
2646 | } else if (detail->qname.name == ctx->str.hover) { | |||
2647 | error = state->handler->node_is_hover(state->pw, | |||
2648 | node, match); | |||
2649 | flags = CSS_NODE_FLAGS_NONE; | |||
2650 | } else if (detail->qname.name == ctx->str.active) { | |||
2651 | error = state->handler->node_is_active(state->pw, | |||
2652 | node, match); | |||
2653 | flags = CSS_NODE_FLAGS_NONE; | |||
2654 | } else if (detail->qname.name == ctx->str.focus) { | |||
2655 | error = state->handler->node_is_focus(state->pw, | |||
2656 | node, match); | |||
2657 | flags = CSS_NODE_FLAGS_NONE; | |||
2658 | } else if (detail->qname.name == ctx->str.target) { | |||
2659 | error = state->handler->node_is_target(state->pw, | |||
2660 | node, match); | |||
2661 | } else if (detail->qname.name == ctx->str.lang) { | |||
2662 | error = state->handler->node_is_lang(state->pw, | |||
2663 | node, detail->value.string, match); | |||
2664 | } else if (detail->qname.name == ctx->str.enabled) { | |||
2665 | error = state->handler->node_is_enabled(state->pw, | |||
2666 | node, match); | |||
2667 | } else if (detail->qname.name == ctx->str.disabled) { | |||
2668 | error = state->handler->node_is_disabled(state->pw, | |||
2669 | node, match); | |||
2670 | } else if (detail->qname.name == ctx->str.checked) { | |||
2671 | error = state->handler->node_is_checked(state->pw, | |||
2672 | node, match); | |||
2673 | } else { | |||
2674 | *match = false0; | |||
2675 | } | |||
2676 | add_node_flags(node, state, flags); | |||
2677 | break; | |||
2678 | case CSS_SELECTOR_PSEUDO_ELEMENT: | |||
2679 | *match = true1; | |||
2680 | ||||
2681 | if (detail->qname.name == ctx->str.first_line) { | |||
2682 | *pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LINE; | |||
2683 | } else if (detail->qname.name == ctx->str.first_letter) { | |||
2684 | *pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LETTER; | |||
2685 | } else if (detail->qname.name == ctx->str.before) { | |||
2686 | *pseudo_element = CSS_PSEUDO_ELEMENT_BEFORE; | |||
2687 | } else if (detail->qname.name == ctx->str.after) { | |||
2688 | *pseudo_element = CSS_PSEUDO_ELEMENT_AFTER; | |||
2689 | } else | |||
2690 | *match = false0; | |||
2691 | break; | |||
2692 | case CSS_SELECTOR_ATTRIBUTE: | |||
2693 | error = state->handler->node_has_attribute(state->pw, node, | |||
2694 | &detail->qname, match); | |||
2695 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2696 | break; | |||
2697 | case CSS_SELECTOR_ATTRIBUTE_EQUAL: | |||
2698 | error = state->handler->node_has_attribute_equal(state->pw, | |||
2699 | node, &detail->qname, detail->value.string, | |||
2700 | match); | |||
2701 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2702 | break; | |||
2703 | case CSS_SELECTOR_ATTRIBUTE_DASHMATCH: | |||
2704 | error = state->handler->node_has_attribute_dashmatch(state->pw, | |||
2705 | node, &detail->qname, detail->value.string, | |||
2706 | match); | |||
2707 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2708 | break; | |||
2709 | case CSS_SELECTOR_ATTRIBUTE_INCLUDES: | |||
2710 | error = state->handler->node_has_attribute_includes(state->pw, | |||
2711 | node, &detail->qname, detail->value.string, | |||
2712 | match); | |||
2713 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2714 | break; | |||
2715 | case CSS_SELECTOR_ATTRIBUTE_PREFIX: | |||
2716 | error = state->handler->node_has_attribute_prefix(state->pw, | |||
2717 | node, &detail->qname, detail->value.string, | |||
2718 | match); | |||
2719 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2720 | break; | |||
2721 | case CSS_SELECTOR_ATTRIBUTE_SUFFIX: | |||
2722 | error = state->handler->node_has_attribute_suffix(state->pw, | |||
2723 | node, &detail->qname, detail->value.string, | |||
2724 | match); | |||
2725 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2726 | break; | |||
2727 | case CSS_SELECTOR_ATTRIBUTE_SUBSTRING: | |||
2728 | error = state->handler->node_has_attribute_substring(state->pw, | |||
2729 | node, &detail->qname, detail->value.string, | |||
2730 | match); | |||
2731 | add_node_flags(node, state, CSS_NODE_FLAGS_TAINT_ATTRIBUTE); | |||
2732 | break; | |||
2733 | } | |||
2734 | ||||
2735 | /* Invert match, if the detail requests it */ | |||
2736 | if (error == CSS_OK && detail->negate != 0) | |||
2737 | *match = !*match; | |||
2738 | ||||
2739 | return error; | |||
2740 | } | |||
2741 | ||||
2742 | css_error cascade_style(const css_style *style, css_select_state *state) | |||
2743 | { | |||
2744 | css_style s = *style; | |||
2745 | ||||
2746 | while (s.used > 0) { | |||
2747 | opcode_t op; | |||
2748 | css_error error; | |||
2749 | css_code_t opv = *s.bytecode; | |||
2750 | ||||
2751 | advance_bytecode(&s, sizeof(opv)); | |||
2752 | ||||
2753 | op = getOpcode(opv); | |||
2754 | ||||
2755 | error = prop_dispatch[op].cascade(opv, &s, state); | |||
2756 | if (error != CSS_OK) | |||
2757 | return error; | |||
2758 | } | |||
2759 | ||||
2760 | return CSS_OK; | |||
2761 | } | |||
2762 | ||||
2763 | bool_Bool css__outranks_existing(uint16_t op, bool_Bool important, css_select_state *state, | |||
2764 | enum flag_value explicit_default) | |||
2765 | { | |||
2766 | prop_state *existing = &state->props[op][state->current_pseudo]; | |||
2767 | bool_Bool outranks = false0; | |||
2768 | ||||
2769 | /* Sorting on origin & importance gives the following: | |||
2770 | * | |||
2771 | * | UA, - | UA, i | USER, - | USER, i | AUTHOR, - | AUTHOR, i | |||
2772 | * |---------------------------------------------------------- | |||
2773 | * UA , - | S S Y Y Y Y | |||
2774 | * UA , i | S S Y Y Y Y | |||
2775 | * USER , - | - - S Y Y Y | |||
2776 | * USER , i | - - - S - - | |||
2777 | * AUTHOR, - | - - - Y S Y | |||
2778 | * AUTHOR, i | - - - Y - S | |||
2779 | * | |||
2780 | * Where the columns represent the origin/importance of the property | |||
2781 | * being considered and the rows represent the origin/importance of | |||
2782 | * the existing property. | |||
2783 | * | |||
2784 | * - means that the existing property must be preserved | |||
2785 | * Y means that the new property must be applied | |||
2786 | * S means that the specificities of the rules must be considered. | |||
2787 | * | |||
2788 | * If specificities are considered, the highest specificity wins. | |||
2789 | * If specificities are equal, then the rule defined last wins. | |||
2790 | * | |||
2791 | * We have no need to explicitly consider the ordering of rules if | |||
2792 | * the specificities are the same because: | |||
2793 | * | |||
2794 | * a) We process stylesheets in order | |||
2795 | * b) The selector hash chains within a sheet are ordered such that | |||
2796 | * more specific rules come after less specific ones and, when | |||
2797 | * specificities are identical, rules defined later occur after | |||
2798 | * those defined earlier. | |||
2799 | * | |||
2800 | * Therefore, where we consider specificity, below, the property | |||
2801 | * currently being considered will always be applied if its specificity | |||
2802 | * is greater than or equal to that of the existing property. | |||
2803 | */ | |||
2804 | ||||
2805 | if (existing->set == 0) { | |||
2806 | /* Property hasn't been set before, new one wins */ | |||
2807 | outranks = true1; | |||
2808 | } else { | |||
2809 | assert(CSS_ORIGIN_UA < CSS_ORIGIN_USER)((CSS_ORIGIN_UA < CSS_ORIGIN_USER) ? (void) (0) : __assert_fail ("CSS_ORIGIN_UA < CSS_ORIGIN_USER", "src/select/select.c" , 2809, __extension__ __PRETTY_FUNCTION__)); | |||
2810 | assert(CSS_ORIGIN_USER < CSS_ORIGIN_AUTHOR)((CSS_ORIGIN_USER < CSS_ORIGIN_AUTHOR) ? (void) (0) : __assert_fail ("CSS_ORIGIN_USER < CSS_ORIGIN_AUTHOR", "src/select/select.c" , 2810, __extension__ __PRETTY_FUNCTION__)); | |||
2811 | ||||
2812 | if (existing->origin < state->current_origin) { | |||
2813 | /* New origin has more weight than existing one. | |||
2814 | * Thus, new property wins, except when the existing | |||
2815 | * one is USER, i. */ | |||
2816 | if (existing->important == 0 || | |||
2817 | existing->origin != CSS_ORIGIN_USER) { | |||
2818 | outranks = true1; | |||
2819 | } | |||
2820 | } else if (existing->origin == state->current_origin) { | |||
2821 | /* Origins are identical, consider importance, except | |||
2822 | * for UA stylesheets, when specificity is always | |||
2823 | * considered (as importance is meaningless) */ | |||
2824 | if (existing->origin == CSS_ORIGIN_UA) { | |||
2825 | if (state->current_specificity >= | |||
2826 | existing->specificity) { | |||
2827 | outranks = true1; | |||
2828 | } | |||
2829 | } else if (existing->important == 0 && important) { | |||
2830 | /* New is more important than old. */ | |||
2831 | outranks = true1; | |||
2832 | } else if (existing->important && important == false0) { | |||
2833 | /* Old is more important than new */ | |||
2834 | } else { | |||
2835 | /* Same importance, consider specificity */ | |||
2836 | if (state->current_specificity >= | |||
2837 | existing->specificity) { | |||
2838 | outranks = true1; | |||
2839 | } | |||
2840 | } | |||
2841 | } else { | |||
2842 | /* Existing origin has more weight than new one. | |||
2843 | * Thus, existing property wins, except when the new | |||
2844 | * one is USER, i. */ | |||
2845 | if (state->current_origin == CSS_ORIGIN_USER && | |||
2846 | important) { | |||
2847 | outranks = true1; | |||
2848 | } | |||
2849 | } | |||
2850 | } | |||
2851 | ||||
2852 | if (outranks) { | |||
2853 | /* The new property is about to replace the old one. | |||
2854 | * Update our state to reflect this. */ | |||
2855 | existing->set = 1; | |||
2856 | existing->specificity = state->current_specificity; | |||
2857 | existing->origin = state->current_origin; | |||
2858 | existing->important = important; | |||
2859 | existing->explicit_default = explicit_default; | |||
2860 | } | |||
2861 | ||||
2862 | return outranks; | |||
2863 | } | |||
2864 | ||||
2865 | /****************************************************************************** | |||
2866 | * Debug helpers * | |||
2867 | ******************************************************************************/ | |||
2868 | #ifdef DEBUG_CHAIN_MATCHING | |||
2869 | void dump_chain(const css_selector *selector) | |||
2870 | { | |||
2871 | const css_selector_detail *detail = &selector->data; | |||
2872 | ||||
2873 | if (selector->data.comb != CSS_COMBINATOR_NONE) | |||
2874 | dump_chain(selector->combinator); | |||
2875 | ||||
2876 | if (selector->data.comb == CSS_COMBINATOR_ANCESTOR) | |||
2877 | fprintf(stderrstderr, " "); | |||
2878 | else if (selector->data.comb == CSS_COMBINATOR_SIBLING) | |||
2879 | fprintf(stderrstderr, " + "); | |||
2880 | else if (selector->data.comb == CSS_COMBINATOR_PARENT) | |||
2881 | fprintf(stderrstderr, " > "); | |||
2882 | ||||
2883 | do { | |||
2884 | switch (detail->type) { | |||
2885 | case CSS_SELECTOR_ELEMENT: | |||
2886 | if (lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2886, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}) == 1 && | |||
2887 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2887, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )[0] == '*' && | |||
2888 | detail->next == 1) { | |||
2889 | break; | |||
2890 | } | |||
2891 | fprintf(stderrstderr, "%.*s", | |||
2892 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2892, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2893 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2893, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )); | |||
2894 | break; | |||
2895 | case CSS_SELECTOR_CLASS: | |||
2896 | fprintf(stderrstderr, ".%.*s", | |||
2897 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2897, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2898 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2898, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )); | |||
2899 | break; | |||
2900 | case CSS_SELECTOR_ID: | |||
2901 | fprintf(stderrstderr, "#%.*s", | |||
2902 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2902, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2903 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2903, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )); | |||
2904 | break; | |||
2905 | case CSS_SELECTOR_PSEUDO_CLASS: | |||
2906 | case CSS_SELECTOR_PSEUDO_ELEMENT: | |||
2907 | fprintf(stderrstderr, ":%.*s", | |||
2908 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2908, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2909 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2909, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )); | |||
2910 | ||||
2911 | if (detail->value != NULL((void*)0)) { | |||
2912 | fprintf(stderrstderr, "(%.*s)", | |||
2913 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2913, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2914 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2914, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2915 | } | |||
2916 | break; | |||
2917 | case CSS_SELECTOR_ATTRIBUTE: | |||
2918 | fprintf(stderrstderr, "[%.*s]", | |||
2919 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2919, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2920 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2920, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} )); | |||
2921 | break; | |||
2922 | case CSS_SELECTOR_ATTRIBUTE_EQUAL: | |||
2923 | fprintf(stderrstderr, "[%.*s=\"%.*s\"]", | |||
2924 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2924, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2925 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2925, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2926 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2926, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2927 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2927, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2928 | break; | |||
2929 | case CSS_SELECTOR_ATTRIBUTE_DASHMATCH: | |||
2930 | fprintf(stderrstderr, "[%.*s|=\"%.*s\"]", | |||
2931 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2931, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2932 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2932, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2933 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2933, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2934 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2934, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2935 | break; | |||
2936 | case CSS_SELECTOR_ATTRIBUTE_INCLUDES: | |||
2937 | fprintf(stderrstderr, "[%.*s~=\"%.*s\"]", | |||
2938 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2938, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2939 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2939, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2940 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2940, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2941 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2941, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2942 | break; | |||
2943 | case CSS_SELECTOR_ATTRIBUTE_PREFIX: | |||
2944 | fprintf(stderrstderr, "[%.*s^=\"%.*s\"]", | |||
2945 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2945, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2946 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2946, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2947 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2947, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2948 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2948, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2949 | break; | |||
2950 | case CSS_SELECTOR_ATTRIBUTE_SUFFIX: | |||
2951 | fprintf(stderrstderr, "[%.*s$=\"%.*s\"]", | |||
2952 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2952, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2953 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2953, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2954 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2954, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2955 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2955, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2956 | break; | |||
2957 | case CSS_SELECTOR_ATTRIBUTE_SUBSTRING: | |||
2958 | fprintf(stderrstderr, "[%.*s*=\"%.*s\"]", | |||
2959 | (int) lwc_string_length(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2959, __extension__ __PRETTY_FUNCTION__)); (detail->name)->len;}), | |||
2960 | lwc_string_data(detail->name)({((detail->name != ((void*)0)) ? (void) (0) : __assert_fail ("detail->name != NULL", "src/select/select.c", 2960, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->name)+1);} ), | |||
2961 | (int) lwc_string_length(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2961, __extension__ __PRETTY_FUNCTION__)); (detail->value)->len;}), | |||
2962 | lwc_string_data(detail->value)({((detail->value != ((void*)0)) ? (void) (0) : __assert_fail ("detail->value != NULL", "src/select/select.c", 2962, __extension__ __PRETTY_FUNCTION__)); (const char *)((detail->value)+1); })); | |||
2963 | break; | |||
2964 | } | |||
2965 | ||||
2966 | if (detail->next) | |||
2967 | detail++; | |||
2968 | else | |||
2969 | detail = NULL((void*)0); | |||
2970 | } while (detail); | |||
2971 | } | |||
2972 | #endif | |||
2973 |