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