diff options
author | John Mark Bell <jmb@netsurf-browser.org> | 2007-07-28 23:03:56 +0000 |
---|---|---|
committer | John Mark Bell <jmb@netsurf-browser.org> | 2007-07-28 23:03:56 +0000 |
commit | 07be77efe90ed1da2ea317c2ac49326b60d2d1c4 (patch) | |
tree | b6b9a2daf9e28fdffe8a05c1e0455f44a6412ee4 /src | |
parent | ddbf47a33aa4fba4ce23e751db78acf59895fcc3 (diff) | |
download | libdom-07be77efe90ed1da2ea317c2ac49326b60d2d1c4.tar.gz libdom-07be77efe90ed1da2ea317c2ac49326b60d2d1c4.tar.bz2 |
Implement Document destructor.
Fix handling of nodes within a document (they no longer explicitly reference the document)
svn path=/trunk/dom/; revision=3468
Diffstat (limited to 'src')
-rw-r--r-- | src/core/document.c | 79 | ||||
-rw-r--r-- | src/core/document.h | 3 | ||||
-rw-r--r-- | src/core/node.c | 35 |
3 files changed, 109 insertions, 8 deletions
diff --git a/src/core/document.c b/src/core/document.c index ff9e5ec..d238558 100644 --- a/src/core/document.c +++ b/src/core/document.c @@ -138,8 +138,10 @@ dom_exception dom_document_create(struct dom_implementation *impl, } } - /* Initialise base class */ - err = dom_node_initialise(&d->base, d, DOM_DOCUMENT_NODE, + /* Initialise base class -- the Document has no parent, so + * destruction will be attempted as soon as its reference count + * reaches zero. */ + err = dom_node_initialise(&d->base, NULL, DOM_DOCUMENT_NODE, NULL, NULL); if (err != DOM_NO_ERR) { /* Clean up interned strings */ @@ -168,6 +170,79 @@ dom_exception dom_document_create(struct dom_implementation *impl, } /** + * Destroy a document + * + * \param doc The document to destroy + * + * The contents of ::doc will be destroyed and ::doc will be freed. + */ +void dom_document_destroy(struct dom_document *doc) +{ + struct dom_node *c, *d; + + /* Destroy children of this node */ + for (c = doc->base.first_child; c != NULL; c = d) { + d = c->next; + + /* Detach child */ + c->parent = NULL; + + if (c->refcnt > 0) { + /* Something is using this child */ + + /** \todo add to list of nodes pending deletion */ + + continue; + } + + /* Detach from sibling list */ + c->previous = NULL; + c->next = NULL; + + dom_node_destroy(c); + } + + /** \todo Ensure list of nodes pending deletion is empty. If not, + * then we can't yet destroy the document (its destruction will + * have to wait until the pending nodes are destroyed) */ + + /* Ok, the document tree is empty, as is the list of nodes pending + * deletion. Therefore, it is safe to destroy the document. */ + + /* Destroy the doctype (if there is one) */ + if (doc->type != NULL) { + ((struct dom_node *) doc->type)->parent = NULL; + + dom_node_destroy((struct dom_node *) doc->type); + } + + doc->type = NULL; + + if (doc->impl != NULL) + dom_implementation_unref(doc->impl); + doc->impl = NULL; + + /* This is paranoia -- if there are any remaining nodelists or + * namednodemaps, then the document's reference count will be + * non-zero as these data structures reference the document because + * they are held by the client. */ + doc->nodelists = NULL; + doc->maps = NULL; + + /* Clean up interned strings */ + for (int i = 0; i <= DOM_NODE_TYPE_COUNT; i++) { + if (doc->nodenames[i] != NULL) + dom_string_unref(doc->nodenames[i]); + } + + /* Finalise base class */ + dom_node_finalise(doc, &doc->base); + + /* Free document */ + doc->alloc(doc, 0, doc->pw); +} + +/** * Retrieve the doctype of a document * * \param doc The document to retrieve the doctype from diff --git a/src/core/document.h b/src/core/document.h index 6561035..367b1ec 100644 --- a/src/core/document.h +++ b/src/core/document.h @@ -19,6 +19,9 @@ struct dom_node; struct dom_nodelist; struct dom_string; +/* Destroy a document */ +void dom_document_destroy(struct dom_document *doc); + /* Get base of document buffer */ const uint8_t *dom_document_get_base(struct dom_document *doc); diff --git a/src/core/node.c b/src/core/node.c index db0fb05..3ccf869 100644 --- a/src/core/node.c +++ b/src/core/node.c @@ -39,10 +39,11 @@ void dom_node_destroy(struct dom_node *node) struct dom_document *owner = node->owner; /* This function simply acts as a central despatcher - * for type-specific destructors. It claims a reference upon the - * owning document during destruction to ensure that the document - * doesn't get destroyed before its contents. */ + * for type-specific destructors. */ + /* Claim a reference upon the owning document during destruction + * to ensure that the document doesn't get destroyed before its + * contents. */ dom_node_ref((struct dom_node *) owner); switch (node->type) { @@ -74,7 +75,7 @@ void dom_node_destroy(struct dom_node *node) dom_comment_destroy(owner, (struct dom_comment *) node); break; case DOM_DOCUMENT_NODE: - /** \todo document node */ + dom_document_destroy((struct dom_document *) node); break; case DOM_DOCUMENT_TYPE_NODE: /** \todo document type node */ @@ -88,6 +89,9 @@ void dom_node_destroy(struct dom_node *node) break; } + /* Release the reference we claimed on the document. If this is + * the last reference held on the document and the list of nodes + * pending deletion is empty, then the document will be destroyed. */ dom_node_unref((struct dom_node *) owner); } @@ -124,7 +128,24 @@ dom_exception dom_node_initialise(struct dom_node *node, node->next = NULL; node->attributes = NULL; - dom_node_ref((struct dom_node *) doc); + /* Note: nodes do not reference the document to which they belong, + * as this would result in the document never being destroyed once + * the client has finished with it. The document will be aware of + * any nodes that it owns through 2 mechanisms: + * + * either a) Membership of the document tree + * or b) Membership of the list of nodes pending deletion + * + * It is not possible for any given node to be a member of both + * data structures at the same time. + * + * The document will not be destroyed until both of these + * structures are empty. It will forcibly attempt to empty + * the document tree on document destruction. Any still-referenced + * nodes at that time will be added to the list of nodes pending + * deletion. This list will not be forcibly emptied, as it contains + * those nodes (and their sub-trees) in use by client code. + */ node->owner = doc; /** \todo Namespace handling */ @@ -170,7 +191,9 @@ void dom_node_finalise(struct dom_document *doc, struct dom_node *node) if (node->namespace != NULL) dom_string_unref(node->namespace); - dom_node_unref((struct dom_node *) node->owner); + /** \todo check if this node is in list of nodes pending deletion. + * If so, it must be removed from the list, so the document gets + * destroyed once the list is empty (and no longer referenced) */ node->owner = NULL; /* Paranoia */ |