1 /*
2  * Copyright (C) 2018-2021 Alexander Borisov
3  *
4  * Author: Alexander Borisov <borisov@lexbor.com>
5  */
6 
7 #include "lexbor/dom/interfaces/element.h"
8 #include "lexbor/dom/interfaces/attr.h"
9 #include "lexbor/tag/tag.h"
10 #include "lexbor/ns/ns.h"
11 
12 #include "lexbor/core/str.h"
13 #include "lexbor/core/utils.h"
14 #include "lexbor/core/hash.h"
15 
16 
17 static const lxb_char_t *
18 lxb_dom_element_upper_update(lxb_dom_element_t *element, size_t *len);
19 
20 const lxb_tag_data_t *
21 lxb_tag_append(lexbor_hash_t *hash, lxb_tag_id_t tag_id,
22                const lxb_char_t *name, size_t length);
23 
24 const lxb_tag_data_t *
25 lxb_tag_append_lower(lexbor_hash_t *hash,
26                      const lxb_char_t *name, size_t length);
27 
28 const lxb_ns_data_t *
29 lxb_ns_append(lexbor_hash_t *hash, const lxb_char_t *link, size_t length);
30 
31 
32 lxb_dom_element_t *
lxb_dom_element_interface_create(lxb_dom_document_t * document)33 lxb_dom_element_interface_create(lxb_dom_document_t *document)
34 {
35     lxb_dom_element_t *element;
36 
37     element = lexbor_mraw_calloc(document->mraw,
38                                  sizeof(lxb_dom_element_t));
39     if (element == NULL) {
40         return NULL;
41     }
42 
43     lxb_dom_node_t *node = lxb_dom_interface_node(element);
44 
45     node->owner_document = lxb_dom_document_owner(document);
46     node->type = LXB_DOM_NODE_TYPE_ELEMENT;
47 
48     return element;
49 }
50 
51 lxb_dom_element_t *
lxb_dom_element_interface_clone(lxb_dom_document_t * document,const lxb_dom_element_t * element)52 lxb_dom_element_interface_clone(lxb_dom_document_t *document,
53                                 const lxb_dom_element_t *element)
54 {
55     lxb_dom_element_t *new;
56 
57     new = lxb_dom_element_interface_create(document);
58     if (new == NULL) {
59         return NULL;
60     }
61 
62     if (lxb_dom_element_interface_copy(new, element) != LXB_STATUS_OK) {
63         return lxb_dom_element_interface_destroy(new);
64     }
65 
66     return new;
67 }
68 
69 lxb_dom_element_t *
lxb_dom_element_interface_destroy(lxb_dom_element_t * element)70 lxb_dom_element_interface_destroy(lxb_dom_element_t *element)
71 {
72     lxb_dom_attr_t *attr_next;
73     lxb_dom_attr_t *attr = element->first_attr;
74 
75     (void) lxb_dom_node_interface_destroy(lxb_dom_interface_node(element));
76 
77     while (attr != NULL) {
78         attr_next = attr->next;
79 
80         lxb_dom_attr_interface_destroy(attr);
81 
82         attr = attr_next;
83     }
84 
85     return NULL;
86 }
87 
88 lxb_status_t
lxb_dom_element_interface_copy(lxb_dom_element_t * dst,const lxb_dom_element_t * src)89 lxb_dom_element_interface_copy(lxb_dom_element_t *dst,
90                                const lxb_dom_element_t *src)
91 {
92     lxb_status_t status;
93     lxb_dom_document_t *document;
94     lxb_dom_attr_t *attr, *clone;
95 
96     status = lxb_dom_node_interface_copy(&dst->node, &src->node, false);
97     if (status != LXB_STATUS_OK) {
98         return status;
99     }
100 
101     document = lxb_dom_interface_node(dst)->owner_document;
102     attr = src->first_attr;
103 
104     while (attr != NULL) {
105         clone = lxb_dom_attr_interface_clone(document, attr);
106         if (clone == NULL) {
107             return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
108         }
109 
110         (void) lxb_dom_element_attr_append(dst, clone);
111 
112         attr = attr->next;
113     }
114 
115     return LXB_STATUS_OK;
116 }
117 
118 LXB_API lxb_status_t
lxb_dom_element_qualified_name_set(lxb_dom_element_t * element,const lxb_char_t * prefix,size_t prefix_len,const lxb_char_t * lname,size_t lname_len)119 lxb_dom_element_qualified_name_set(lxb_dom_element_t *element,
120                                    const lxb_char_t *prefix, size_t prefix_len,
121                                    const lxb_char_t *lname, size_t lname_len)
122 {
123     lxb_char_t *key = (lxb_char_t *) lname;
124     const lxb_tag_data_t *tag_data;
125 
126     if (prefix != NULL && prefix_len != 0) {
127         key = lexbor_malloc(prefix_len + lname_len + 2);
128         if (key == NULL) {
129             return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
130         }
131 
132         memcpy(key, prefix, prefix_len);
133         memcpy(&key[prefix_len + 1], lname, lname_len);
134 
135         lname_len = prefix_len + lname_len + 1;
136 
137         key[prefix_len] = ':';
138         key[lname_len] = '\0';
139     }
140 
141     tag_data = lxb_tag_append(element->node.owner_document->tags,
142                               element->node.local_name, key, lname_len);
143 
144     if (key != lname) {
145         lexbor_free(key);
146     }
147 
148     if (tag_data == NULL) {
149         return LXB_STATUS_ERROR;
150     }
151 
152     element->qualified_name = (lxb_tag_id_t) tag_data;
153 
154     return LXB_STATUS_OK;
155 }
156 
157 lxb_dom_element_t *
lxb_dom_element_create(lxb_dom_document_t * document,const lxb_char_t * local_name,size_t lname_len,const lxb_char_t * ns_link,size_t ns_len,const lxb_char_t * prefix,size_t prefix_len,const lxb_char_t * is,size_t is_len,bool sync_custom)158 lxb_dom_element_create(lxb_dom_document_t *document,
159                        const lxb_char_t *local_name, size_t lname_len,
160                        const lxb_char_t *ns_link, size_t ns_len,
161                        const lxb_char_t *prefix, size_t prefix_len,
162                        const lxb_char_t *is, size_t is_len,
163                        bool sync_custom)
164 {
165     lxb_status_t status;
166     const lxb_ns_data_t *ns_data;
167     const lxb_tag_data_t *tag_data;
168     const lxb_ns_prefix_data_t *ns_prefix;
169     lxb_dom_element_t *element;
170 
171     /* TODO: Must implement custom elements */
172 
173     /* 7. Otherwise */
174 
175     ns_data = NULL;
176     tag_data = NULL;
177     ns_prefix = NULL;
178 
179     tag_data = lxb_tag_append_lower(document->tags, local_name, lname_len);
180     if (tag_data == NULL) {
181         return NULL;
182     }
183 
184     if (ns_link != NULL) {
185         ns_data = lxb_ns_append(document->ns, ns_link, ns_len);
186     }
187     else {
188         ns_data = lxb_ns_data_by_id(document->ns, LXB_NS__UNDEF);
189     }
190 
191     if (ns_data == NULL) {
192         return NULL;
193     }
194 
195     element = lxb_dom_document_create_interface(document, tag_data->tag_id,
196                                                 ns_data->ns_id);
197     if (element == NULL) {
198         return NULL;
199     }
200 
201     if (prefix != NULL) {
202         ns_prefix = lxb_ns_prefix_append(document->prefix, prefix, prefix_len);
203         if (ns_prefix == NULL) {
204             return lxb_dom_document_destroy_interface(element);
205         }
206 
207         element->node.prefix = ns_prefix->prefix_id;
208 
209         status = lxb_dom_element_qualified_name_set(element, prefix, prefix_len,
210                                                     local_name, lname_len);
211         if (status != LXB_STATUS_OK) {
212             return lxb_dom_document_destroy_interface(element);
213         }
214     }
215 
216     if (is_len != 0) {
217         status = lxb_dom_element_is_set(element, is, is_len);
218         if (status != LXB_STATUS_OK) {
219             return lxb_dom_document_destroy_interface(element);
220         }
221     }
222 
223     element->node.local_name = tag_data->tag_id;
224     element->node.ns = ns_data->ns_id;
225 
226     if (ns_data->ns_id == LXB_NS_HTML && is_len != 0) {
227         element->custom_state = LXB_DOM_ELEMENT_CUSTOM_STATE_UNDEFINED;
228     }
229     else {
230         element->custom_state = LXB_DOM_ELEMENT_CUSTOM_STATE_UNCUSTOMIZED;
231     }
232 
233     return element;
234 }
235 
236 lxb_dom_element_t *
lxb_dom_element_destroy(lxb_dom_element_t * element)237 lxb_dom_element_destroy(lxb_dom_element_t *element)
238 {
239     return lxb_dom_document_destroy_interface(element);
240 }
241 
242 bool
lxb_dom_element_has_attributes(lxb_dom_element_t * element)243 lxb_dom_element_has_attributes(lxb_dom_element_t *element)
244 {
245     return element->first_attr != NULL;
246 }
247 
248 lxb_dom_attr_t *
lxb_dom_element_set_attribute(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t qn_len,const lxb_char_t * value,size_t value_len)249 lxb_dom_element_set_attribute(lxb_dom_element_t *element,
250                               const lxb_char_t *qualified_name, size_t qn_len,
251                               const lxb_char_t *value, size_t value_len)
252 {
253     lxb_status_t status;
254     lxb_dom_attr_t *attr;
255 
256     attr = lxb_dom_element_attr_is_exist(element, qualified_name, qn_len);
257 
258     if (attr != NULL) {
259         status = lxb_dom_attr_set_value(attr, value, value_len);
260         if (status != LXB_STATUS_OK) {
261             return lxb_dom_attr_interface_destroy(attr);
262         }
263 
264         return attr;
265     }
266 
267     attr = lxb_dom_attr_interface_create(element->node.owner_document);
268     if (attr == NULL) {
269         return NULL;
270     }
271 
272     attr->node.ns = element->node.ns;
273 
274     if (element->node.ns == LXB_NS_HTML
275         && element->node.owner_document->type == LXB_DOM_DOCUMENT_DTYPE_HTML)
276     {
277         status = lxb_dom_attr_set_name(attr, qualified_name, qn_len, true);
278     }
279     else {
280         status = lxb_dom_attr_set_name(attr, qualified_name, qn_len, false);
281     }
282 
283     if (status != LXB_STATUS_OK) {
284         return lxb_dom_attr_interface_destroy(attr);
285     }
286 
287     status = lxb_dom_attr_set_value(attr, value, value_len);
288     if (status != LXB_STATUS_OK) {
289         return lxb_dom_attr_interface_destroy(attr);
290     }
291 
292     lxb_dom_element_attr_append(element, attr);
293 
294     return attr;
295 }
296 
297 const lxb_char_t *
lxb_dom_element_get_attribute(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t qn_len,size_t * value_len)298 lxb_dom_element_get_attribute(lxb_dom_element_t *element,
299                               const lxb_char_t *qualified_name, size_t qn_len,
300                               size_t *value_len)
301 {
302     lxb_dom_attr_t *attr;
303 
304     attr = lxb_dom_element_attr_by_name(element, qualified_name, qn_len);
305     if (attr == NULL) {
306         if (value_len != NULL) {
307             *value_len = 0;
308         }
309 
310         return NULL;
311     }
312 
313     return lxb_dom_attr_value(attr, value_len);
314 }
315 
316 lxb_status_t
lxb_dom_element_remove_attribute(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t qn_len)317 lxb_dom_element_remove_attribute(lxb_dom_element_t *element,
318                                  const lxb_char_t *qualified_name, size_t qn_len)
319 {
320     lxb_status_t status;
321     lxb_dom_attr_t *attr;
322 
323     attr = lxb_dom_element_attr_by_name(element, qualified_name, qn_len);
324     if (attr == NULL) {
325         return LXB_STATUS_OK;
326     }
327 
328     status = lxb_dom_element_attr_remove(element, attr);
329     if (status != LXB_STATUS_OK) {
330         return status;
331     }
332 
333     lxb_dom_attr_interface_destroy(attr);
334 
335     return LXB_STATUS_OK;
336 }
337 
338 bool
lxb_dom_element_has_attribute(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t qn_len)339 lxb_dom_element_has_attribute(lxb_dom_element_t *element,
340                               const lxb_char_t *qualified_name, size_t qn_len)
341 {
342     return lxb_dom_element_attr_by_name(element, qualified_name, qn_len) != NULL;
343 }
344 
345 lxb_status_t
lxb_dom_element_attr_append(lxb_dom_element_t * element,lxb_dom_attr_t * attr)346 lxb_dom_element_attr_append(lxb_dom_element_t *element, lxb_dom_attr_t *attr)
347 {
348     lxb_dom_attr_t *exist;
349     lxb_dom_document_t *doc = lxb_dom_interface_node(element)->owner_document;
350 
351     if (attr->node.local_name == LXB_DOM_ATTR_ID) {
352         exist = element->attr_id;
353 
354         if (exist != NULL) {
355             lxb_dom_element_attr_remove(element, exist);
356             lxb_dom_attr_interface_destroy(exist);
357         }
358 
359         element->attr_id = attr;
360     }
361     else if (attr->node.local_name == LXB_DOM_ATTR_CLASS) {
362         exist = element->attr_class;
363 
364         if (exist != NULL) {
365             lxb_dom_element_attr_remove(element, exist);
366             lxb_dom_attr_interface_destroy(exist);
367         }
368 
369         element->attr_class = attr;
370     }
371 
372     if (element->first_attr == NULL) {
373         element->first_attr = attr;
374         element->last_attr = attr;
375 
376         goto done;
377     }
378 
379     attr->prev = element->last_attr;
380 
381     element->last_attr->next = attr;
382     element->last_attr = attr;
383 
384 done:
385 
386     attr->owner = element;
387 
388     if (doc->ev_insert != NULL) {
389         doc->ev_insert(lxb_dom_interface_node(attr));
390     }
391 
392     return LXB_STATUS_OK;
393 }
394 
395 lxb_status_t
lxb_dom_element_attr_remove(lxb_dom_element_t * element,lxb_dom_attr_t * attr)396 lxb_dom_element_attr_remove(lxb_dom_element_t *element, lxb_dom_attr_t *attr)
397 {
398     (void) element;
399 
400     lxb_dom_attr_remove(attr);
401 
402     return LXB_STATUS_OK;
403 }
404 
405 lxb_dom_attr_t *
lxb_dom_element_attr_by_name(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t length)406 lxb_dom_element_attr_by_name(lxb_dom_element_t *element,
407                              const lxb_char_t *qualified_name, size_t length)
408 {
409     const lxb_dom_attr_data_t *data;
410     lexbor_hash_t *attrs = element->node.owner_document->attrs;
411     lxb_dom_attr_t *attr = element->first_attr;
412 
413     if (element->node.ns == LXB_NS_HTML
414         && element->node.owner_document->type == LXB_DOM_DOCUMENT_DTYPE_HTML)
415     {
416         data = lxb_dom_attr_data_by_local_name(attrs, qualified_name, length);
417     }
418     else {
419         data = lxb_dom_attr_data_by_qualified_name(attrs, qualified_name,
420                                                    length);
421     }
422 
423     if (data == NULL) {
424         return NULL;
425     }
426 
427     while (attr != NULL) {
428         if (attr->node.local_name == data->attr_id
429             || attr->qualified_name == data->attr_id)
430         {
431             return attr;
432         }
433 
434         attr = attr->next;
435     }
436 
437     return NULL;
438 }
439 
440 lxb_dom_attr_t *
lxb_dom_element_attr_by_local_name_data(lxb_dom_element_t * element,const lxb_dom_attr_data_t * data)441 lxb_dom_element_attr_by_local_name_data(lxb_dom_element_t *element,
442                                         const lxb_dom_attr_data_t *data)
443 {
444     lxb_dom_attr_t *attr = element->first_attr;
445 
446     while (attr != NULL) {
447         if (attr->node.local_name == data->attr_id) {
448             return attr;
449         }
450 
451         attr = attr->next;
452     }
453 
454     return NULL;
455 }
456 
457 lxb_dom_attr_t *
lxb_dom_element_attr_by_id(lxb_dom_element_t * element,lxb_dom_attr_id_t attr_id)458 lxb_dom_element_attr_by_id(lxb_dom_element_t *element,
459                            lxb_dom_attr_id_t attr_id)
460 {
461     lxb_dom_attr_t *attr = element->first_attr;
462 
463     while (attr != NULL) {
464         if (attr->node.local_name == attr_id) {
465             return attr;
466         }
467 
468         attr = attr->next;
469     }
470 
471     return NULL;
472 }
473 
474 bool
lxb_dom_element_compare(lxb_dom_element_t * first,lxb_dom_element_t * second)475 lxb_dom_element_compare(lxb_dom_element_t *first, lxb_dom_element_t *second)
476 {
477     lxb_dom_attr_t *f_attr = first->first_attr;
478     lxb_dom_attr_t *s_attr = second->first_attr;
479 
480     if (first->node.local_name != second->node.local_name
481         || first->node.ns != second->node.ns
482         || first->qualified_name != second->qualified_name)
483     {
484         return false;
485     }
486 
487     /* Compare attr counts */
488     while (f_attr != NULL && s_attr != NULL) {
489         f_attr = f_attr->next;
490         s_attr = s_attr->next;
491     }
492 
493     if (f_attr != NULL || s_attr != NULL) {
494         return false;
495     }
496 
497     /* Compare attr */
498     f_attr = first->first_attr;
499 
500     while (f_attr != NULL) {
501         s_attr = second->first_attr;
502 
503         while (s_attr != NULL) {
504             if (lxb_dom_attr_compare(f_attr, s_attr)) {
505                 break;
506             }
507 
508             s_attr = s_attr->next;
509         }
510 
511         if (s_attr == NULL) {
512             return false;
513         }
514 
515         f_attr = f_attr->next;
516     }
517 
518     return true;
519 }
520 
521 lxb_dom_attr_t *
lxb_dom_element_attr_is_exist(lxb_dom_element_t * element,const lxb_char_t * qualified_name,size_t length)522 lxb_dom_element_attr_is_exist(lxb_dom_element_t *element,
523                               const lxb_char_t *qualified_name, size_t length)
524 {
525     const lxb_dom_attr_data_t *data;
526     lxb_dom_attr_t *attr = element->first_attr;
527 
528     data = lxb_dom_attr_data_by_local_name(element->node.owner_document->attrs,
529                                            qualified_name, length);
530     if (data == NULL) {
531         return NULL;
532     }
533 
534     while (attr != NULL) {
535         if (attr->node.local_name == data->attr_id
536             || attr->qualified_name == data->attr_id)
537         {
538             return attr;
539         }
540 
541         attr = attr->next;
542     }
543 
544     return NULL;
545 }
546 
547 lxb_status_t
lxb_dom_element_is_set(lxb_dom_element_t * element,const lxb_char_t * is,size_t is_len)548 lxb_dom_element_is_set(lxb_dom_element_t *element,
549                        const lxb_char_t *is, size_t is_len)
550 {
551     if (element->is_value == NULL) {
552         element->is_value = lexbor_mraw_calloc(element->node.owner_document->mraw,
553                                                sizeof(lexbor_str_t));
554         if (element->is_value == NULL) {
555             return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
556         }
557     }
558 
559     if (element->is_value->data == NULL) {
560         lexbor_str_init(element->is_value,
561                         element->node.owner_document->text, is_len);
562 
563         if (element->is_value->data == NULL) {
564             return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
565         }
566     }
567 
568     if (element->is_value->length != 0) {
569         element->is_value->length = 0;
570     }
571 
572     lxb_char_t *data = lexbor_str_append(element->is_value,
573                                          element->node.owner_document->text,
574                                          is, is_len);
575     if (data == NULL) {
576         return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
577     }
578 
579     return LXB_STATUS_OK;
580 }
581 
582 lxb_status_t
lxb_dom_elements_by_tag_name(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * qname,size_t len)583 lxb_dom_elements_by_tag_name(lxb_dom_element_t *root,
584                              lxb_dom_collection_t *collection,
585                              const lxb_char_t *qname, size_t len)
586 {
587     return lxb_dom_node_by_tag_name(lxb_dom_interface_node(root),
588                                     collection, qname, len);
589 }
590 
591 lxb_status_t
lxb_dom_elements_by_class_name(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * class_name,size_t len)592 lxb_dom_elements_by_class_name(lxb_dom_element_t *root,
593                                lxb_dom_collection_t *collection,
594                                const lxb_char_t *class_name, size_t len)
595 {
596     return lxb_dom_node_by_class_name(lxb_dom_interface_node(root),
597                                       collection, class_name, len);
598 }
599 
600 lxb_status_t
lxb_dom_elements_by_attr(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * qname,size_t qname_len,const lxb_char_t * value,size_t value_len,bool case_insensitive)601 lxb_dom_elements_by_attr(lxb_dom_element_t *root,
602                          lxb_dom_collection_t *collection,
603                          const lxb_char_t *qname, size_t qname_len,
604                          const lxb_char_t *value, size_t value_len,
605                          bool case_insensitive)
606 {
607     return lxb_dom_node_by_attr(lxb_dom_interface_node(root),
608                                 collection, qname, qname_len,
609                                 value, value_len, case_insensitive);
610 }
611 
612 lxb_status_t
lxb_dom_elements_by_attr_begin(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * qname,size_t qname_len,const lxb_char_t * value,size_t value_len,bool case_insensitive)613 lxb_dom_elements_by_attr_begin(lxb_dom_element_t *root,
614                                lxb_dom_collection_t *collection,
615                                const lxb_char_t *qname, size_t qname_len,
616                                const lxb_char_t *value, size_t value_len,
617                                bool case_insensitive)
618 {
619     return lxb_dom_node_by_attr_begin(lxb_dom_interface_node(root),
620                                       collection, qname, qname_len,
621                                       value, value_len, case_insensitive);
622 }
623 
624 lxb_status_t
lxb_dom_elements_by_attr_end(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * qname,size_t qname_len,const lxb_char_t * value,size_t value_len,bool case_insensitive)625 lxb_dom_elements_by_attr_end(lxb_dom_element_t *root,
626                              lxb_dom_collection_t *collection,
627                              const lxb_char_t *qname, size_t qname_len,
628                              const lxb_char_t *value, size_t value_len,
629                              bool case_insensitive)
630 {
631     return lxb_dom_node_by_attr_end(lxb_dom_interface_node(root),
632                                     collection, qname, qname_len,
633                                     value, value_len, case_insensitive);
634 }
635 
636 lxb_status_t
lxb_dom_elements_by_attr_contain(lxb_dom_element_t * root,lxb_dom_collection_t * collection,const lxb_char_t * qname,size_t qname_len,const lxb_char_t * value,size_t value_len,bool case_insensitive)637 lxb_dom_elements_by_attr_contain(lxb_dom_element_t *root,
638                                  lxb_dom_collection_t *collection,
639                                  const lxb_char_t *qname, size_t qname_len,
640                                  const lxb_char_t *value, size_t value_len,
641                                  bool case_insensitive)
642 {
643     return lxb_dom_node_by_attr_contain(lxb_dom_interface_node(root),
644                                         collection, qname, qname_len,
645                                         value, value_len, case_insensitive);
646 }
647 
648 const lxb_char_t *
lxb_dom_element_qualified_name(lxb_dom_element_t * element,size_t * len)649 lxb_dom_element_qualified_name(lxb_dom_element_t *element, size_t *len)
650 {
651     const lxb_tag_data_t *data;
652 
653     if (element->qualified_name != 0) {
654         data = lxb_tag_data_by_id(element->node.owner_document->tags,
655                                   element->qualified_name);
656     }
657     else {
658         data = lxb_tag_data_by_id(element->node.owner_document->tags,
659                                   element->node.local_name);
660     }
661 
662     if (len != NULL) {
663         *len = data->entry.length;
664     }
665 
666     return lexbor_hash_entry_str(&data->entry);
667 }
668 
669 const lxb_char_t *
lxb_dom_element_qualified_name_upper(lxb_dom_element_t * element,size_t * len)670 lxb_dom_element_qualified_name_upper(lxb_dom_element_t *element, size_t *len)
671 {
672     lxb_tag_data_t *data;
673 
674     if (element->upper_name == LXB_TAG__UNDEF) {
675         return lxb_dom_element_upper_update(element, len);
676     }
677 
678     data = (lxb_tag_data_t *) element->upper_name;
679 
680     if (len != NULL) {
681         *len = data->entry.length;
682     }
683 
684     return lexbor_hash_entry_str(&data->entry);
685 }
686 
687 static const lxb_char_t *
lxb_dom_element_upper_update(lxb_dom_element_t * element,size_t * len)688 lxb_dom_element_upper_update(lxb_dom_element_t *element, size_t *len)
689 {
690     size_t length;
691     lxb_tag_data_t *data;
692     const lxb_char_t *name;
693 
694     if (element->upper_name != LXB_TAG__UNDEF) {
695         /* TODO: release current tag data if ref_count == 0. */
696         /* data = (lxb_tag_data_t *) element->upper_name; */
697     }
698 
699     name = lxb_dom_element_qualified_name(element, &length);
700     if (name == NULL) {
701         return NULL;
702     }
703 
704     data = lexbor_hash_insert(element->node.owner_document->tags,
705                               lexbor_hash_insert_upper, name, length);
706     if (data == NULL) {
707         return NULL;
708     }
709 
710     data->tag_id = element->node.local_name;
711 
712     if (len != NULL) {
713         *len = length;
714     }
715 
716     element->upper_name = (lxb_tag_id_t) data;
717 
718     return lexbor_hash_entry_str(&data->entry);
719 }
720 
721 const lxb_char_t *
lxb_dom_element_local_name(lxb_dom_element_t * element,size_t * len)722 lxb_dom_element_local_name(lxb_dom_element_t *element, size_t *len)
723 {
724     const lxb_tag_data_t *data;
725 
726     data = lxb_tag_data_by_id(element->node.owner_document->tags,
727                               element->node.local_name);
728     if (data == NULL) {
729         if (len != NULL) {
730             *len = 0;
731         }
732 
733         return NULL;
734     }
735 
736     if (len != NULL) {
737         *len = data->entry.length;
738     }
739 
740     return lexbor_hash_entry_str(&data->entry);
741 }
742 
743 const lxb_char_t *
lxb_dom_element_prefix(lxb_dom_element_t * element,size_t * len)744 lxb_dom_element_prefix(lxb_dom_element_t *element, size_t *len)
745 {
746     const lxb_ns_prefix_data_t *data;
747 
748     if (element->node.prefix == LXB_NS__UNDEF) {
749         goto empty;
750     }
751 
752     data = lxb_ns_prefix_data_by_id(element->node.owner_document->tags,
753                                     element->node.prefix);
754     if (data == NULL) {
755         goto empty;
756     }
757 
758     return lexbor_hash_entry_str(&data->entry);
759 
760 empty:
761 
762     if (len != NULL) {
763         *len = 0;
764     }
765 
766     return NULL;
767 }
768 
769 const lxb_char_t *
lxb_dom_element_tag_name(lxb_dom_element_t * element,size_t * len)770 lxb_dom_element_tag_name(lxb_dom_element_t *element, size_t *len)
771 {
772     lxb_dom_document_t *doc = lxb_dom_interface_node(element)->owner_document;
773 
774     if (element->node.ns != LXB_NS_HTML
775         || doc->type != LXB_DOM_DOCUMENT_DTYPE_HTML)
776     {
777         return lxb_dom_element_qualified_name(element, len);
778     }
779 
780     return lxb_dom_element_qualified_name_upper(element, len);
781 }
782 
783 
784 
785 /*
786  * No inline functions for ABI.
787  */
788 const lxb_char_t *
lxb_dom_element_id_noi(lxb_dom_element_t * element,size_t * len)789 lxb_dom_element_id_noi(lxb_dom_element_t *element, size_t *len)
790 {
791     return lxb_dom_element_id(element, len);
792 }
793 
794 const lxb_char_t *
lxb_dom_element_class_noi(lxb_dom_element_t * element,size_t * len)795 lxb_dom_element_class_noi(lxb_dom_element_t *element, size_t *len)
796 {
797     return lxb_dom_element_class(element, len);
798 }
799 
800 bool
lxb_dom_element_is_custom_noi(lxb_dom_element_t * element)801 lxb_dom_element_is_custom_noi(lxb_dom_element_t *element)
802 {
803     return lxb_dom_element_is_custom(element);
804 }
805 
806 bool
lxb_dom_element_custom_is_defined_noi(lxb_dom_element_t * element)807 lxb_dom_element_custom_is_defined_noi(lxb_dom_element_t *element)
808 {
809     return lxb_dom_element_custom_is_defined(element);
810 }
811 
812 lxb_dom_attr_t *
lxb_dom_element_first_attribute_noi(lxb_dom_element_t * element)813 lxb_dom_element_first_attribute_noi(lxb_dom_element_t *element)
814 {
815     return lxb_dom_element_first_attribute(element);
816 }
817 
818 lxb_dom_attr_t *
lxb_dom_element_next_attribute_noi(lxb_dom_attr_t * attr)819 lxb_dom_element_next_attribute_noi(lxb_dom_attr_t *attr)
820 {
821     return lxb_dom_element_next_attribute(attr);
822 }
823 
824 lxb_dom_attr_t *
lxb_dom_element_prev_attribute_noi(lxb_dom_attr_t * attr)825 lxb_dom_element_prev_attribute_noi(lxb_dom_attr_t *attr)
826 {
827     return lxb_dom_element_prev_attribute(attr);
828 }
829 
830 lxb_dom_attr_t *
lxb_dom_element_last_attribute_noi(lxb_dom_element_t * element)831 lxb_dom_element_last_attribute_noi(lxb_dom_element_t *element)
832 {
833     return lxb_dom_element_last_attribute(element);
834 }
835 
836 lxb_dom_attr_t *
lxb_dom_element_id_attribute_noi(lxb_dom_element_t * element)837 lxb_dom_element_id_attribute_noi(lxb_dom_element_t *element)
838 {
839     return lxb_dom_element_id_attribute(element);
840 }
841 
842 lxb_dom_attr_t *
lxb_dom_element_class_attribute_noi(lxb_dom_element_t * element)843 lxb_dom_element_class_attribute_noi(lxb_dom_element_t *element)
844 {
845     return lxb_dom_element_class_attribute(element);
846 }
847