1 /*
2 * Copyright (C) 2021-2024 Alexander Borisov
3 *
4 * Author: Alexander Borisov <borisov@lexbor.com>
5 */
6
7
8 #ifndef LEXBOR_SELECTORS_H
9 #define LEXBOR_SELECTORS_H
10
11 #ifdef __cplusplus
12 extern "C" {
13 #endif
14
15 #include "lexbor/selectors/base.h"
16 #include "lexbor/dom/dom.h"
17 #include "lexbor/css/selectors/selectors.h"
18 #include "lexbor/core/array_obj.h"
19
20
21 typedef enum {
22 LXB_SELECTORS_OPT_DEFAULT = 0x00,
23
24 /*
25 * Includes the passed (root) node in the search.
26 *
27 * By default, the root node does not participate in selector searches,
28 * only its children.
29 *
30 * This behavior is logical, if you have found a node and then you want to
31 * search for other nodes in it, you don't need to check it again.
32 *
33 * But there are cases when it is necessary for root node to participate
34 * in the search. That's what this option is for.
35 */
36 LXB_SELECTORS_OPT_MATCH_ROOT = 1 << 1,
37
38 /*
39 * Stop searching after the first match with any of the selectors
40 * in the list.
41 *
42 * By default, the callback will be triggered for each selector list.
43 * That is, if your node matches different selector lists, it will be
44 * returned multiple times in the callback.
45 *
46 * For example:
47 * HTML: <div id="ok"><span>test</span></div>
48 * Selectors: div, div[id="ok"], div:has(:not(a))
49 *
50 * The default behavior will cause three callbacks with the same node (div).
51 * Because it will be found by every selector in the list.
52 *
53 * This option allows you to end the element check after the first match on
54 * any of the selectors. That is, the callback will be called only once
55 * for example above. This way we get rid of duplicates in the search.
56 */
57 LXB_SELECTORS_OPT_MATCH_FIRST = 1 << 2
58 }
59 lxb_selectors_opt_t;
60
61 typedef struct lxb_selectors lxb_selectors_t;
62 typedef struct lxb_selectors_entry lxb_selectors_entry_t;
63 typedef struct lxb_selectors_nested lxb_selectors_nested_t;
64
65 typedef lxb_status_t
66 (*lxb_selectors_cb_f)(lxb_dom_node_t *node,
67 lxb_css_selector_specificity_t spec, void *ctx);
68
69 typedef lxb_selectors_entry_t *
70 (*lxb_selectors_state_cb_f)(lxb_selectors_t *selectors,
71 lxb_selectors_entry_t *entry);
72
73 struct lxb_selectors_entry {
74 uintptr_t id;
75 lxb_css_selector_combinator_t combinator;
76 const lxb_css_selector_t *selector;
77 lxb_dom_node_t *node;
78 lxb_selectors_entry_t *next;
79 lxb_selectors_entry_t *prev;
80 lxb_selectors_entry_t *following;
81 lxb_selectors_nested_t *nested;
82 };
83
84 struct lxb_selectors_nested {
85 lxb_selectors_entry_t *entry;
86 lxb_selectors_state_cb_f return_state;
87
88 lxb_selectors_cb_f cb;
89 void *ctx;
90
91 lxb_dom_node_t *root;
92 lxb_selectors_entry_t *last;
93 lxb_selectors_nested_t *parent;
94
95 size_t index;
96 bool found;
97 };
98
99 struct lxb_selectors {
100 lxb_selectors_state_cb_f state;
101 lexbor_dobject_t *objs;
102 lexbor_dobject_t *nested;
103
104 lxb_selectors_nested_t *current;
105 lxb_selectors_entry_t *first;
106
107 lxb_selectors_opt_t options;
108 lxb_status_t status;
109 };
110
111
112 /*
113 * Create lxb_selectors_t object.
114 *
115 * @return lxb_selectors_t * if successful, otherwise NULL.
116 */
117 LXB_API lxb_selectors_t *
118 lxb_selectors_create(void);
119
120 /*
121 * Initialization of lxb_selectors_t object.
122 *
123 * Caches are initialized in this function.
124 *
125 * @param[in] lxb_selectors_t *
126 *
127 * @return LXB_STATUS_OK if successful, otherwise an error status value.
128 */
129 LXB_API lxb_status_t
130 lxb_selectors_init(lxb_selectors_t *selectors);
131
132 /*
133 * Clears the object. Returns object to states as after initialization.
134 *
135 * After each call to lxb_selectors_find() and lxb_selectors_find_for_node(),
136 * the lxb_selectors_t object is cleared. That is, you don't need to call this
137 * function every time after searching by a selector.
138 *
139 * @param[in] lxb_url_parser_t *
140 */
141 LXB_API void
142 lxb_selectors_clean(lxb_selectors_t *selectors);
143
144 /*
145 * Destroy lxb_selectors_t object.
146 *
147 * Destroying all caches.
148 *
149 * @param[in] lxb_selectors_t *. Can be NULL.
150 * @param[in] if false: only destroys internal caches.
151 * if true: destroys the lxb_selectors_t object and all internal caches.
152 *
153 * @return lxb_selectors_t * if self_destroy = false, otherwise NULL.
154 */
155 LXB_API lxb_selectors_t *
156 lxb_selectors_destroy(lxb_selectors_t *selectors, bool self_destroy);
157
158 /*
159 * Search for nodes by selector list.
160 *
161 * Default Behavior:
162 * 1. The root node does not participate in the search, only its child nodes.
163 * 2. If a node matches multiple selector lists, a callback with that node
164 * will be called on each list.
165 * For example:
166 * HTML: <div id="ok"><span></span></div>
167 * Selectors: div, div[id="ok"], div:has(:not(a))
168 * For each selector list, a callback with a "div" node will be called.
169 *
170 * To change the search behavior, see lxb_selectors_opt_set().
171 *
172 * @param[in] lxb_selectors_t *.
173 * @param[in] lxb_dom_node_t *. The node from which the search will begin.
174 * @param[in] const lxb_css_selector_list_t *. Selectors List.
175 * @param[in] lxb_selectors_cb_f. Callback for a found node.
176 * @param[in] void *. Context for the callback.
177 * if true: destroys the lxb_selectors_t object and all internal caches.
178 *
179 * @return LXB_STATUS_OK if successful, otherwise an error status value.
180 */
181 LXB_API lxb_status_t
182 lxb_selectors_find(lxb_selectors_t *selectors, lxb_dom_node_t *root,
183 const lxb_css_selector_list_t *list,
184 lxb_selectors_cb_f cb, void *ctx);
185
186 /*
187 * Match a node to a Selectors List.
188 *
189 * In other words, the function checks which selector lists will find the
190 * specified node.
191 *
192 * Default Behavior:
193 * 1. If a node matches multiple selector lists, a callback with that node
194 * will be called on each list.
195 * For example:
196 * HTML: <div id="ok"><span></span></div>
197 * Node: div
198 * Selectors: div, div[id="ok"], div:has(:not(a))
199 * For each selector list, a callback with a "div" node will be called.
200 *
201 * To change the search behavior, see lxb_selectors_opt_set().
202 *
203 * @param[in] lxb_selectors_t *.
204 * @param[in] lxb_dom_node_t *. The node from which the search will begin.
205 * @param[in] const lxb_css_selector_list_t *. Selectors List.
206 * @param[in] lxb_selectors_cb_f. Callback for a found node.
207 * @param[in] void *. Context for the callback.
208 * if true: destroys the lxb_selectors_t object and all internal caches.
209 *
210 * @return LXB_STATUS_OK if successful, otherwise an error status value.
211 */
212 LXB_API lxb_status_t
213 lxb_selectors_match_node(lxb_selectors_t *selectors, lxb_dom_node_t *node,
214 const lxb_css_selector_list_t *list,
215 lxb_selectors_cb_f cb, void *ctx);
216
217 /*
218 * Deprecated!
219 * This function does exactly the same thing as lxb_selectors_match_node().
220 */
221 LXB_API LXB_DEPRECATED(lxb_status_t
222 lxb_selectors_find_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *root,
223 lxb_css_selector_list_t *list,
224 lxb_selectors_cb_f cb, void *ctx));
225
226 /*
227 * Inline functions.
228 */
229
230 /*
231 * The function sets the node search options.
232 *
233 * For more information, see lxb_selectors_opt_t.
234 *
235 * @param[in] lxb_selectors_t *.
236 * @param[in] lxb_selectors_opt_t.
237 */
238 lxb_inline void
lxb_selectors_opt_set(lxb_selectors_t * selectors,lxb_selectors_opt_t opt)239 lxb_selectors_opt_set(lxb_selectors_t *selectors, lxb_selectors_opt_t opt)
240 {
241 selectors->options = opt;
242 }
243
244 /*
245 * Get the current selector.
246 *
247 * Function to get the selector by which the node was found.
248 * Use context (void *ctx) to pass the lxb_selectors_t object to the callback.
249 *
250 * @param[in] const lxb_selectors_t *.
251 *
252 * @return const lxb_css_selector_list_t *.
253 */
254 lxb_inline const lxb_css_selector_list_t *
lxb_selectors_selector(const lxb_selectors_t * selectors)255 lxb_selectors_selector(const lxb_selectors_t *selectors)
256 {
257 return selectors->current->entry->selector->list;
258 }
259
260 /*
261 * Not inline for inline.
262 */
263
264 /*
265 * Same as lxb_selectors_opt_set() function, but not inline.
266 */
267 LXB_API void
268 lxb_selectors_opt_set_noi(lxb_selectors_t *selectors, lxb_selectors_opt_t opt);
269
270 /*
271 * Same as lxb_selectors_selector() function, but not inline.
272 */
273 LXB_API const lxb_css_selector_list_t *
274 lxb_selectors_selector_noi(const lxb_selectors_t *selectors);
275
276
277 #ifdef __cplusplus
278 } /* extern "C" */
279 #endif
280
281 #endif /* LEXBOR_SELECTORS_H */
282