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