Author: jmb
Date: Sun Jul 29 00:03:56 2007
New Revision: 3468
URL:
http://source.netsurf-browser.org?rev=3468&view=rev
Log:
Implement Document destructor.
Fix handling of nodes within a document (they no longer explicitly reference the
document)
Modified:
trunk/dom/src/core/document.c
trunk/dom/src/core/document.h
trunk/dom/src/core/node.c
Modified: trunk/dom/src/core/document.c
URL:
http://source.netsurf-browser.org/trunk/dom/src/core/document.c?rev=3468&...
==============================================================================
--- trunk/dom/src/core/document.c (original)
+++ trunk/dom/src/core/document.c Sun Jul 29 00:03:56 2007
@@ -138,8 +138,10 @@
}
}
- /* 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 */
@@ -165,6 +167,79 @@
*doc = d;
return DOM_NO_ERR;
+}
+
+/**
+ * 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);
}
/**
Modified: trunk/dom/src/core/document.h
URL:
http://source.netsurf-browser.org/trunk/dom/src/core/document.h?rev=3468&...
==============================================================================
--- trunk/dom/src/core/document.h (original)
+++ trunk/dom/src/core/document.h Sun Jul 29 00:03:56 2007
@@ -18,6 +18,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);
Modified: trunk/dom/src/core/node.c
URL:
http://source.netsurf-browser.org/trunk/dom/src/core/node.c?rev=3468&...
==============================================================================
--- trunk/dom/src/core/node.c (original)
+++ trunk/dom/src/core/node.c Sun Jul 29 00:03:56 2007
@@ -39,10 +39,11 @@
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 @@
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 @@
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 @@
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 @@
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 */