php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
css_selectors.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Niels Dossche <nielsdos@php.net> |
14 +----------------------------------------------------------------------+
15*/
16
17#ifdef HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include "php.h"
22#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
23#include "../php_dom.h"
24
25#include "lexbor/css/parser.h"
27
28// TODO: optimization idea: cache the parsed selectors in an LRU fashion?
29
30typedef struct {
31 HashTable *list;
32 dom_object *intern;
33} dom_query_selector_all_ctx;
34
35typedef struct {
36 const xmlNode *reference;
37 bool result;
38} dom_query_selector_matches_ctx;
39
40static lxb_selectors_opt_t dom_quirks_opt(lxb_selectors_opt_t options, const dom_object *intern)
41{
42 if (intern->document != NULL && intern->document->quirks_mode) {
44 }
45 return options;
46}
47
48lxb_status_t dom_query_selector_find_single_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
49{
50 xmlNodePtr *result = (xmlNodePtr *) ctx;
51 *result = (xmlNodePtr) node;
52 return LXB_STATUS_STOP;
53}
54
55lxb_status_t dom_query_selector_find_array_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
56{
57 dom_query_selector_all_ctx *qsa_ctx = (dom_query_selector_all_ctx *) ctx;
59 php_dom_create_object((xmlNodePtr) node, &object, qsa_ctx->intern);
60 zend_hash_next_index_insert_new(qsa_ctx->list, &object);
61 return LXB_STATUS_OK;
62}
63
64lxb_status_t dom_query_selector_find_matches_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
65{
66 dom_query_selector_matches_ctx *matches_ctx = (dom_query_selector_matches_ctx *) ctx;
67 if (node == matches_ctx->reference) {
68 matches_ctx->result = true;
69 return LXB_STATUS_STOP;
70 }
71 return LXB_STATUS_OK;
72}
73
74static lxb_css_selector_list_t *dom_parse_selector(
75 lxb_css_parser_t *parser,
76 lxb_selectors_t *selectors,
77 const zend_string *selectors_str,
79 const dom_object *intern
80)
81{
83
84 memset(parser, 0, sizeof(lxb_css_parser_t));
87
88 memset(selectors, 0, sizeof(lxb_selectors_t));
89 status = lxb_selectors_init(selectors);
91 lxb_selectors_opt_set(selectors, dom_quirks_opt(options, intern));
92
93 lxb_css_selector_list_t *list = lxb_css_selectors_parse(parser, (const lxb_char_t *) ZSTR_VAL(selectors_str), ZSTR_LEN(selectors_str));
94 if (UNEXPECTED(list == NULL)) {
95 size_t nr_of_messages = lexbor_array_obj_length(&parser->log->messages);
96 if (nr_of_messages > 0) {
98 char *error;
99 zend_spprintf(&error, 0, "Invalid selector (%.*s)", (int) msg->text.length, msg->text.data);
101 efree(error);
102 } else {
103 php_dom_throw_error_with_message(SYNTAX_ERR, "Invalid selector", true);
104 }
105 }
106
107 return list;
108}
109
110static lxb_status_t dom_check_css_execution_status(lxb_status_t status)
111{
113 zend_argument_value_error(1, "contains an unsupported selector");
114 return status;
115 }
116 return LXB_STATUS_OK;
117}
118
119static void dom_selector_cleanup(lxb_css_parser_t *parser, lxb_selectors_t *selectors, lxb_css_selector_list_t *list)
120{
122 lxb_selectors_destroy(selectors);
123 (void) lxb_css_parser_destroy(parser, false);
124}
125
126static lxb_status_t dom_query_selector_common(
127 const xmlNode *root,
128 const dom_object *intern,
129 const zend_string *selectors_str,
131 void *ctx,
133)
134{
136
137 lxb_css_parser_t parser;
138 lxb_selectors_t selectors;
139
140 lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, options, intern);
141 if (UNEXPECTED(list == NULL)) {
143 } else {
144 status = lxb_selectors_find(&selectors, root, list, cb, ctx);
145 status = dom_check_css_execution_status(status);
146 }
147
148 dom_selector_cleanup(&parser, &selectors, list);
149
150 return status;
151}
152
153static lxb_status_t dom_query_matches(
154 const xmlNode *root,
155 const dom_object *intern,
156 const zend_string *selectors_str,
157 void *ctx
158)
159{
161
162 lxb_css_parser_t parser;
163 lxb_selectors_t selectors;
164
165 lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern);
166 if (UNEXPECTED(list == NULL)) {
168 } else {
169 status = lxb_selectors_match_node(&selectors, root, list, dom_query_selector_find_matches_callback, ctx);
170 status = dom_check_css_execution_status(status);
171 }
172
173 dom_selector_cleanup(&parser, &selectors, list);
174
175 return status;
176}
177
178static const xmlNode *dom_query_closest(
179 const xmlNode *root,
180 const dom_object *intern,
181 const zend_string *selectors_str
182)
183{
184 const xmlNode *ret = NULL;
185
186 lxb_css_parser_t parser;
187 lxb_selectors_t selectors;
188
189 lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern);
190 if (EXPECTED(list != NULL)) {
191 const xmlNode *current = root;
192 while (current != NULL) {
193 dom_query_selector_matches_ctx ctx = { current, false };
194 lxb_status_t status = lxb_selectors_match_node(&selectors, current, list, dom_query_selector_find_matches_callback, &ctx);
195 status = dom_check_css_execution_status(status);
197 break;
198 }
199 if (ctx.result) {
200 ret = current;
201 break;
202 }
203 current = current->parent;
204 }
205 }
206
207 dom_selector_cleanup(&parser, &selectors, list);
208
209 return ret;
210}
211
212/* https://dom.spec.whatwg.org/#dom-parentnode-queryselector */
213void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
214{
215 xmlNodePtr result = NULL;
216
217 if (dom_query_selector_common(
218 thisp,
219 intern,
220 selectors_str,
221 dom_query_selector_find_single_callback,
222 &result,
224 ) != LXB_STATUS_OK || result == NULL) {
225 RETURN_NULL();
226 } else {
227 DOM_RET_OBJ(result, intern);
228 }
229}
230
231/* https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall */
232void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
233{
234 HashTable *list = zend_new_array(0);
235 dom_query_selector_all_ctx ctx = { list, intern };
236
237 if (dom_query_selector_common(
238 thisp,
239 intern,
240 selectors_str,
241 dom_query_selector_find_array_callback,
242 &ctx,
244 ) != LXB_STATUS_OK) {
245 zend_array_destroy(list);
247 } else {
250 dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr;
251 ZVAL_ARR(&mapptr->baseobj_zv, list);
252 mapptr->nodetype = DOM_NODESET;
253 }
254}
255
256/* https://dom.spec.whatwg.org/#dom-element-matches */
257void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
258{
259 dom_query_selector_matches_ctx ctx = { thisp, false };
260
261 if (dom_query_matches(
262 thisp,
263 intern,
264 selectors_str,
265 &ctx
266 ) != LXB_STATUS_OK) {
268 } else {
269 RETURN_BOOL(ctx.result);
270 }
271}
272
273/* https://dom.spec.whatwg.org/#dom-element-closest */
274void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
275{
276 const xmlNode *result = dom_query_closest(thisp, intern, selectors_str);
277 if (EXPECTED(result != NULL)) {
278 DOM_RET_OBJ((xmlNodePtr) result, intern);
279 }
280}
281
282#endif
lxb_inline size_t lexbor_array_obj_length(lexbor_array_obj_t *array)
Definition array_obj.h:80
lxb_inline void * lexbor_array_obj_get(const lexbor_array_obj_t *array, size_t idx)
Definition array_obj.h:70
char * cb
Definition assert.c:26
@ LXB_STATUS_STOP
Definition base.h:68
@ LXB_STATUS_OK
Definition base.h:49
@ LXB_STATUS_ERROR
Definition base.h:50
struct lxb_css_parser lxb_css_parser_t
Definition base.h:41
lxb_css_parser_t * lxb_css_parser_destroy(lxb_css_parser_t *parser, bool self_destroy)
Definition parser.c:130
lxb_status_t lxb_css_parser_init(lxb_css_parser_t *parser, lxb_css_syntax_tokenizer_t *tkz)
Definition parser.c:19
struct lxb_css_selector_list lxb_css_selector_list_t
Definition base.h:40
lxb_css_selector_list_t * lxb_css_selectors_parse(lxb_css_parser_t *parser, const lxb_char_t *data, size_t length)
Definition selectors.c:134
DNS_STATUS status
Definition dns_win32.c:49
void php_dom_throw_error_with_message(dom_exception_code error_code, const char *error_message, bool strict_error)
@ SYNTAX_ERR
error($message)
Definition ext_skel.php:22
memset(ptr, 0, type->size)
#define NULL
Definition gdcache.h:45
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
#define DOM_NODESET
Definition php_dom.h:65
void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern)
@ DOM_NODELIST
Definition php_dom.h:115
PHP_JSON_API size_t int options
Definition php_json.h:102
char * msg
Definition phpdbg.h:289
void lxb_css_selector_list_destroy_memory(lxb_css_selector_list_t *list)
Definition selector.c:232
uint32_t lxb_css_selector_specificity_t
Definition selector.h:107
lxb_status_t lxb_selectors_find(lxb_selectors_t *selectors, const xmlNode *root, const lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx)
Definition selectors.c:410
void lxb_selectors_destroy(lxb_selectors_t *selectors)
Definition selectors.c:316
lxb_status_t lxb_selectors_init(lxb_selectors_t *selectors)
Definition selectors.c:285
lxb_status_t lxb_selectors_match_node(lxb_selectors_t *selectors, const xmlNode *node, const lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx)
Definition selectors.c:434
struct lxb_selectors lxb_selectors_t
Definition selectors.h:66
lxb_inline void lxb_selectors_opt_set(lxb_selectors_t *selectors, lxb_selectors_opt_t opt)
Definition selectors.h:230
lxb_selectors_opt_t
Definition selectors.h:23
@ LXB_SELECTORS_OPT_DEFAULT
Definition selectors.h:24
@ LXB_SELECTORS_OPT_QUIRKS_MODE
Definition selectors.h:62
@ LXB_SELECTORS_OPT_MATCH_FIRST
Definition selectors.h:59
lxb_status_t(* lxb_selectors_cb_f)(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
Definition selectors.h:71
zval * current
Definition session.c:1024
void * ptr
Definition xml_common.h:26
php_libxml_ref_obj * document
Definition xml_common.h:27
lexbor_array_obj_t messages
Definition log.h:35
lxb_css_log_t * log
Definition parser.h:168
unsigned int lxb_status_t
Definition types.h:28
unsigned char lxb_char_t
Definition types.h:27
struct _dom_object dom_object
#define DOM_RET_OBJ(obj, domobject)
Definition xml_common.h:76
#define Z_DOMOBJ_P(zv)
Definition xml_common.h:36
PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
ZEND_API size_t zend_spprintf(char **message, size_t max_len, const char *format,...)
Definition zend.c:311
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define RETURN_NULL()
Definition zend_API.h:1036
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_THROWS()
Definition zend_API.h:1060
#define efree(ptr)
Definition zend_alloc.h:155
struct _zval_struct zval
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
Definition zend_hash.c:1229
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
Definition zend_hash.c:1808
#define zend_new_array(size)
Definition zend_hash.h:338
struct _zend_string zend_string
#define EXPECTED(condition)
#define ZEND_ASSERT(c)
#define UNEXPECTED(condition)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
struct _zend_array HashTable
Definition zend_types.h:386
#define ZVAL_ARR(z, a)
zval * return_value
bool result
object
zval * ret