1 /*
2 * This file is part of libdom.
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 * Copyright 2009 Bo Yang <struggleyb.nku.com>
6 */
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12
13 #include "html/html_document.h"
14 #include "html/html_element.h"
15
16 #include "core/node.h"
17 #include "core/attr.h"
18 #include "core/document.h"
19 #include "utils/utils.h"
20
21 struct dom_html_element_vtable _dom_html_element_vtable = {
22 {
23 {
24 {
25 DOM_NODE_EVENT_TARGET_VTABLE
26 },
27 DOM_NODE_VTABLE_ELEMENT,
28 },
29 DOM_ELEMENT_VTABLE_HTML_ELEMENT,
30 },
31 DOM_HTML_ELEMENT_VTABLE
32 };
33
34 static struct dom_element_protected_vtable _dom_html_element_protect_vtable = {
35 {
36 DOM_HTML_ELEMENT_PROTECT_VTABLE
37 },
38 DOM_ELEMENT_PROTECT_VTABLE
39 };
40
_dom_html_element_create(struct dom_html_element_create_params * params,struct dom_html_element ** result)41 dom_exception _dom_html_element_create(
42 struct dom_html_element_create_params *params,
43 struct dom_html_element **result)
44 {
45 dom_exception error;
46 dom_html_element *el;
47
48 el = malloc(sizeof(struct dom_html_element));
49 if (el == NULL)
50 return DOM_NO_MEM_ERR;
51
52 el->base.base.base.vtable = &_dom_html_element_vtable;
53 el->base.base.vtable = &_dom_html_element_protect_vtable;
54
55 error = _dom_html_element_initialise(params, el);
56 if (error != DOM_NO_ERR) {
57 free(el);
58 return error;
59 }
60
61 *result = el;
62
63 return DOM_NO_ERR;
64 }
65
_dom_html_element_initialise(struct dom_html_element_create_params * params,struct dom_html_element * el)66 dom_exception _dom_html_element_initialise(
67 struct dom_html_element_create_params *params,
68 struct dom_html_element *el)
69 {
70 dom_exception err;
71
72 el->type = params->type;
73
74 err = _dom_element_initialise(¶ms->doc->base, &el->base,
75 params->name, params->namespace, params->prefix);
76 if (err != DOM_NO_ERR)
77 return err;
78
79 return err;
80 }
81
_dom_html_element_finalise(struct dom_html_element * ele)82 void _dom_html_element_finalise(struct dom_html_element *ele)
83 {
84 _dom_element_finalise(&ele->base);
85 }
86
87 /*------------------------------------------------------------------------*/
88 /* The protected virtual functions */
89
90 /* The virtual destroy function, see src/core/node.c for detail */
_dom_html_element_destroy(dom_node_internal * node)91 void _dom_html_element_destroy(dom_node_internal *node)
92 {
93 dom_html_element *html = (dom_html_element *) node;
94
95 _dom_html_element_finalise(html);
96
97 free(html);
98 }
99
100 /* The virtual copy function, see src/core/node.c for detail */
_dom_html_element_copy(dom_node_internal * old,dom_node_internal ** copy)101 dom_exception _dom_html_element_copy(dom_node_internal *old,
102 dom_node_internal **copy)
103 {
104 dom_html_element *new_node;
105 dom_exception err;
106
107 new_node = malloc(sizeof(dom_html_element));
108 if (new_node == NULL)
109 return DOM_NO_MEM_ERR;
110
111 err = dom_html_element_copy_internal(old, new_node);
112 if (err != DOM_NO_ERR) {
113 free(new_node);
114 return err;
115 }
116
117 *copy = (dom_node_internal *) new_node;
118
119 return DOM_NO_ERR;
120 }
121
_dom_html_element_copy_internal(dom_html_element * old,dom_html_element * new)122 dom_exception _dom_html_element_copy_internal(
123 dom_html_element *old, dom_html_element *new)
124 {
125 dom_exception err;
126
127 err = dom_element_copy_internal(old, new);
128 if (err != DOM_NO_ERR) {
129 return err;
130 }
131
132 new->type = old->type;
133
134 return DOM_NO_ERR;
135 }
136
137 /*-----------------------------------------------------------------------*/
138 /* API functions */
139
140 #define SIMPLE_GET_SET(fattr,attr) \
141 dom_exception _dom_html_element_get_##fattr(dom_html_element *element, \
142 dom_string **fattr) \
143 { \
144 dom_exception ret; \
145 dom_string *_memo_##attr; \
146 \
147 _memo_##attr = \
148 ((struct dom_html_document *) \
149 ((struct dom_node_internal *)element)->owner)->memoised[hds_##attr]; \
150 \
151 ret = dom_element_get_attribute(element, _memo_##attr, fattr); \
152 \
153 return ret; \
154 } \
155 \
156 dom_exception _dom_html_element_set_##fattr(dom_html_element *element, \
157 dom_string *fattr) \
158 { \
159 dom_exception ret; \
160 dom_string *_memo_##attr; \
161 \
162 _memo_##attr = \
163 ((struct dom_html_document *) \
164 ((struct dom_node_internal *)element)->owner)->memoised[hds_##attr]; \
165 \
166 ret = dom_element_set_attribute(element, _memo_##attr, fattr); \
167 \
168 return ret; \
169 }
170
SIMPLE_GET_SET(id,id)171 SIMPLE_GET_SET(id,id)
172 SIMPLE_GET_SET(title,title)
173 SIMPLE_GET_SET(lang,lang)
174 SIMPLE_GET_SET(dir,dir)
175 SIMPLE_GET_SET(class_name,class)
176
177 dom_exception _dom_html_element_get_attribute(
178 struct dom_element *element,
179 dom_string *name, dom_string **value)
180 {
181 dom_exception exc;
182 dom_string *lower_case_name;
183
184 exc = dom_string_tolower(name, true, &lower_case_name);
185 if (exc != DOM_NO_ERR) {
186 return exc;
187 }
188
189 exc = _dom_element_get_attribute(element, lower_case_name, value);
190 dom_string_unref(lower_case_name);
191
192 return exc;
193 }
194
_dom_html_element_set_attribute(struct dom_element * element,dom_string * name,dom_string * value)195 dom_exception _dom_html_element_set_attribute(
196 struct dom_element *element,
197 dom_string *name, dom_string *value)
198 {
199 dom_exception exc;
200 dom_string *lower_case_name;
201
202 exc = dom_string_tolower(name, true, &lower_case_name);
203 if (exc != DOM_NO_ERR) {
204 return exc;
205 }
206
207 exc = _dom_element_set_attribute(element, lower_case_name, value);
208 dom_string_unref(lower_case_name);
209
210 return exc;
211 }
212
_dom_html_element_remove_attribute(struct dom_element * element,dom_string * name)213 dom_exception _dom_html_element_remove_attribute(
214 struct dom_element *element,
215 dom_string *name)
216 {
217 dom_exception exc;
218 dom_string *lower_case_name;
219
220 exc = dom_string_tolower(name, true, &lower_case_name);
221 if (exc != DOM_NO_ERR) {
222 return exc;
223 }
224
225 exc = _dom_element_remove_attribute(element, lower_case_name);
226 dom_string_unref(lower_case_name);
227
228 return exc;
229 }
230
_dom_html_element_has_attribute(struct dom_element * element,dom_string * name,bool * result)231 dom_exception _dom_html_element_has_attribute(
232 struct dom_element *element,
233 dom_string *name, bool *result)
234 {
235 dom_exception exc;
236 dom_string *lower_case_name;
237
238 exc = dom_string_tolower(name, true, &lower_case_name);
239 if (exc != DOM_NO_ERR) {
240 return exc;
241 }
242
243 exc = _dom_element_has_attribute(element, lower_case_name, result);
244 dom_string_unref(lower_case_name);
245
246 return exc;
247 }
248
249
250 /**
251 * Retrieve a list of descendant elements of an element which match a given
252 * tag name (caselessly)
253 *
254 * \param element The root of the subtree to search
255 * \param name The tag name to match (or "*" for all tags)
256 * \param result Pointer to location to receive result
257 * \return DOM_NO_ERR.
258 *
259 * The returned nodelist will have its reference count increased. It is
260 * the responsibility of the caller to unref the nodelist once it has
261 * finished with it.
262 */
_dom_html_element_get_elements_by_tag_name(struct dom_element * element,dom_string * name,struct dom_nodelist ** result)263 dom_exception _dom_html_element_get_elements_by_tag_name(
264 struct dom_element *element, dom_string *name,
265 struct dom_nodelist **result)
266 {
267 dom_exception err;
268 dom_node_internal *base = (dom_node_internal *) element;
269
270 assert(base->owner != NULL);
271
272 err = _dom_document_get_nodelist(base->owner,
273 DOM_NODELIST_BY_NAME_CASELESS,
274 (struct dom_node_internal *) element, name, NULL,
275 NULL, result);
276
277 return err;
278 }
279
280 /**
281 * Retrieve a list of descendant elements of an element which match a given
282 * namespace/localname pair, caselessly.
283 *
284 * \param element The root of the subtree to search
285 * \param namespace The namespace URI to match (or "*" for all)
286 * \param localname The local name to match (or "*" for all)
287 * \param result Pointer to location to receive result
288 * \return DOM_NO_ERR on success,
289 * DOM_NOT_SUPPORTED_ERR if the implementation does not support
290 * the feature "XML" and the language exposed
291 * through the Document does not support
292 * Namespaces.
293 *
294 * The returned nodelist will have its reference count increased. It is
295 * the responsibility of the caller to unref the nodelist once it has
296 * finished with it.
297 */
_dom_html_element_get_elements_by_tag_name_ns(struct dom_element * element,dom_string * namespace,dom_string * localname,struct dom_nodelist ** result)298 dom_exception _dom_html_element_get_elements_by_tag_name_ns(
299 struct dom_element *element, dom_string *namespace,
300 dom_string *localname, struct dom_nodelist **result)
301 {
302 dom_exception err;
303
304 /** \todo ensure XML feature is supported */
305
306 err = _dom_document_get_nodelist(element->base.owner,
307 DOM_NODELIST_BY_NAMESPACE_CASELESS,
308 (struct dom_node_internal *) element, NULL,
309 namespace, localname,
310 result);
311
312 return err;
313 }
314
315 /**
316 * Retrieve an HTML element's tag type.
317 *
318 * \param element The element to get the tag type of.
319 * \param type Updated to the tag type of the element.
320 * \return DOM_NO_ERR
321 *
322 * Elements with non-standard tags will be DOM_HTML_ELEMENT_TYPE__UNKNOWN.
323 */
_dom_html_element_get_tag_type(const struct dom_html_element * element,dom_html_element_type * type)324 dom_exception _dom_html_element_get_tag_type(
325 const struct dom_html_element *element,
326 dom_html_element_type *type)
327 {
328 *type = element->type;
329
330 return DOM_NO_ERR;
331 }
332
333 /*-----------------------------------------------------------------------*/
334 /* Common functions */
335
336 /**
337 * Get the a bool property
338 *
339 * \param ele The dom_html_element object
340 * \param name The name of the attribute
341 * \param len The length of ::name
342 * \param has The returned status
343 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
344 */
dom_html_element_get_bool_property(dom_html_element * ele,const char * name,uint32_t len,bool * has)345 dom_exception dom_html_element_get_bool_property(dom_html_element *ele,
346 const char *name, uint32_t len, bool *has)
347 {
348 dom_string *str = NULL;
349 dom_attr *a = NULL;
350 dom_exception err;
351
352 err = dom_string_create((const uint8_t *) name, len, &str);
353 if (err != DOM_NO_ERR)
354 goto fail;
355
356 err = dom_element_get_attribute_node(ele, str, &a);
357 if (err != DOM_NO_ERR)
358 goto cleanup1;
359
360 if (a != NULL) {
361 *has = true;
362 } else {
363 *has = false;
364 }
365
366 dom_node_unref(a);
367
368 cleanup1:
369 dom_string_unref(str);
370
371 fail:
372 return err;
373 }
374
375 /**
376 * Set a bool property
377 *
378 * \param ele The dom_html_element object
379 * \param name The name of the attribute
380 * \param len The length of ::name
381 * \param has The status
382 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
383 */
dom_html_element_set_bool_property(dom_html_element * ele,const char * name,uint32_t len,bool has)384 dom_exception dom_html_element_set_bool_property(dom_html_element *ele,
385 const char *name, uint32_t len, bool has)
386 {
387 dom_string *str = NULL;
388 dom_attr *a = NULL;
389 dom_exception err;
390
391 err = dom_string_create((const uint8_t *) name, len, &str);
392 if (err != DOM_NO_ERR)
393 goto fail;
394
395 err = dom_element_get_attribute_node(ele, str, &a);
396 if (err != DOM_NO_ERR)
397 goto cleanup1;
398
399 if (a != NULL && has == false) {
400 dom_attr *res = NULL;
401
402 err = dom_element_remove_attribute_node(ele, a, &res);
403 if (err != DOM_NO_ERR)
404 goto cleanup2;
405
406 dom_node_unref(res);
407 } else if (a == NULL && has == true) {
408 dom_document *doc = dom_node_get_owner(ele);
409 dom_attr *res = NULL;
410
411 err = _dom_attr_create(doc, str, NULL, NULL, true, &a);
412 if (err != DOM_NO_ERR) {
413 goto cleanup1;
414 }
415
416 err = dom_element_set_attribute_node(ele, a, &res);
417 if (err != DOM_NO_ERR)
418 goto cleanup2;
419
420 dom_node_unref(res);
421 }
422
423 cleanup2:
424 dom_node_unref(a);
425
426 cleanup1:
427 dom_string_unref(str);
428
429 fail:
430 return err;
431 }
432
_strndup(const char * s,size_t n)433 static char *_strndup(const char *s, size_t n)
434 {
435 size_t len;
436 char *s2;
437
438 for (len = 0; len != n && s[len] != '\0'; len++)
439 continue;
440
441 s2 = malloc(len + 1);
442 if (s2 == NULL)
443 return NULL;
444
445 memcpy(s2, s, len);
446 s2[len] = '\0';
447 return s2;
448 }
449
450 /**
451 * Get the a int32_t property
452 *
453 * \param ele The dom_html_element object
454 * \param name The name of the attribute
455 * \param len The length of ::name
456 * \param value The returned value, or -1 if prop. not set
457 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
458 */
dom_html_element_get_int32_t_property(dom_html_element * ele,const char * name,uint32_t len,int32_t * value)459 dom_exception dom_html_element_get_int32_t_property(dom_html_element *ele,
460 const char *name, uint32_t len, int32_t *value)
461 {
462 dom_string *str = NULL, *s2 = NULL;
463 dom_attr *a = NULL;
464 dom_exception err;
465
466 err = dom_string_create((const uint8_t *) name, len, &str);
467 if (err != DOM_NO_ERR)
468 goto fail;
469
470 err = dom_element_get_attribute_node(ele, str, &a);
471 if (err != DOM_NO_ERR)
472 goto cleanup1;
473
474 if (a != NULL) {
475 err = dom_node_get_text_content(a, &s2);
476 if (err == DOM_NO_ERR) {
477 char *s3 = _strndup(dom_string_data(s2),
478 dom_string_byte_length(s2));
479 if (s3 != NULL) {
480 *value = strtoul(s3, NULL, 0);
481 free(s3);
482 } else {
483 err = DOM_NO_MEM_ERR;
484 }
485 dom_string_unref(s2);
486 }
487 } else {
488 /* Property is not set on this node */
489 *value = -1;
490 }
491
492 dom_node_unref(a);
493
494 cleanup1:
495 dom_string_unref(str);
496
497 fail:
498 return err;
499 }
500
501 /**
502 * Set a int32_t property
503 *
504 * \param ele The dom_html_element object
505 * \param name The name of the attribute
506 * \param len The length of ::name
507 * \param value The value
508 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
509 */
dom_html_element_set_int32_t_property(dom_html_element * ele,const char * name,uint32_t len,uint32_t value)510 dom_exception dom_html_element_set_int32_t_property(dom_html_element *ele,
511 const char *name, uint32_t len, uint32_t value)
512 {
513 dom_string *str = NULL, *svalue = NULL;
514 dom_exception err;
515 char numbuffer[32];
516
517 err = dom_string_create((const uint8_t *) name, len, &str);
518 if (err != DOM_NO_ERR)
519 goto fail;
520
521 if (snprintf(numbuffer, 32, "%u", value) == 32)
522 numbuffer[31] = '\0';
523
524 err = dom_string_create((const uint8_t *) numbuffer,
525 strlen(numbuffer), &svalue);
526 if (err != DOM_NO_ERR)
527 goto cleanup;
528
529 err = dom_element_set_attribute(ele, str, svalue);
530
531 dom_string_unref(svalue);
532 cleanup:
533 dom_string_unref(str);
534
535 fail:
536 return err;
537 }
538
539 /**
540 * Get the a dom_ulong property
541 *
542 * \param ele The dom_html_element object
543 * \param name The name of the attribute
544 * \param len The length of ::name
545 * \param value The returned value, or -1 if prop. not set
546 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
547 */
dom_html_element_get_dom_ulong_property(dom_html_element * ele,const char * name,uint32_t len,dom_ulong * value)548 dom_exception dom_html_element_get_dom_ulong_property(dom_html_element *ele,
549 const char *name, uint32_t len, dom_ulong *value)
550 {
551 dom_string *str = NULL, *s2 = NULL;
552 dom_attr *a = NULL;
553 dom_exception err;
554
555 err = dom_string_create((const uint8_t *) name, len, &str);
556 if (err != DOM_NO_ERR)
557 goto fail;
558
559 err = dom_element_get_attribute_node(ele, str, &a);
560 if (err != DOM_NO_ERR)
561 goto cleanup1;
562
563 if (a != NULL) {
564 err = dom_node_get_text_content(a, &s2);
565 if (err == DOM_NO_ERR) {
566 char *s3 = _strndup(dom_string_data(s2),
567 dom_string_byte_length(s2));
568 if (s3 != NULL) {
569 *value = strtoul(s3, NULL, 0);
570 free(s3);
571 } else {
572 err = DOM_NO_MEM_ERR;
573 }
574 dom_string_unref(s2);
575 }
576 } else {
577 /* Property is not set on this node */
578 *value = -1;
579 }
580
581 dom_node_unref(a);
582
583 cleanup1:
584 dom_string_unref(str);
585
586 fail:
587 return err;
588 }
589
590 /**
591 * Set a dom_ulong property
592 *
593 * \param ele The dom_html_element object
594 * \param name The name of the attribute
595 * \param len The length of ::name
596 * \param value The value
597 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
598 */
dom_html_element_set_dom_ulong_property(dom_html_element * ele,const char * name,uint32_t len,dom_ulong value)599 dom_exception dom_html_element_set_dom_ulong_property(dom_html_element *ele,
600 const char *name, uint32_t len, dom_ulong value)
601 {
602 dom_string *str = NULL, *svalue = NULL;
603 dom_exception err;
604 char numbuffer[32];
605
606 err = dom_string_create((const uint8_t *) name, len, &str);
607 if (err != DOM_NO_ERR)
608 goto fail;
609
610 if (snprintf(numbuffer, 32, "%u", value) == 32)
611 numbuffer[31] = '\0';
612
613 err = dom_string_create((const uint8_t *) numbuffer,
614 strlen(numbuffer), &svalue);
615 if (err != DOM_NO_ERR)
616 goto cleanup;
617
618 err = dom_element_set_attribute(ele, str, svalue);
619
620 dom_string_unref(svalue);
621 cleanup:
622 dom_string_unref(str);
623
624 fail:
625 return err;
626 }
627