/* * Copyright (C) 2021-2024 Alexander Borisov * * Author: Alexander Borisov */ #ifndef LEXBOR_SELECTORS_H #define LEXBOR_SELECTORS_H #ifdef __cplusplus extern "C" { #endif #include "lexbor/selectors/base.h" #include "lexbor/dom/dom.h" #include "lexbor/css/selectors/selectors.h" #include "lexbor/core/array_obj.h" typedef enum { LXB_SELECTORS_OPT_DEFAULT = 0x00, /* * Includes the passed (root) node in the search. * * By default, the root node does not participate in selector searches, * only its children. * * This behavior is logical, if you have found a node and then you want to * search for other nodes in it, you don't need to check it again. * * But there are cases when it is necessary for root node to participate * in the search. That's what this option is for. */ LXB_SELECTORS_OPT_MATCH_ROOT = 1 << 1, /* * Stop searching after the first match with any of the selectors * in the list. * * By default, the callback will be triggered for each selector list. * That is, if your node matches different selector lists, it will be * returned multiple times in the callback. * * For example: * HTML:
test
* Selectors: div, div[id="ok"], div:has(:not(a)) * * The default behavior will cause three callbacks with the same node (div). * Because it will be found by every selector in the list. * * This option allows you to end the element check after the first match on * any of the selectors. That is, the callback will be called only once * for example above. This way we get rid of duplicates in the search. */ LXB_SELECTORS_OPT_MATCH_FIRST = 1 << 2 } lxb_selectors_opt_t; typedef struct lxb_selectors lxb_selectors_t; typedef struct lxb_selectors_entry lxb_selectors_entry_t; typedef struct lxb_selectors_nested lxb_selectors_nested_t; typedef lxb_status_t (*lxb_selectors_cb_f)(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx); typedef lxb_selectors_entry_t * (*lxb_selectors_state_cb_f)(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry); struct lxb_selectors_entry { uintptr_t id; lxb_css_selector_combinator_t combinator; const lxb_css_selector_t *selector; lxb_dom_node_t *node; lxb_selectors_entry_t *next; lxb_selectors_entry_t *prev; lxb_selectors_entry_t *following; lxb_selectors_nested_t *nested; }; struct lxb_selectors_nested { lxb_selectors_entry_t *entry; lxb_selectors_state_cb_f return_state; lxb_selectors_cb_f cb; void *ctx; lxb_dom_node_t *root; lxb_selectors_entry_t *last; lxb_selectors_nested_t *parent; size_t index; bool found; }; struct lxb_selectors { lxb_selectors_state_cb_f state; lexbor_dobject_t *objs; lexbor_dobject_t *nested; lxb_selectors_nested_t *current; lxb_selectors_entry_t *first; lxb_selectors_opt_t options; lxb_status_t status; }; /* * Create lxb_selectors_t object. * * @return lxb_selectors_t * if successful, otherwise NULL. */ LXB_API lxb_selectors_t * lxb_selectors_create(void); /* * Initialization of lxb_selectors_t object. * * Caches are initialized in this function. * * @param[in] lxb_selectors_t * * * @return LXB_STATUS_OK if successful, otherwise an error status value. */ LXB_API lxb_status_t lxb_selectors_init(lxb_selectors_t *selectors); /* * Clears the object. Returns object to states as after initialization. * * After each call to lxb_selectors_find() and lxb_selectors_find_for_node(), * the lxb_selectors_t object is cleared. That is, you don't need to call this * function every time after searching by a selector. * * @param[in] lxb_url_parser_t * */ LXB_API void lxb_selectors_clean(lxb_selectors_t *selectors); /* * Destroy lxb_selectors_t object. * * Destroying all caches. * * @param[in] lxb_selectors_t *. Can be NULL. * @param[in] if false: only destroys internal caches. * if true: destroys the lxb_selectors_t object and all internal caches. * * @return lxb_selectors_t * if self_destroy = false, otherwise NULL. */ LXB_API lxb_selectors_t * lxb_selectors_destroy(lxb_selectors_t *selectors, bool self_destroy); /* * Search for nodes by selector list. * * Default Behavior: * 1. The root node does not participate in the search, only its child nodes. * 2. If a node matches multiple selector lists, a callback with that node * will be called on each list. * For example: * HTML:
* Selectors: div, div[id="ok"], div:has(:not(a)) * For each selector list, a callback with a "div" node will be called. * * To change the search behavior, see lxb_selectors_opt_set(). * * @param[in] lxb_selectors_t *. * @param[in] lxb_dom_node_t *. The node from which the search will begin. * @param[in] const lxb_css_selector_list_t *. Selectors List. * @param[in] lxb_selectors_cb_f. Callback for a found node. * @param[in] void *. Context for the callback. * if true: destroys the lxb_selectors_t object and all internal caches. * * @return LXB_STATUS_OK if successful, otherwise an error status value. */ LXB_API lxb_status_t lxb_selectors_find(lxb_selectors_t *selectors, lxb_dom_node_t *root, const lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx); /* * Match a node to a Selectors List. * * In other words, the function checks which selector lists will find the * specified node. * * Default Behavior: * 1. If a node matches multiple selector lists, a callback with that node * will be called on each list. * For example: * HTML:
* Node: div * Selectors: div, div[id="ok"], div:has(:not(a)) * For each selector list, a callback with a "div" node will be called. * * To change the search behavior, see lxb_selectors_opt_set(). * * @param[in] lxb_selectors_t *. * @param[in] lxb_dom_node_t *. The node from which the search will begin. * @param[in] const lxb_css_selector_list_t *. Selectors List. * @param[in] lxb_selectors_cb_f. Callback for a found node. * @param[in] void *. Context for the callback. * if true: destroys the lxb_selectors_t object and all internal caches. * * @return LXB_STATUS_OK if successful, otherwise an error status value. */ LXB_API lxb_status_t lxb_selectors_match_node(lxb_selectors_t *selectors, lxb_dom_node_t *node, const lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx); /* * Deprecated! * This function does exactly the same thing as lxb_selectors_match_node(). */ LXB_API LXB_DEPRECATED(lxb_status_t lxb_selectors_find_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *root, lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx)); /* * Inline functions. */ /* * The function sets the node search options. * * For more information, see lxb_selectors_opt_t. * * @param[in] lxb_selectors_t *. * @param[in] lxb_selectors_opt_t. */ lxb_inline void lxb_selectors_opt_set(lxb_selectors_t *selectors, lxb_selectors_opt_t opt) { selectors->options = opt; } /* * Get the current selector. * * Function to get the selector by which the node was found. * Use context (void *ctx) to pass the lxb_selectors_t object to the callback. * * @param[in] const lxb_selectors_t *. * * @return const lxb_css_selector_list_t *. */ lxb_inline const lxb_css_selector_list_t * lxb_selectors_selector(const lxb_selectors_t *selectors) { return selectors->current->entry->selector->list; } /* * Not inline for inline. */ /* * Same as lxb_selectors_opt_set() function, but not inline. */ LXB_API void lxb_selectors_opt_set_noi(lxb_selectors_t *selectors, lxb_selectors_opt_t opt); /* * Same as lxb_selectors_selector() function, but not inline. */ LXB_API const lxb_css_selector_list_t * lxb_selectors_selector_noi(const lxb_selectors_t *selectors); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LEXBOR_SELECTORS_H */