@@ -65,7 +65,21 @@ static zend_always_inline bool lxb_selectors_adapted_cmp_local_name_id(const xml
65
65
66
66
static zend_always_inline const xmlAttr * lxb_selectors_adapted_attr (const xmlNode * node , const lxb_char_t * name )
67
67
{
68
- const xmlAttr * attr = xmlHasProp (node , (const xmlChar * ) name );
68
+ const xmlAttr * attr = NULL ;
69
+ ZEND_ASSERT (node -> doc != NULL );
70
+ if (php_dom_ns_is_html_and_document_is_html (node )) {
71
+ /* No need to handle DTD entities as we're in HTML. */
72
+ size_t name_bound = strlen ((const char * ) name ) + 1 ;
73
+ for (const xmlAttr * cur = node -> properties ; cur != NULL ; cur = cur -> next ) {
74
+ if (lexbor_str_data_nlocmp_right (cur -> name , name , name_bound )) {
75
+ attr = cur ;
76
+ break ;
77
+ }
78
+ }
79
+ } else {
80
+ attr = xmlHasProp (node , (const xmlChar * ) name );
81
+ }
82
+
69
83
if (attr != NULL && attr -> ns != NULL ) {
70
84
return NULL ;
71
85
}
@@ -85,8 +99,67 @@ static zend_always_inline dom_lxb_str_wrapper lxb_selectors_adapted_attr_value(c
85
99
return ret ;
86
100
}
87
101
102
+ static bool lxb_selectors_attrib_name_cmp (const lxb_css_selector_t * selector , const char * name , size_t len )
103
+ {
104
+ return selector -> name .length == len && lexbor_str_data_nlocmp_right ((const lxb_char_t * ) name , selector -> name .data , len );
105
+ }
106
+
107
+ /* From https://html.spec.whatwg.org/#case-sensitivity-of-selectors
108
+ * "Attribute selectors on an HTML element in an HTML document must treat the values of attributes with the following names as ASCII case-insensitive:" */
109
+ static bool lxb_selectors_is_lowercased_html_attrib_name (const lxb_css_selector_t * selector )
110
+ {
111
+ return lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("accept" ))
112
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("accept-charset" ))
113
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("align" ))
114
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("alink" ))
115
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("axis" ))
116
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("bgcolor" ))
117
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("charset" ))
118
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("checked" ))
119
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("clear" ))
120
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("codetype" ))
121
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("color" ))
122
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("compact" ))
123
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("declare" ))
124
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("defer" ))
125
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("dir" ))
126
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("direction" ))
127
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("disabled" ))
128
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("enctype" ))
129
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("face" ))
130
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("frame" ))
131
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("hreflang" ))
132
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("http-equiv" ))
133
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("lang" ))
134
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("language" ))
135
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("link" ))
136
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("media" ))
137
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("method" ))
138
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("multiple" ))
139
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("nohref" ))
140
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("noresize" ))
141
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("noshade" ))
142
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("nowrap" ))
143
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("readonly" ))
144
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("rel" ))
145
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("rev" ))
146
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("rules" ))
147
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("scope" ))
148
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("scrolling" ))
149
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("selected" ))
150
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("shape" ))
151
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("target" ))
152
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("text" ))
153
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("type" ))
154
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("valign" ))
155
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("valuetype" ))
156
+ || lxb_selectors_attrib_name_cmp (selector , ZEND_STRL ("vlink" ));
157
+ }
158
+
88
159
static void lxb_selectors_adapted_set_entry_id_ex (lxb_selectors_entry_t * entry , const lxb_css_selector_t * selector , const xmlNode * node )
89
160
{
161
+ entry -> id .attr_case_insensitive = lxb_selectors_is_lowercased_html_attrib_name (selector );
162
+
90
163
if (node -> doc != NULL && node -> doc -> dict != NULL ) {
91
164
const xmlChar * interned = xmlDictExists (node -> doc -> dict , selector -> name .data , selector -> name .length );
92
165
if (interned != NULL ) {
@@ -1287,10 +1360,10 @@ lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src,
1287
1360
}
1288
1361
1289
1362
static bool
1290
- lxb_selectors_match_attribute_value (const lxb_css_selector_attribute_t * attr , const lexbor_str_t * trg , const lexbor_str_t * src )
1363
+ lxb_selectors_match_attribute_value (const lxb_css_selector_attribute_t * attr , bool force_modifier_i , const lexbor_str_t * trg , const lexbor_str_t * src )
1291
1364
{
1292
1365
bool res ;
1293
- bool ins = attr -> modifier == LXB_CSS_SELECTOR_MODIFIER_I ;
1366
+ bool ins = attr -> modifier == LXB_CSS_SELECTOR_MODIFIER_I || force_modifier_i ;
1294
1367
1295
1368
switch (attr -> match ) {
1296
1369
case LXB_CSS_SELECTOR_MATCH_EQUAL : /* = */
@@ -1402,7 +1475,13 @@ lxb_selectors_match_attribute(const lxb_css_selector_t *selector,
1402
1475
}
1403
1476
1404
1477
dom_lxb_str_wrapper trg = lxb_selectors_adapted_attr_value (dom_attr );
1405
- bool res = lxb_selectors_match_attribute_value (attr , & trg .str , src );
1478
+ ZEND_ASSERT (node -> doc != NULL );
1479
+ bool res = lxb_selectors_match_attribute_value (
1480
+ attr ,
1481
+ entry -> id .attr_case_insensitive && php_dom_ns_is_html_and_document_is_html (node ),
1482
+ & trg .str ,
1483
+ src
1484
+ );
1406
1485
dom_lxb_str_wrapper_release (& trg );
1407
1486
return res ;
1408
1487
}
0 commit comments