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 2007 John-Mark Bell <jmb@netsurf-browser.org>
6  * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
7  */
8 
9 #include <assert.h>
10 #include <stdlib.h>
11 
12 #include <dom/functypes.h>
13 #include <dom/core/attr.h>
14 #include <dom/core/element.h>
15 #include <dom/core/document.h>
16 #include <dom/core/implementation.h>
17 
18 #include "core/string.h"
19 #include "core/attr.h"
20 #include "core/cdatasection.h"
21 #include "core/comment.h"
22 #include "core/document.h"
23 #include "core/doc_fragment.h"
24 #include "core/element.h"
25 #include "core/entity_ref.h"
26 #include "core/namednodemap.h"
27 #include "core/nodelist.h"
28 #include "core/pi.h"
29 #include "core/text.h"
30 #include "utils/validate.h"
31 #include "utils/namespace.h"
32 #include "utils/utils.h"
33 
34 /**
35  * Item in list of active nodelists
36  */
37 struct dom_doc_nl {
38 	dom_nodelist *list;	/**< Nodelist */
39 
40 	struct dom_doc_nl *next;	/**< Next item */
41 	struct dom_doc_nl *prev;	/**< Previous item */
42 };
43 
44 /* The virtual functions of this dom_document */
45 static struct dom_document_vtable document_vtable = {
46 	{
47 		{
48 			DOM_NODE_EVENT_TARGET_VTABLE
49 		},
50 		DOM_NODE_VTABLE_DOCUMENT
51 	},
52 	DOM_DOCUMENT_VTABLE
53 };
54 
55 static struct dom_node_protect_vtable document_protect_vtable = {
56 	DOM_DOCUMENT_PROTECT_VTABLE
57 };
58 
59 
60 /*----------------------------------------------------------------------*/
61 
62 /* Internally used helper functions */
63 static dom_exception dom_document_dup_node(dom_document *doc,
64 		dom_node *node, bool deep, dom_node **result,
65 		dom_node_operation opt);
66 
67 
68 /*----------------------------------------------------------------------*/
69 
70 /* The constructors and destructors */
71 
72 /**
73  * Create a Document
74  *
75  * \param doc    Pointer to location to receive created document
76  * \param daf    The default action fetcher
77  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion.
78  *
79  * The returned document will already be referenced.
80  */
_dom_document_create(dom_events_default_action_fetcher daf,void * daf_ctx,dom_document ** doc)81 dom_exception _dom_document_create(dom_events_default_action_fetcher daf,
82 				   void *daf_ctx,
83 				   dom_document **doc)
84 {
85 	dom_document *d;
86 	dom_exception err;
87 
88 	/* Create document */
89 	d = malloc(sizeof(dom_document));
90 	if (d == NULL)
91 		return DOM_NO_MEM_ERR;
92 
93 	/* Initialise the virtual table */
94 	d->base.base.vtable = &document_vtable;
95 	d->base.vtable = &document_protect_vtable;
96 
97 	/* Initialise base class -- the Document has no parent, so
98 	 * destruction will be attempted as soon as its reference count
99 	 * reaches zero. Documents own themselves (this simplifies the
100 	 * rest of the code, as it doesn't need to special case Documents)
101 	 */
102 	err = _dom_document_initialise(d, daf, daf_ctx);
103 	if (err != DOM_NO_ERR) {
104 		/* Clean up document */
105 		free(d);
106 		return err;
107 	}
108 
109 	*doc = d;
110 
111 	return DOM_NO_ERR;
112 }
113 
114 /* Initialise the document */
_dom_document_initialise(dom_document * doc,dom_events_default_action_fetcher daf,void * daf_ctx)115 dom_exception _dom_document_initialise(dom_document *doc,
116 				       dom_events_default_action_fetcher daf,
117 				       void *daf_ctx)
118 {
119 	dom_exception err;
120 	dom_string *name;
121 
122 	err = dom_string_create((const uint8_t *) "#document",
123 			SLEN("#document"), &name);
124 	if (err != DOM_NO_ERR)
125 		return err;
126 
127 	err = dom_string_create_interned((const uint8_t *) "about:blank",
128 			SLEN("about:blank"), &doc->uri);
129 	if (err != DOM_NO_ERR) {
130 		dom_string_unref(name);
131 		return err;
132 	}
133 
134 	doc->nodelists = NULL;
135 
136 	err = _dom_node_initialise(&doc->base, doc, DOM_DOCUMENT_NODE,
137 			name, NULL, NULL, NULL);
138 	dom_string_unref(name);
139 	if (err != DOM_NO_ERR) {
140 		dom_string_unref(doc->uri);
141 		return err;
142 	}
143 
144 	list_init(&doc->pending_nodes);
145 
146 	err = dom_string_create_interned((const uint8_t *) "id",
147 					 SLEN("id"), &doc->id_name);
148 	if (err != DOM_NO_ERR) {
149 		dom_string_unref(doc->uri);
150 		return err;
151 	}
152 	doc->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE;
153 
154 	err = dom_string_create_interned((const uint8_t *) "class",
155 			SLEN("class"), &doc->class_string);
156 	if (err != DOM_NO_ERR) {
157 		dom_string_unref(doc->uri);
158 		dom_string_unref(doc->id_name);
159 		return err;
160 	}
161 
162 	err = dom_string_create_interned((const uint8_t *) "script",
163 			SLEN("script"), &doc->script_string);
164 	if (err != DOM_NO_ERR) {
165 		dom_string_unref(doc->uri);
166 		dom_string_unref(doc->id_name);
167 		dom_string_unref(doc->class_string);
168 		return err;
169 	}
170 
171 	/* Intern the empty string. The use of a space in the constant
172 	 * is to prevent the compiler warning about an empty string.
173 	 */
174 	err = dom_string_create_interned((const uint8_t *) " ", 0,
175 					 &doc->_memo_empty);
176 	if (err != DOM_NO_ERR) {
177 		dom_string_unref(doc->uri);
178 		dom_string_unref(doc->id_name);
179 		dom_string_unref(doc->class_string);
180 		dom_string_unref(doc->script_string);
181 		return err;
182 	}
183 
184 	err = dom_string_create_interned((const uint8_t *) "DOMNodeInserted",
185 					 SLEN("DOMNodeInserted"),
186 					 &doc->_memo_domnodeinserted);
187 	if (err != DOM_NO_ERR) {
188 		dom_string_unref(doc->_memo_empty);
189 		dom_string_unref(doc->uri);
190 		dom_string_unref(doc->id_name);
191 		dom_string_unref(doc->class_string);
192 		dom_string_unref(doc->script_string);
193 		return err;
194 	}
195 
196 	err = dom_string_create_interned((const uint8_t *) "DOMNodeRemoved",
197 					 SLEN("DOMNodeRemoved"),
198 					 &doc->_memo_domnoderemoved);
199 	if (err != DOM_NO_ERR) {
200 		dom_string_unref(doc->_memo_domnodeinserted);
201 		dom_string_unref(doc->_memo_empty);
202 		dom_string_unref(doc->uri);
203 		dom_string_unref(doc->id_name);
204 		dom_string_unref(doc->class_string);
205 		dom_string_unref(doc->script_string);
206 		return err;
207 	}
208 
209 	err = dom_string_create_interned((const uint8_t *) "DOMNodeInsertedIntoDocument",
210 					 SLEN("DOMNodeInsertedIntoDocument"),
211 					 &doc->_memo_domnodeinsertedintodocument);
212 	if (err != DOM_NO_ERR) {
213 		dom_string_unref(doc->_memo_domnoderemoved);
214 		dom_string_unref(doc->_memo_domnodeinserted);
215 		dom_string_unref(doc->_memo_empty);
216 		dom_string_unref(doc->uri);
217 		dom_string_unref(doc->id_name);
218 		dom_string_unref(doc->class_string);
219 		dom_string_unref(doc->script_string);
220 		return err;
221 	}
222 
223 	err = dom_string_create_interned((const uint8_t *) "DOMNodeRemovedFromDocument",
224 					 SLEN("DOMNodeRemovedFromDocument"),
225 					 &doc->_memo_domnoderemovedfromdocument);
226 	if (err != DOM_NO_ERR) {
227 		dom_string_unref(doc->_memo_domnodeinsertedintodocument);
228 		dom_string_unref(doc->_memo_domnoderemoved);
229 		dom_string_unref(doc->_memo_domnodeinserted);
230 		dom_string_unref(doc->_memo_empty);
231 		dom_string_unref(doc->uri);
232 		dom_string_unref(doc->id_name);
233 		dom_string_unref(doc->class_string);
234 		dom_string_unref(doc->script_string);
235 		return err;
236 	}
237 
238 	err = dom_string_create_interned((const uint8_t *) "DOMAttrModified",
239 					 SLEN("DOMAttrModified"),
240 					 &doc->_memo_domattrmodified);
241 	if (err != DOM_NO_ERR) {
242 		dom_string_unref(doc->_memo_domnoderemovedfromdocument);
243 		dom_string_unref(doc->_memo_domnodeinsertedintodocument);
244 		dom_string_unref(doc->_memo_domnoderemoved);
245 		dom_string_unref(doc->_memo_domnodeinserted);
246 		dom_string_unref(doc->_memo_empty);
247 		dom_string_unref(doc->uri);
248 		dom_string_unref(doc->id_name);
249 		dom_string_unref(doc->class_string);
250 		dom_string_unref(doc->script_string);
251 		return err;
252 	}
253 
254 	err = dom_string_create_interned((const uint8_t *) "DOMCharacterDataModified",
255 					 SLEN("DOMCharacterDataModified"),
256 					 &doc->_memo_domcharacterdatamodified);
257 	if (err != DOM_NO_ERR) {
258 		dom_string_unref(doc->_memo_domattrmodified);
259 		dom_string_unref(doc->_memo_domnoderemovedfromdocument);
260 		dom_string_unref(doc->_memo_domnodeinsertedintodocument);
261 		dom_string_unref(doc->_memo_domnoderemoved);
262 		dom_string_unref(doc->_memo_domnodeinserted);
263 		dom_string_unref(doc->_memo_empty);
264 		dom_string_unref(doc->uri);
265 		dom_string_unref(doc->id_name);
266 		dom_string_unref(doc->class_string);
267 		dom_string_unref(doc->script_string);
268 		return err;
269 	}
270 
271 	err = dom_string_create_interned((const uint8_t *) "DOMSubtreeModified",
272 					 SLEN("DOMSubtreeModified"),
273 					 &doc->_memo_domsubtreemodified);
274 	if (err != DOM_NO_ERR) {
275 		dom_string_unref(doc->_memo_domcharacterdatamodified);
276 		dom_string_unref(doc->_memo_domattrmodified);
277 		dom_string_unref(doc->_memo_domnoderemovedfromdocument);
278 		dom_string_unref(doc->_memo_domnodeinsertedintodocument);
279 		dom_string_unref(doc->_memo_domnoderemoved);
280 		dom_string_unref(doc->_memo_domnodeinserted);
281 		dom_string_unref(doc->_memo_empty);
282 		dom_string_unref(doc->uri);
283 		dom_string_unref(doc->id_name);
284 		dom_string_unref(doc->class_string);
285 		dom_string_unref(doc->script_string);
286 		return err;
287 	}
288 
289 	/* We should not pass a NULL when all things hook up */
290 	return _dom_document_event_internal_initialise(&doc->dei, daf, daf_ctx);
291 }
292 
293 
294 /* Finalise the document */
_dom_document_finalise(dom_document * doc)295 bool _dom_document_finalise(dom_document *doc)
296 {
297 	/* Finalise base class, delete the tree in force */
298 	_dom_node_finalise(&doc->base);
299 
300 	/* Now, the first_child and last_child should be null */
301 	doc->base.first_child = NULL;
302 	doc->base.last_child = NULL;
303 
304 	/* Ensure list of nodes pending deletion is empty. If not,
305 	 * then we can't yet destroy the document (its destruction will
306 	 * have to wait until the pending nodes are destroyed) */
307 	if (doc->pending_nodes.next != &doc->pending_nodes)
308 		return false;
309 
310 	/* Ok, the document tree is empty, as is the list of nodes pending
311 	 * deletion. Therefore, it is safe to destroy the document. */
312 
313 	/* This is paranoia -- if there are any remaining nodelists,
314 	 * then the document's reference count will be
315 	 * non-zero as these data structures reference the document because
316 	 * they are held by the client. */
317 	doc->nodelists = NULL;
318 
319 	if (doc->id_name != NULL)
320 		dom_string_unref(doc->id_name);
321 
322 	dom_string_unref(doc->uri);
323 	dom_string_unref(doc->class_string);
324 	dom_string_unref(doc->script_string);
325 	dom_string_unref(doc->_memo_empty);
326 	dom_string_unref(doc->_memo_domnodeinserted);
327 	dom_string_unref(doc->_memo_domnoderemoved);
328 	dom_string_unref(doc->_memo_domnodeinsertedintodocument);
329 	dom_string_unref(doc->_memo_domnoderemovedfromdocument);
330 	dom_string_unref(doc->_memo_domattrmodified);
331 	dom_string_unref(doc->_memo_domcharacterdatamodified);
332 	dom_string_unref(doc->_memo_domsubtreemodified);
333 
334 	_dom_document_event_internal_finalise(&doc->dei);
335 
336 	return true;
337 }
338 
339 
340 
341 /*----------------------------------------------------------------------*/
342 
343 /* Public virtual functions */
344 
345 /**
346  * Retrieve the doctype of a document
347  *
348  * \param doc     The document to retrieve the doctype from
349  * \param result  Pointer to location to receive result
350  * \return DOM_NO_ERR.
351  *
352  * The returned node will have its reference count increased. It is
353  * the responsibility of the caller to unref the node once it has
354  * finished with it.
355  */
_dom_document_get_doctype(dom_document * doc,dom_document_type ** result)356 dom_exception _dom_document_get_doctype(dom_document *doc,
357 		dom_document_type **result)
358 {
359 	dom_node_internal *c;
360 
361 	for (c = doc->base.first_child; c != NULL; c = c->next) {
362 		if (c->type == DOM_DOCUMENT_TYPE_NODE)
363 			break;
364 	}
365 
366 	if (c != NULL)
367 		dom_node_ref(c);
368 
369 	*result = (dom_document_type *) c;
370 
371 	return DOM_NO_ERR;
372 }
373 
374 /**
375  * Retrieve the DOM implementation that handles this document
376  *
377  * \param doc     The document to retrieve the implementation from
378  * \param result  Pointer to location to receive result
379  * \return DOM_NO_ERR.
380  *
381  * The returned implementation will have its reference count increased.
382  * It is the responsibility of the caller to unref the implementation once
383  * it has finished with it.
384  */
_dom_document_get_implementation(dom_document * doc,dom_implementation ** result)385 dom_exception _dom_document_get_implementation(dom_document *doc,
386 		dom_implementation **result)
387 {
388 	UNUSED(doc);
389 
390 	*result = (dom_implementation *) "libdom";
391 
392 	return DOM_NO_ERR;
393 }
394 
395 /**
396  * Retrieve the document element of a document
397  *
398  * \param doc     The document to retrieve the document element from
399  * \param result  Pointer to location to receive result
400  * \return DOM_NO_ERR.
401  *
402  * The returned node will have its reference count increased. It is
403  * the responsibility of the caller to unref the node once it has
404  * finished with it.
405  */
_dom_document_get_document_element(dom_document * doc,dom_element ** result)406 dom_exception _dom_document_get_document_element(dom_document *doc,
407 		dom_element **result)
408 {
409 	dom_node_internal *root;
410 
411 	/* Find the first element node in child list */
412 	for (root = doc->base.first_child; root != NULL; root = root->next) {
413 		if (root->type == DOM_ELEMENT_NODE)
414 			break;
415 	}
416 
417 	if (root != NULL)
418 		dom_node_ref(root);
419 
420 	*result = (dom_element *) root;
421 
422 	return DOM_NO_ERR;
423 }
424 
425 /**
426  * Create an element
427  *
428  * \param doc       The document owning the element
429  * \param tag_name  The name of the element
430  * \param result    Pointer to location to receive result
431  * \return DOM_NO_ERR                on success,
432  *         DOM_INVALID_CHARACTER_ERR if \p tag_name is invalid.
433  *
434  * \p doc and \p tag_name will have their reference counts increased.
435  *
436  * The returned node will have its reference count increased. It is
437  * the responsibility of the caller to unref the node once it has
438  * finished with it.
439  */
_dom_document_create_element(dom_document * doc,dom_string * tag_name,dom_element ** result)440 dom_exception _dom_document_create_element(dom_document *doc,
441 		dom_string *tag_name, dom_element **result)
442 {
443 	if (_dom_validate_name(tag_name) == false)
444 		return DOM_INVALID_CHARACTER_ERR;
445 
446 	return _dom_element_create(doc, tag_name, NULL, NULL, result);
447 }
448 
449 /**
450  * Create a document fragment
451  *
452  * \param doc     The document owning the fragment
453  * \param result  Pointer to location to receive result
454  * \return DOM_NO_ERR.
455  *
456  * The returned node will have its reference count increased. It is
457  * the responsibility of the caller to unref the node once it has
458  * finished with it.
459  */
_dom_document_create_document_fragment(dom_document * doc,dom_document_fragment ** result)460 dom_exception _dom_document_create_document_fragment(dom_document *doc,
461 		dom_document_fragment **result)
462 {
463 	dom_string *name;
464 	dom_exception err;
465 
466 	err = dom_string_create((const uint8_t *) "#document-fragment",
467 			SLEN("#document-fragment"), &name);
468 	if (err != DOM_NO_ERR)
469 		return err;
470 
471 	err = _dom_document_fragment_create(doc, name, NULL, result);
472 	dom_string_unref(name);
473 
474 	return err;
475 }
476 
477 /**
478  * Create a text node
479  *
480  * \param doc     The document owning the node
481  * \param data    The data for the node
482  * \param result  Pointer to location to receive result
483  * \return DOM_NO_ERR.
484  *
485  * The returned node will have its reference count increased. It is
486  * the responsibility of the caller to unref the node once it has
487  * finished with it.
488  */
_dom_document_create_text_node(dom_document * doc,dom_string * data,dom_text ** result)489 dom_exception _dom_document_create_text_node(dom_document *doc,
490 		dom_string *data, dom_text **result)
491 {
492 	dom_string *name;
493 	dom_exception err;
494 
495 	err = dom_string_create((const uint8_t *) "#text",
496 			SLEN("#text"), &name);
497 	if (err != DOM_NO_ERR)
498 		return err;
499 
500 	err = _dom_text_create(doc, name, data, result);
501 	dom_string_unref(name);
502 
503 	return err;
504 }
505 
506 /**
507  * Create a comment node
508  *
509  * \param doc     The document owning the node
510  * \param data    The data for the node
511  * \param result  Pointer to location to receive result
512  * \return DOM_NO_ERR.
513  *
514  * The returned node will have its reference count increased. It is
515  * the responsibility of the caller to unref the node once it has
516  * finished with it.
517  */
_dom_document_create_comment(dom_document * doc,dom_string * data,dom_comment ** result)518 dom_exception _dom_document_create_comment(dom_document *doc,
519 		dom_string *data, dom_comment **result)
520 {
521 	dom_string *name;
522 	dom_exception err;
523 
524 	err = dom_string_create((const uint8_t *) "#comment", SLEN("#comment"),
525 			&name);
526 	if (err != DOM_NO_ERR)
527 		return err;
528 
529 	err = _dom_comment_create(doc, name, data, result);
530 	dom_string_unref(name);
531 
532 	return err;
533 }
534 
535 /**
536  * Create a CDATA section
537  *
538  * \param doc     The document owning the section
539  * \param data    The data for the section contents
540  * \param result  Pointer to location to receive result
541  * \return DOM_NO_ERR            on success,
542  *         DOM_NOT_SUPPORTED_ERR if this is an HTML document.
543  *
544  * The returned node will have its reference count increased. It is
545  * the responsibility of the caller to unref the node once it has
546  * finished with it.
547  */
_dom_document_create_cdata_section(dom_document * doc,dom_string * data,dom_cdata_section ** result)548 dom_exception _dom_document_create_cdata_section(dom_document *doc,
549 		dom_string *data, dom_cdata_section **result)
550 {
551 	dom_string *name;
552 	dom_exception err;
553 
554 	err = dom_string_create((const uint8_t *) "#cdata-section",
555 			SLEN("#cdata-section"), &name);
556 	if (err != DOM_NO_ERR)
557 		return err;
558 
559 	err = _dom_cdata_section_create(doc, name, data, result);
560 	dom_string_unref(name);
561 
562 	return err;
563 }
564 
565 /**
566  * Create a processing instruction
567  *
568  * \param doc     The document owning the instruction
569  * \param target  The instruction target
570  * \param data    The data for the node
571  * \param result  Pointer to location to receive result
572  * \return DOM_NO_ERR                on success,
573  *         DOM_INVALID_CHARACTER_ERR if \p target is invalid,
574  *         DOM_NOT_SUPPORTED_ERR     if this is an HTML document.
575  *
576  * The returned node will have its reference count increased. It is
577  * the responsibility of the caller to unref the node once it has
578  * finished with it.
579  */
_dom_document_create_processing_instruction(dom_document * doc,dom_string * target,dom_string * data,dom_processing_instruction ** result)580 dom_exception _dom_document_create_processing_instruction(
581 		dom_document *doc, dom_string *target,
582 		dom_string *data,
583 		dom_processing_instruction **result)
584 {
585 	if (_dom_validate_name(target) == false)
586 		return DOM_INVALID_CHARACTER_ERR;
587 
588 	return _dom_processing_instruction_create(doc, target, data, result);
589 }
590 
591 /**
592  * Create an attribute
593  *
594  * \param doc     The document owning the attribute
595  * \param name    The name of the attribute
596  * \param result  Pointer to location to receive result
597  * \return DOM_NO_ERR                on success,
598  *         DOM_INVALID_CHARACTER_ERR if \p name is invalid.
599  *
600  * The constructed attribute will always be classified as 'specified'.
601  *
602  * The returned node will have its reference count increased. It is
603  * the responsibility of the caller to unref the node once it has
604  * finished with it.
605  */
_dom_document_create_attribute(dom_document * doc,dom_string * name,dom_attr ** result)606 dom_exception _dom_document_create_attribute(dom_document *doc,
607 		dom_string *name, dom_attr **result)
608 {
609 	if (_dom_validate_name(name) == false)
610 		return DOM_INVALID_CHARACTER_ERR;
611 
612 	return _dom_attr_create(doc, name, NULL, NULL, true, result);
613 }
614 
615 /**
616  * Create an entity reference
617  *
618  * \param doc     The document owning the reference
619  * \param name    The name of the entity to reference
620  * \param result  Pointer to location to receive result
621  * \return DOM_NO_ERR                on success,
622  *         DOM_INVALID_CHARACTER_ERR if \p name is invalid,
623  *         DOM_NOT_SUPPORTED_ERR     if this is an HTML document.
624  *
625  * The returned node will have its reference count increased. It is
626  * the responsibility of the caller to unref the node once it has
627  * finished with it.
628  */
_dom_document_create_entity_reference(dom_document * doc,dom_string * name,dom_entity_reference ** result)629 dom_exception _dom_document_create_entity_reference(dom_document *doc,
630 		dom_string *name,
631 		dom_entity_reference **result)
632 {
633 	if (_dom_validate_name(name) == false)
634 		return DOM_INVALID_CHARACTER_ERR;
635 
636 	return _dom_entity_reference_create(doc, name, NULL, result);
637 }
638 
639 /**
640  * Retrieve a list of all elements with a given tag name
641  *
642  * \param doc      The document to search in
643  * \param tagname  The tag name to search for ("*" for all)
644  * \param result   Pointer to location to receive result
645  * \return DOM_NO_ERR.
646  *
647  * The returned list will have its reference count increased. It is
648  * the responsibility of the caller to unref the list once it has
649  * finished with it.
650  */
_dom_document_get_elements_by_tag_name(dom_document * doc,dom_string * tagname,dom_nodelist ** result)651 dom_exception _dom_document_get_elements_by_tag_name(dom_document *doc,
652 		dom_string *tagname, dom_nodelist **result)
653 {
654 	return _dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAME,
655 			(dom_node_internal *) doc,  tagname, NULL, NULL,
656 			result);
657 }
658 
659 /**
660  * Import a node from another document into this one
661  *
662  * \param doc     The document to import into
663  * \param node    The node to import
664  * \param deep    Whether to copy the node's subtree
665  * \param result  Pointer to location to receive imported node in this document.
666  * \return DOM_NO_ERR                on success,
667  *         DOM_INVALID_CHARACTER_ERR if any of the names are invalid,
668  *         DOM_NOT_SUPPORTED_ERR     if the type of \p node is unsupported
669  *
670  * The returned node will have its reference count increased. It is
671  * the responsibility of the caller to unref the node once it has
672  * finished with it.
673  */
_dom_document_import_node(dom_document * doc,dom_node * node,bool deep,dom_node ** result)674 dom_exception _dom_document_import_node(dom_document *doc,
675 		dom_node *node, bool deep, dom_node **result)
676 {
677 	/* TODO: The DOM_INVALID_CHARACTER_ERR exception */
678 
679 	return dom_document_dup_node(doc, node, deep, result,
680 			DOM_NODE_IMPORTED);
681 }
682 
683 /**
684  * Create an element from the qualified name and namespace URI
685  *
686  * \param doc        The document owning the element
687  * \param namespace  The namespace URI to use, or NULL for none
688  * \param qname      The qualified name of the element
689  * \param result     Pointer to location to receive result
690  * \return DOM_NO_ERR                on success,
691  *         DOM_INVALID_CHARACTER_ERR if \p qname is invalid,
692  *         DOM_NAMESPACE_ERR         if \p qname is malformed, or it has a
693  *                                   prefix and \p namespace is NULL, or
694  *                                   \p qname has a prefix "xml" and
695  *                                   \p namespace is not
696  *                                   "http://www.w3.org/XML/1998/namespace",
697  *                                   or \p qname has a prefix "xmlns" and
698  *                                   \p namespace is not
699  *                                   "http://www.w3.org/2000/xmlns", or
700  *                                   \p namespace is
701  *                                   "http://www.w3.org/2000/xmlns" and
702  *                                   \p qname is not (or is not prefixed by)
703  *                                   "xmlns",
704  *         DOM_NOT_SUPPORTED_ERR     if \p doc does not support the "XML"
705  *                                   feature.
706  *
707  * The returned node will have its reference count increased. It is
708  * the responsibility of the caller to unref the node once it has
709  * finished with it.
710  */
_dom_document_create_element_ns(dom_document * doc,dom_string * namespace,dom_string * qname,dom_element ** result)711 dom_exception _dom_document_create_element_ns(dom_document *doc,
712 		dom_string *namespace, dom_string *qname,
713 		dom_element **result)
714 {
715 	dom_string *prefix, *localname;
716 	dom_exception err;
717 
718 	if (_dom_validate_name(qname) == false)
719 		return DOM_INVALID_CHARACTER_ERR;
720 
721 	/* Validate qname */
722 	err = _dom_namespace_validate_qname(qname, namespace);
723 	if (err != DOM_NO_ERR) {
724 		return err;
725 	}
726 
727 	/* Divide QName into prefix/localname pair */
728 	err = _dom_namespace_split_qname(qname, &prefix, &localname);
729 	if (err != DOM_NO_ERR) {
730 		return err;
731 	}
732 
733 	/* Attempt to create element */
734 	err = _dom_element_create(doc, localname, namespace, prefix, result);
735 
736 	/* Tidy up */
737 	if (localname != NULL) {
738 		dom_string_unref(localname);
739 	}
740 
741 	if (prefix != NULL) {
742 		dom_string_unref(prefix);
743 	}
744 
745 	return err;
746 }
747 
748 /**
749  * Create an attribute from the qualified name and namespace URI
750  *
751  * \param doc        The document owning the attribute
752  * \param namespace  The namespace URI to use
753  * \param qname      The qualified name of the attribute
754  * \param result     Pointer to location to receive result
755  * \return DOM_NO_ERR                on success,
756  *         DOM_INVALID_CHARACTER_ERR if \p qname is invalid,
757  *         DOM_NAMESPACE_ERR         if \p qname is malformed, or it has a
758  *                                   prefix and \p namespace is NULL, or
759  *                                   \p qname has a prefix "xml" and
760  *                                   \p namespace is not
761  *                                   "http://www.w3.org/XML/1998/namespace",
762  *                                   or \p qname has a prefix "xmlns" and
763  *                                   \p namespace is not
764  *                                   "http://www.w3.org/2000/xmlns", or
765  *                                   \p namespace is
766  *                                   "http://www.w3.org/2000/xmlns" and
767  *                                   \p qname is not (or is not prefixed by)
768  *                                   "xmlns",
769  *         DOM_NOT_SUPPORTED_ERR     if \p doc does not support the "XML"
770  *                                   feature.
771  *
772  * The returned node will have its reference count increased. It is
773  * the responsibility of the caller to unref the node once it has
774  * finished with it.
775  */
_dom_document_create_attribute_ns(dom_document * doc,dom_string * namespace,dom_string * qname,dom_attr ** result)776 dom_exception _dom_document_create_attribute_ns(dom_document *doc,
777 		dom_string *namespace, dom_string *qname,
778 		dom_attr **result)
779 {
780 	dom_string *prefix, *localname;
781 	dom_exception err;
782 
783 	if (_dom_validate_name(qname) == false)
784 		return DOM_INVALID_CHARACTER_ERR;
785 
786 	/* Validate qname */
787 	err = _dom_namespace_validate_qname(qname, namespace);
788 	if (err != DOM_NO_ERR) {
789 		return err;
790 	}
791 
792 	/* Divide QName into prefix/localname pair */
793 	err = _dom_namespace_split_qname(qname, &prefix, &localname);
794 	if (err != DOM_NO_ERR) {
795 		return err;
796 	}
797 
798 	/* Attempt to create attribute */
799 	err = _dom_attr_create(doc, localname, namespace, prefix, true, result);
800 
801 	/* Tidy up */
802 	if (localname != NULL) {
803 		dom_string_unref(localname);
804 	}
805 
806 	if (prefix != NULL) {
807 		dom_string_unref(prefix);
808 	}
809 
810 	return err;
811 }
812 
813 /**
814  * Retrieve a list of all elements with a given local name and namespace URI
815  *
816  * \param doc        The document to search in
817  * \param namespace  The namespace URI
818  * \param localname  The local name
819  * \param result     Pointer to location to receive result
820  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
821  *
822  * The returned list will have its reference count increased. It is
823  * the responsibility of the caller to unref the list once it has
824  * finished with it.
825  */
_dom_document_get_elements_by_tag_name_ns(dom_document * doc,dom_string * namespace,dom_string * localname,dom_nodelist ** result)826 dom_exception _dom_document_get_elements_by_tag_name_ns(
827 		dom_document *doc, dom_string *namespace,
828 		dom_string *localname, dom_nodelist **result)
829 {
830 	return _dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAMESPACE,
831 			(dom_node_internal *) doc, NULL, namespace, localname,
832 			result);
833 }
834 
835 /**
836  * Retrieve the element that matches the specified ID
837  *
838  * \param doc     The document to search in
839  * \param id      The ID to search for
840  * \param result  Pointer to location to receive result
841  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
842  *
843  * The returned node will have its reference count increased. It is
844  * the responsibility of the caller to unref the node once it has
845  * finished with it.
846  */
_dom_document_get_element_by_id(dom_document * doc,dom_string * id,dom_element ** result)847 dom_exception _dom_document_get_element_by_id(dom_document *doc,
848 		dom_string *id, dom_element **result)
849 {
850 	dom_node_internal *root;
851 	dom_exception err;
852 
853 	*result = NULL;
854 
855 	err = dom_document_get_document_element(doc, (void *) &root);
856 	if (err != DOM_NO_ERR)
857 		return err;
858 
859 	err = _dom_find_element_by_id(root, id, result);
860 	dom_node_unref(root);
861 
862 	if (*result != NULL)
863 		dom_node_ref(*result);
864 
865 	return err;
866 }
867 
868 /**
869  * Retrieve the input encoding of the document
870  *
871  * \param doc     The document to query
872  * \param result  Pointer to location to receive result
873  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
874  *
875  * The returned string will have its reference count increased. It is
876  * the responsibility of the caller to unref the string once it has
877  * finished with it.
878  */
_dom_document_get_input_encoding(dom_document * doc,dom_string ** result)879 dom_exception _dom_document_get_input_encoding(dom_document *doc,
880 		dom_string **result)
881 {
882 	UNUSED(doc);
883 	UNUSED(result);
884 
885 	return DOM_NOT_SUPPORTED_ERR;
886 }
887 
888 /**
889  * Retrieve the XML encoding of the document
890  *
891  * \param doc     The document to query
892  * \param result  Pointer to location to receive result
893  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
894  *
895  * The returned string will have its reference count increased. It is
896  * the responsibility of the caller to unref the string once it has
897  * finished with it.
898  */
_dom_document_get_xml_encoding(dom_document * doc,dom_string ** result)899 dom_exception _dom_document_get_xml_encoding(dom_document *doc,
900 		dom_string **result)
901 {
902 	UNUSED(doc);
903 	UNUSED(result);
904 
905 	return DOM_NOT_SUPPORTED_ERR;
906 }
907 
908 /**
909  * Retrieve the standalone status of the document
910  *
911  * \param doc     The document to query
912  * \param result  Pointer to location to receive result
913  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
914  */
_dom_document_get_xml_standalone(dom_document * doc,bool * result)915 dom_exception _dom_document_get_xml_standalone(dom_document *doc,
916 		bool *result)
917 {
918 	UNUSED(doc);
919 	UNUSED(result);
920 
921 	return DOM_NOT_SUPPORTED_ERR;
922 }
923 
924 /**
925  * Set the standalone status of the document
926  *
927  * \param doc         The document to query
928  * \param standalone  Standalone status to use
929  * \return DOM_NO_ERR            on success,
930  *         DOM_NOT_SUPPORTED_ERR if the document does not support the "XML"
931  *                               feature.
932  *
933  * We don't support this API now, so the return value is always
934  * DOM_NOT_SUPPORTED_ERR.
935  */
_dom_document_set_xml_standalone(dom_document * doc,bool standalone)936 dom_exception _dom_document_set_xml_standalone(dom_document *doc,
937 		bool standalone)
938 {
939 	UNUSED(doc);
940 	UNUSED(standalone);
941 
942 	return DOM_NOT_SUPPORTED_ERR;
943 }
944 
945 /**
946  * Retrieve the XML version of the document
947  *
948  * \param doc     The document to query
949  * \param result  Pointer to location to receive result
950  * \return DOM_NO_ERR
951  *
952  * The returned string will have its reference count increased. It is
953  * the responsibility of the caller to unref the string once it has
954  * finished with it.
955  *
956  * We don't support this API now, so the return value is always
957  * DOM_NOT_SUPPORTED_ERR.
958  */
_dom_document_get_xml_version(dom_document * doc,dom_string ** result)959 dom_exception _dom_document_get_xml_version(dom_document *doc,
960 		dom_string **result)
961 {
962 	UNUSED(doc);
963 	UNUSED(result);
964 
965 	return DOM_NOT_SUPPORTED_ERR;
966 }
967 
968 /**
969  * Set the XML version of the document
970  *
971  * \param doc      The document to query
972  * \param version  XML version to use
973  * \return DOM_NO_ERR            on success,
974  *         DOM_NOT_SUPPORTED_ERR if the document does not support the "XML"
975  *                               feature.
976  *
977  * We don't support this API now, so the return value is always
978  * DOM_NOT_SUPPORTED_ERR.
979  */
_dom_document_set_xml_version(dom_document * doc,dom_string * version)980 dom_exception _dom_document_set_xml_version(dom_document *doc,
981 		dom_string *version)
982 {
983 	UNUSED(doc);
984 	UNUSED(version);
985 
986 	return DOM_NOT_SUPPORTED_ERR;
987 }
988 
989 /**
990  * Retrieve the error checking mode of the document
991  *
992  * \param doc     The document to query
993  * \param result  Pointer to location to receive result
994  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
995  */
_dom_document_get_strict_error_checking(dom_document * doc,bool * result)996 dom_exception _dom_document_get_strict_error_checking(
997 		dom_document *doc, bool *result)
998 {
999 	UNUSED(doc);
1000 	UNUSED(result);
1001 
1002 	return DOM_NOT_SUPPORTED_ERR;
1003 }
1004 
1005 /**
1006  * Set the error checking mode of the document
1007  *
1008  * \param doc     The document to query
1009  * \param strict  Whether to use strict error checking
1010  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
1011  */
_dom_document_set_strict_error_checking(dom_document * doc,bool strict)1012 dom_exception _dom_document_set_strict_error_checking(
1013 		dom_document *doc, bool strict)
1014 {
1015 	UNUSED(doc);
1016 	UNUSED(strict);
1017 
1018 	return DOM_NOT_SUPPORTED_ERR;
1019 }
1020 
1021 /**
1022  * Retrieve the URI of the document
1023  *
1024  * \param doc     The document to query
1025  * \param result  Pointer to location to receive result
1026  * \return DOM_NO_ERR.
1027  *
1028  * The returned string will have its reference count increased. It is
1029  * the responsibility of the caller to unref the string once it has
1030  * finished with it.
1031  */
_dom_document_get_uri(dom_document * doc,dom_string ** result)1032 dom_exception _dom_document_get_uri(dom_document *doc,
1033 		dom_string **result)
1034 {
1035 	*result = dom_string_ref(doc->uri);
1036 
1037 	return DOM_NO_ERR;
1038 }
1039 
1040 /**
1041  * Set the URI of the document
1042  *
1043  * \param doc  The document to query
1044  * \param uri  The URI to use
1045  * \return DOM_NO_ERR.
1046  *
1047  * The returned string will have its reference count increased. It is
1048  * the responsibility of the caller to unref the string once it has
1049  * finished with it.
1050  */
_dom_document_set_uri(dom_document * doc,dom_string * uri)1051 dom_exception _dom_document_set_uri(dom_document *doc,
1052 		dom_string *uri)
1053 {
1054 	dom_string_unref(doc->uri);
1055 
1056 	doc->uri = dom_string_ref(uri);
1057 
1058 	return DOM_NO_ERR;
1059 }
1060 
1061 /**
1062  * Attempt to adopt a node from another document into this document
1063  *
1064  * \param doc     The document to adopt into
1065  * \param node    The node to adopt
1066  * \param result  Pointer to location to receive adopted node
1067  * \return DOM_NO_ERR                      on success,
1068  *         DOM_NO_MODIFICATION_ALLOWED_ERR if \p node is readonly,
1069  *         DOM_NOT_SUPPORTED_ERR           if \p node is of type Document or
1070  *                                         DocumentType
1071  *
1072  * The returned node will have its reference count increased. It is
1073  * the responsibility of the caller to unref the node once it has
1074  * finished with it.
1075  *
1076  * @note: The spec said adoptNode may be light weight than the importNode
1077  *	  because the former need no Node creation. But in our implementation
1078  *	  this can't be ensured. Both adoptNode and importNode create new
1079  *	  nodes using the importing/adopting document's resource manager. So,
1080  *	  generally, the adoptNode and importNode call the same function
1081  *	  dom_document_dup_node.
1082  */
_dom_document_adopt_node(dom_document * doc,dom_node * node,dom_node ** result)1083 dom_exception _dom_document_adopt_node(dom_document *doc,
1084 		dom_node *node, dom_node **result)
1085 {
1086 	dom_node_internal *n = (dom_node_internal *) node;
1087 	dom_exception err;
1088 	dom_node_internal *parent;
1089 	dom_node_internal *tmp;
1090 
1091 	*result = NULL;
1092 
1093 	if (n->type == DOM_DOCUMENT_NODE ||
1094 			n->type == DOM_DOCUMENT_TYPE_NODE) {
1095 		return DOM_NOT_SUPPORTED_ERR;
1096 	}
1097 
1098 	if (n->type == DOM_ENTITY_NODE ||
1099 			n->type == DOM_NOTATION_NODE ||
1100 			n->type == DOM_PROCESSING_INSTRUCTION_NODE ||
1101 			n->type == DOM_TEXT_NODE ||
1102 			n->type == DOM_CDATA_SECTION_NODE ||
1103 			n->type == DOM_COMMENT_NODE) {
1104 		*result = NULL;
1105 		return DOM_NO_ERR;
1106 	}
1107 
1108 	/* Support XML when necessary */
1109 	if (n->type == DOM_ENTITY_REFERENCE_NODE) {
1110 		return DOM_NOT_SUPPORTED_ERR;
1111 	}
1112 
1113 	err = dom_document_dup_node(doc, node, true, result, DOM_NODE_ADOPTED);
1114 	if (err != DOM_NO_ERR) {
1115 		*result = NULL;
1116 		return err;
1117 	}
1118 
1119 	parent = n->parent;
1120 	if (parent != NULL) {
1121 		err = dom_node_remove_child(parent, node, (void *) &tmp);
1122 		if (err != DOM_NO_ERR) {
1123 			dom_node_unref(*result);
1124 			*result = NULL;
1125 			return err;
1126 		}
1127                 dom_node_unref(tmp);
1128 	}
1129 
1130 	return DOM_NO_ERR;
1131 }
1132 
1133 /**
1134  * Retrieve the DOM configuration associated with a document
1135  *
1136  * \param doc     The document to query
1137  * \param result  Pointer to location to receive result
1138  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
1139  *
1140  * The returned object will have its reference count increased. It is
1141  * the responsibility of the caller to unref the object once it has
1142  * finished with it.
1143  */
_dom_document_get_dom_config(dom_document * doc,struct dom_configuration ** result)1144 dom_exception _dom_document_get_dom_config(dom_document *doc,
1145 		struct dom_configuration **result)
1146 {
1147 	UNUSED(doc);
1148 	UNUSED(result);
1149 
1150 	return DOM_NOT_SUPPORTED_ERR;
1151 }
1152 
1153 /**
1154  * Normalize a document
1155  *
1156  * \param doc  The document to normalize
1157  * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now.
1158  */
_dom_document_normalize(dom_document * doc)1159 dom_exception _dom_document_normalize(dom_document *doc)
1160 {
1161 	UNUSED(doc);
1162 
1163 	return DOM_NOT_SUPPORTED_ERR;
1164 }
1165 
1166 /**
1167  * Rename a node in a document
1168  *
1169  * \param doc        The document containing the node
1170  * \param node       The node to rename
1171  * \param namespace  The new namespace for the node
1172  * \param qname      The new qualified name for the node
1173  * \param result     Pointer to location to receive renamed node
1174  * \return DOM_NO_ERR                on success,
1175  *         DOM_INVALID_CHARACTER_ERR if \p tag_name is invalid,
1176  *         DOM_WRONG_DOCUMENT_ERR    if \p node was created in a different
1177  *                                   document
1178  *         DOM_NAMESPACE_ERR         if \p qname is malformed, or it has a
1179  *                                   prefix and \p namespace is NULL, or
1180  *                                   \p qname has a prefix "xml" and
1181  *                                   \p namespace is not
1182  *                                   "http://www.w3.org/XML/1998/namespace",
1183  *                                   or \p qname has a prefix "xmlns" and
1184  *                                   \p namespace is not
1185  *                                   "http://www.w3.org/2000/xmlns", or
1186  *                                   \p namespace is
1187  *                                   "http://www.w3.org/2000/xmlns" and
1188  *                                   \p qname is not (or is not prefixed by)
1189  *                                   "xmlns",
1190  *         DOM_NOT_SUPPORTED_ERR     if \p doc does not support the "XML"
1191  *                                   feature.
1192  *
1193  * The returned node will have its reference count increased. It is
1194  * the responsibility of the caller to unref the node once it has
1195  * finished with it.
1196  *
1197  * We don't support this API now, so the return value is always
1198  * DOM_NOT_SUPPORTED_ERR.
1199  */
_dom_document_rename_node(dom_document * doc,dom_node * node,dom_string * namespace,dom_string * qname,dom_node ** result)1200 dom_exception _dom_document_rename_node(dom_document *doc,
1201 		dom_node *node,
1202 		dom_string *namespace, dom_string *qname,
1203 		dom_node **result)
1204 {
1205 	UNUSED(doc);
1206 	UNUSED(node);
1207 	UNUSED(namespace);
1208 	UNUSED(qname);
1209 	UNUSED(result);
1210 
1211 	return DOM_NOT_SUPPORTED_ERR;
1212 }
1213 
_dom_document_get_text_content(dom_node_internal * node,dom_string ** result)1214 dom_exception _dom_document_get_text_content(dom_node_internal *node,
1215 					     dom_string **result)
1216 {
1217 	UNUSED(node);
1218 
1219 	*result = NULL;
1220 
1221 	return DOM_NO_ERR;
1222 }
1223 
_dom_document_set_text_content(dom_node_internal * node,dom_string * content)1224 dom_exception _dom_document_set_text_content(dom_node_internal *node,
1225 					     dom_string *content)
1226 {
1227 	UNUSED(node);
1228 	UNUSED(content);
1229 
1230 	return DOM_NO_ERR;
1231 }
1232 
1233 /*-----------------------------------------------------------------------*/
1234 
1235 /* Overload protected virtual functions */
1236 
1237 /* The virtual destroy function of this class */
_dom_document_destroy(dom_node_internal * node)1238 void _dom_document_destroy(dom_node_internal *node)
1239 {
1240 	dom_document *doc = (dom_document *) node;
1241 
1242 	if (_dom_document_finalise(doc) == true) {
1243 		free(doc);
1244 	}
1245 }
1246 
1247 /* The copy constructor function of this class */
_dom_document_copy(dom_node_internal * old,dom_node_internal ** copy)1248 dom_exception _dom_document_copy(dom_node_internal *old,
1249 		dom_node_internal **copy)
1250 {
1251 	UNUSED(old);
1252 	UNUSED(copy);
1253 
1254 	return DOM_NOT_SUPPORTED_ERR;
1255 }
1256 
1257 
1258 /* ----------------------------------------------------------------------- */
1259 
1260 /* Helper functions */
1261 
1262 /**
1263  * Get a nodelist, creating one if necessary
1264  *
1265  * \param doc        The document to get a nodelist for
1266  * \param type	     The type of the NodeList
1267  * \param root       Root node of subtree that list applies to
1268  * \param tagname    Name of nodes in list (or NULL)
1269  * \param namespace  Namespace part of nodes in list (or NULL)
1270  * \param localname  Local part of nodes in list (or NULL)
1271  * \param list       Pointer to location to receive list
1272  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
1273  *
1274  * The returned list will have its reference count increased. It is
1275  * the responsibility of the caller to unref the list once it has
1276  * finished with it.
1277  */
_dom_document_get_nodelist(dom_document * doc,nodelist_type type,dom_node_internal * root,dom_string * tagname,dom_string * namespace,dom_string * localname,dom_nodelist ** list)1278 dom_exception _dom_document_get_nodelist(dom_document *doc,
1279 		nodelist_type type, dom_node_internal *root,
1280 		dom_string *tagname, dom_string *namespace,
1281 		dom_string *localname, dom_nodelist **list)
1282 {
1283 	struct dom_doc_nl *l;
1284 	dom_exception err;
1285 
1286 	for (l = doc->nodelists; l; l = l->next) {
1287 		if (_dom_nodelist_match(l->list, type, root, tagname,
1288 				namespace, localname))
1289 			break;
1290 	}
1291 
1292 	if (l != NULL) {
1293 		/* Found an existing list, so use it */
1294 		dom_nodelist_ref(l->list);
1295 	} else {
1296 		/* No existing list */
1297 
1298 		/* Create active list entry */
1299 		l = malloc(sizeof(struct dom_doc_nl));
1300 		if (l == NULL)
1301 			return DOM_NO_MEM_ERR;
1302 
1303 		/* Create nodelist */
1304 		err = _dom_nodelist_create(doc, type, root, tagname, namespace,
1305 				localname, &l->list);
1306 		if (err != DOM_NO_ERR) {
1307 			free(l);
1308 			return err;
1309 		}
1310 
1311 		/* Add to document's list of active nodelists */
1312 		l->prev = NULL;
1313 		l->next = doc->nodelists;
1314 		if (doc->nodelists)
1315 			doc->nodelists->prev = l;
1316 		doc->nodelists = l;
1317 	}
1318 
1319 	/* Note: the document does not claim a reference on the nodelist
1320 	 * If it did, the nodelist's reference count would never reach zero,
1321 	 * and the list would remain indefinitely. This is not a problem as
1322 	 * the list notifies the document of its destruction via
1323 	 * _dom_document_remove_nodelist. */
1324 
1325 	*list = l->list;
1326 
1327 	return DOM_NO_ERR;
1328 }
1329 
1330 /**
1331  * Remove a nodelist from a document
1332  *
1333  * \param doc   The document to remove the list from
1334  * \param list  The list to remove
1335  */
_dom_document_remove_nodelist(dom_document * doc,dom_nodelist * list)1336 void _dom_document_remove_nodelist(dom_document *doc,
1337 		dom_nodelist *list)
1338 {
1339 	struct dom_doc_nl *l;
1340 
1341 	for (l = doc->nodelists; l; l = l->next) {
1342 		if (l->list == list)
1343 			break;
1344 	}
1345 
1346 	if (l == NULL) {
1347 		/* This should never happen; we should probably abort here */
1348 		return;
1349 	}
1350 
1351 	/* Remove from list */
1352 	if (l->prev != NULL)
1353 		l->prev->next = l->next;
1354 	else
1355 		doc->nodelists = l->next;
1356 
1357 	if (l->next != NULL)
1358 		l->next->prev = l->prev;
1359 
1360 	/* And free item */
1361 	free(l);
1362 }
1363 
1364 /**
1365  * Find element with certain ID in the subtree rooted at root
1366  *
1367  * \param root    The root element from where we start
1368  * \param id      The ID of the target element
1369  * \param result  The result element
1370  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
1371  */
_dom_find_element_by_id(dom_node_internal * root,dom_string * id,dom_element ** result)1372 dom_exception _dom_find_element_by_id(dom_node_internal *root,
1373 		dom_string *id, dom_element **result)
1374 {
1375 	dom_node_internal *node = root;
1376 
1377 	*result = NULL;
1378 
1379 	while (node != NULL) {
1380 		if (node->type == DOM_ELEMENT_NODE) {
1381 			dom_string *real_id;
1382 
1383 			_dom_element_get_id((dom_element *) node, &real_id);
1384 			if (real_id != NULL) {
1385 				if (dom_string_isequal(real_id, id)) {
1386 					dom_string_unref(real_id);
1387 					*result = (dom_element *) node;
1388 					return DOM_NO_ERR;
1389 				}
1390 
1391 				dom_string_unref(real_id);
1392 			}
1393 		}
1394 
1395 		if (node->first_child != NULL) {
1396 			/* Move to child */
1397 			node = node->first_child;
1398 		} else {
1399 			while (node != NULL) {
1400 				if (node->next != NULL) {
1401 					/* Move to next sibling */
1402 					node = node->next;
1403 					break;
1404 				} else if (node->parent != root) {
1405 					/* Move back up to ancestors to
1406 					 * get to next siblings */
1407 					node = node->parent;
1408 				} else {
1409 					/* No more nodes below root. */
1410 					node = NULL;
1411 				}
1412 			}
1413 		}
1414 	}
1415 
1416 	return DOM_NO_ERR;
1417 }
1418 
1419 /**
1420  * Duplicate a Node
1421  *
1422  * \param doc     The documen
1423  * \param node    The node to duplicate
1424  * \param deep    Whether to make a deep copy
1425  * \param result  The returned node
1426  * \param opt     Whether this is adopt or import operation
1427  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
1428  */
dom_document_dup_node(dom_document * doc,dom_node * node,bool deep,dom_node ** result,dom_node_operation opt)1429 dom_exception dom_document_dup_node(dom_document *doc, dom_node *node,
1430 		bool deep, dom_node **result, dom_node_operation opt)
1431 {
1432 	dom_node_internal *n = (dom_node_internal *) node;
1433 	dom_node_internal *ret;
1434 	dom_exception err;
1435 	dom_node_internal *child, *r;
1436 	dom_user_data *ud;
1437 
1438 	if (opt == DOM_NODE_ADOPTED && _dom_node_readonly(n))
1439 		return DOM_NO_MODIFICATION_ALLOWED_ERR;
1440 
1441 	if (n->type == DOM_DOCUMENT_NODE ||
1442 			n->type == DOM_DOCUMENT_TYPE_NODE)
1443 		return DOM_NOT_SUPPORTED_ERR;
1444 
1445 	err = dom_node_copy(node, &ret);
1446 	if (err != DOM_NO_ERR)
1447 		return err;
1448 
1449 	if (n->type == DOM_ATTRIBUTE_NODE) {
1450 		_dom_attr_set_specified((dom_attr *) node, true);
1451 		deep = true;
1452 	}
1453 
1454 	if (n->type == DOM_ENTITY_REFERENCE_NODE) {
1455 		deep = false;
1456 	}
1457 
1458 	if (n->type == DOM_ELEMENT_NODE) {
1459 		/* Specified attributes are copyied but not default attributes,
1460 		 * if the document object hold all the default attributes, we
1461 		 * have nothing to do here */
1462 	}
1463 
1464 	if (opt == DOM_NODE_ADOPTED && (n->type == DOM_ENTITY_NODE ||
1465 			n->type == DOM_NOTATION_NODE)) {
1466 		/* We did not support XML now */
1467 		return DOM_NOT_SUPPORTED_ERR;
1468 	}
1469 
1470 	if (deep == true) {
1471 		child = ((dom_node_internal *) node)->first_child;
1472 		while (child != NULL) {
1473 			err = dom_document_import_node(doc, child, deep,
1474 					(void *) &r);
1475 			if (err != DOM_NO_ERR) {
1476 				dom_node_unref(ret);
1477 				return err;
1478 			}
1479 
1480 			err = dom_node_append_child(ret, r, (void *) &r);
1481 			if (err != DOM_NO_ERR) {
1482 				dom_node_unref(ret);
1483 				dom_node_unref(r);
1484 				return err;
1485 			}
1486 			dom_node_unref(r);
1487 
1488 			child = child->next;
1489 		}
1490 	}
1491 
1492 	/* Call the dom_user_data_handlers */
1493 	ud = n->user_data;
1494 	while (ud != NULL) {
1495 		if (ud->handler != NULL) {
1496 			ud->handler(opt, ud->key, ud->data, node,
1497 					(dom_node *) ret);
1498 		}
1499 		ud = ud->next;
1500 	}
1501 
1502 	*result = (dom_node *) ret;
1503 
1504 	return DOM_NO_ERR;
1505 }
1506 
1507 /**
1508  * Try to destroy the document.
1509  *
1510  * \param doc  The instance of Document
1511  *
1512  * Delete the document if:
1513  * 1. The refcnt reach zero
1514  * 2. The pending list is empty
1515  *
1516  * else, do nothing.
1517  */
_dom_document_try_destroy(dom_document * doc)1518 void _dom_document_try_destroy(dom_document *doc)
1519 {
1520 	if (doc->base.base.refcnt != 0 || doc->base.parent != NULL)
1521 		return;
1522 
1523 	_dom_document_destroy((dom_node_internal *) doc);
1524 }
1525 
1526 /**
1527  * Set the ID attribute name of this document
1528  *
1529  * \param doc   The document object
1530  * \param name  The ID name of the elements in this document
1531  */
_dom_document_set_id_name(dom_document * doc,dom_string * name)1532 void _dom_document_set_id_name(dom_document *doc, dom_string *name)
1533 {
1534 	if (doc->id_name != NULL)
1535 		dom_string_unref(doc->id_name);
1536 	doc->id_name = dom_string_ref(name);
1537 }
1538 
1539 /*-----------------------------------------------------------------------*/
1540 /* Semi-internal API extensions for NetSurf */
1541 
_dom_document_get_quirks_mode(dom_document * doc,dom_document_quirks_mode * result)1542 dom_exception _dom_document_get_quirks_mode(dom_document *doc,
1543 		dom_document_quirks_mode *result)
1544 {
1545 	*result = doc->quirks;
1546 	return DOM_NO_ERR;
1547 }
1548 
_dom_document_set_quirks_mode(dom_document * doc,dom_document_quirks_mode quirks)1549 dom_exception _dom_document_set_quirks_mode(dom_document *doc,
1550 		dom_document_quirks_mode quirks)
1551 {
1552 	doc->quirks = quirks;
1553 	return DOM_NO_ERR;
1554 }
1555