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(&params->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