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