summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-09-29 19:15:54 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-09-29 19:15:54 +0000
commit74395ac888af70d12c3cebf0985c4fc185411c73 (patch)
treeae5e87eb33128517f392cb79bf515e3bb42c2c50
parentf97d7425a82051ba49bc701ee4342cd935ed4faa (diff)
downloadnetsurf-74395ac888af70d12c3cebf0985c4fc185411c73.tar.gz
netsurf-74395ac888af70d12c3cebf0985c4fc185411c73.tar.bz2
Restartable box tree constructor. Yield between elements.
svn path=/trunk/netsurf/; revision=12912
-rw-r--r--render/box.c4
-rw-r--r--render/box.h15
-rw-r--r--render/box_construct.c1062
-rw-r--r--render/html.c95
-rw-r--r--render/html.h1
-rw-r--r--render/html_interaction.c2
-rw-r--r--render/hubbub_binding.c122
-rw-r--r--render/parser_binding.h10
8 files changed, 810 insertions, 501 deletions
diff --git a/render/box.c b/render/box.c
index b48b07e8f..87f1134cf 100644
--- a/render/box.c
+++ b/render/box.c
@@ -109,8 +109,8 @@ free_box_style(struct box *b)
*/
struct box * box_create(css_select_results *styles, css_computed_style *style,
- bool style_owned, char *href, const char *target, char *title,
- char *id, void *context)
+ bool style_owned, const char *href, const char *target,
+ const char *title, char *id, void *context)
{
unsigned int i;
struct box *box;
diff --git a/render/box.h b/render/box.h
index 4b2e8d029..84975ee82 100644
--- a/render/box.h
+++ b/render/box.h
@@ -104,6 +104,7 @@ struct html_content;
#define UNKNOWN_WIDTH INT_MAX
#define UNKNOWN_MAX_WIDTH INT_MAX
+typedef void (*box_construct_complete_cb)(struct html_content *c, bool success);
/** Type of a struct box. */
typedef enum {
@@ -128,7 +129,8 @@ typedef enum {
MAKE_HEIGHT = 1 << 7, /* box causes its own height */
NEED_MIN = 1 << 8, /* minimum width is required for layout */
REPLACE_DIM = 1 << 9, /* replaced element has given dimensions */
- IFRAME = 1 << 10 /* box contains an iframe */
+ IFRAME = 1 << 10, /* box contains an iframe */
+ CONVERT_CHILDREN = 1 << 11 /* wanted children converting */
} box_flags;
/* Sides of a box */
@@ -211,9 +213,9 @@ struct box {
/** Width of space after current text (depends on font and size). */
int space;
- char *href; /**< Link, or 0. */
+ const char *href; /**< Link, or 0. */
const char *target; /**< Link target, or 0. */
- char *title; /**< Title, or 0. */
+ const char *title; /**< Title, or 0. */
unsigned int columns; /**< Number of columns for TABLE / TABLE_CELL. */
unsigned int rows; /**< Number of rows for TABLE only. */
@@ -310,8 +312,8 @@ extern const char *TARGET_BLANK;
void *box_style_alloc(void *ptr, size_t len, void *pw);
struct box * box_create(css_select_results *styles, css_computed_style *style,
- bool style_owned, char *href, const char *target, char *title,
- char *id, void *context);
+ bool style_owned, const char *href, const char *target,
+ const char *title, char *id, void *context);
void box_add_child(struct box *parent, struct box *child);
void box_insert_sibling(struct box *box, struct box *new_box);
void box_unlink_and_free(struct box *box);
@@ -336,7 +338,8 @@ bool box_handle_scrollbars(struct content *c, struct box *box,
bool box_vscrollbar_present(const struct box *box);
bool box_hscrollbar_present(const struct box *box);
-bool xml_to_box(xmlNode *n, struct html_content *c);
+bool xml_to_box(xmlNode *n, struct html_content *c,
+ box_construct_complete_cb cb);
bool box_normalise_block(struct box *block, struct html_content *c);
diff --git a/render/box_construct.c b/render/box_construct.c
index aafb0e5b5..997e78d79 100644
--- a/render/box_construct.c
+++ b/render/box_construct.c
@@ -45,10 +45,44 @@
#include "utils/locale.h"
#include "utils/log.h"
#include "utils/messages.h"
+#include "utils/schedule.h"
#include "utils/talloc.h"
#include "utils/url.h"
#include "utils/utils.h"
+/**
+ * Context for box tree construction
+ */
+struct box_construct_ctx {
+ html_content *content; /**< Content we're constructing for */
+
+ xmlNode *n; /**< Current node to process */
+
+ struct box *root_box; /**< Root box in the tree */
+
+ box_construct_complete_cb cb; /**< Callback to invoke on completion */
+};
+
+/**
+ * Transient properties for construction of current node
+ */
+struct box_construct_props {
+ /** Style from which to inherit, or NULL if none */
+ const css_computed_style *parent_style;
+ /** Current link target, or NULL if none */
+ const char *href;
+ /** Current frame target, or NULL if none */
+ const char *target;
+ /** Current title attribute, or NULL if none */
+ const char *title;
+ /** Identity of the current block-level container */
+ struct box *containing_block;
+ /** Current container for inlines, or NULL if none
+ * \note If non-NULL, will be the last child of containing_block */
+ struct box *inline_container;
+ /** Whether the current node is the root of the DOM tree */
+ bool node_is_root;
+};
static const content_type image_types = CONTENT_IMAGE;
@@ -58,20 +92,11 @@ const char *TARGET_PARENT = "_parent";
const char *TARGET_TOP = "_top";
const char *TARGET_BLANK = "_blank";
-static bool convert_xml_to_box(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title);
-bool box_construct_element(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title);
-void box_construct_generate(xmlNode *n, html_content *content,
- struct box *box, const css_computed_style *style);
-bool box_construct_text(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title);
+static void convert_xml_to_box(struct box_construct_ctx *ctx);
+static bool box_construct_element(struct box_construct_ctx *ctx,
+ bool *convert_children);
+static void box_construct_element_after(xmlNode *n, html_content *content);
+static bool box_construct_text(struct box_construct_ctx *ctx);
static css_select_results * box_get_style(html_content *c,
const css_computed_style *parent_style, xmlNode *n);
static void box_text_transform(char *s, unsigned int len,
@@ -126,41 +151,30 @@ static const struct element_entry element_table[] = {
/**
* Construct a box tree from an xml tree and stylesheets.
*
- * \param n xml tree
- * \param c content of type CONTENT_HTML to construct box tree in
+ * \param n xml tree
+ * \param c content of type CONTENT_HTML to construct box tree in
+ * \param cb callback to report conversion completion
* \return true on success, false on memory exhaustion
*/
-bool xml_to_box(xmlNode *n, html_content *c)
+bool xml_to_box(xmlNode *n, html_content *c, box_construct_complete_cb cb)
{
- struct box root;
- struct box *inline_container = NULL;
-
- root.type = BOX_BLOCK;
- root.style = NULL;
- root.next = NULL;
- root.prev = NULL;
- root.children = NULL;
- root.last = NULL;
- root.parent = NULL;
- root.float_children = NULL;
- root.next_float = NULL;
-
- /* The root box's style */
- if (!convert_xml_to_box(n, c, NULL, &root,
- &inline_container, 0, 0, 0))
- return false;
+ struct box_construct_ctx *ctx;
- if (!box_normalise_block(&root, c))
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
return false;
- c->layout = root.children;
- c->layout->parent = NULL;
+ ctx->content = c;
+ ctx->n = n;
+ ctx->root_box = NULL;
+ ctx->cb = cb;
+
+ schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
return true;
}
-
/* mapping from CSS display to box type
* this table must be in sync with libcss' css_display enum */
static const box_type box_map[] = {
@@ -183,507 +197,644 @@ static const box_type box_map[] = {
BOX_NONE /*CSS_DISPLAY_NONE*/
};
+static inline struct box *box_for_node(const xmlNode *n)
+{
+ return ((binding_private *) n->_private)->box;
+}
+
+static inline bool box_is_root(const xmlNode *n)
+{
+ return n->parent == NULL || n->parent->type == XML_HTML_DOCUMENT_NODE;
+}
/**
- * Recursively construct a box tree from an xml tree and stylesheets.
+ * Find the next node in the DOM tree, completing
+ * element construction where appropriate.
*
- * \param n fragment of xml tree
- * \param content content of type CONTENT_HTML that is being processed
- * \param parent_style style at this point in xml tree, or NULL for root box
- * \param parent parent in box tree
- * \param inline_container current inline container box, or 0, updated to
- * new current inline container on exit
- * \param href current link URL, or 0 if not in a link
- * \param target current link target, or 0 if none
- * \param title current title, or 0 if none
- * \return true on success, false on memory exhaustion
+ * \param n Current node
+ * \param content Containing content
+ * \param convert_children Whether to consider children of \a n
+ * \return Next node to process, or NULL if complete
+ */
+static xmlNode *next_node(xmlNode *n, html_content *content,
+ bool convert_children)
+{
+ xmlNode *next = NULL;
+
+ if (convert_children && n->children != NULL) {
+ next = n->children;
+ } else if (n->next != NULL) {
+ if (box_for_node(n) != NULL)
+ box_construct_element_after(n, content);
+
+ next = n->next;
+ } else {
+ if (box_for_node(n) != NULL)
+ box_construct_element_after(n, content);
+
+ while (box_is_root(n) == false && n->parent->next == NULL) {
+ n = n->parent;
+
+ if (box_for_node(n) != NULL)
+ box_construct_element_after(n, content);
+ }
+
+ if (box_is_root(n) == false) {
+ if (box_for_node(n->parent) != NULL) {
+ box_construct_element_after(n->parent, content);
+ }
+
+ next = n->parent->next;
+ }
+ }
+
+ return next;
+}
+
+/**
+ * Convert an ELEMENT node to a box tree fragment,
+ * then schedule conversion of the next ELEMENT node
*/
+void convert_xml_to_box(struct box_construct_ctx *ctx)
+{
+ xmlNode *next;
+ bool convert_children = true;
+
+ assert(ctx->n != NULL);
+ assert(ctx->n->type == XML_ELEMENT_NODE);
-bool convert_xml_to_box(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title)
+ if (box_construct_element(ctx, &convert_children) == false) {
+ ctx->cb(ctx->content, false);
+ free(ctx);
+ return;
+ }
+
+ /* Find next element to process, converting text nodes as we go */
+ next = next_node(ctx->n, ctx->content, convert_children);
+ while (next != NULL && next->type != XML_ELEMENT_NODE) {
+ if (next->type == XML_TEXT_NODE) {
+ ctx->n = next;
+ if (box_construct_text(ctx) == false) {
+ ctx->cb(ctx->content, false);
+ free(ctx);
+ return;
+ }
+ }
+
+ next = next_node(next, ctx->content, true);
+ }
+
+ ctx->n = next;
+
+ if (next != NULL) {
+ /* More work to do: schedule a continuation */
+ schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
+ } else {
+ /* Conversion complete */
+ struct box root;
+
+ memset(&root, 0, sizeof(root));
+
+ root.type = BOX_BLOCK;
+ root.children = root.last = ctx->root_box;
+ root.children->parent = &root;
+
+ /** \todo Remove box_normalise_block */
+ if (box_normalise_block(&root, ctx->content) == false) {
+ ctx->cb(ctx->content, false);
+ } else {
+ ctx->content->layout = root.children;
+ ctx->content->layout->parent = NULL;
+
+ ctx->cb(ctx->content, true);
+ }
+
+ free(ctx);
+ }
+}
+
+/**
+ * Construct a list marker box
+ *
+ * \param box Box to attach marker to
+ * \param title Current title attribute
+ * \param content Containing content
+ * \param parent Current block-level container
+ * \return True on success, false on memory exhaustion
+ */
+static bool box_construct_marker(struct box *box, const char *title,
+ html_content *content, struct box *parent)
{
- switch (n->type) {
- case XML_ELEMENT_NODE:
- return box_construct_element(n, content, parent_style, parent,
- inline_container, href, target, title);
- case XML_TEXT_NODE:
- return box_construct_text(n, content, parent_style, parent,
- inline_container, href, target, title);
+ lwc_string *image_uri;
+ struct box *marker;
+
+ marker = box_create(NULL, box->style, false, NULL, NULL, title,
+ NULL, content);
+ if (marker == false)
+ return false;
+
+ marker->type = BOX_BLOCK;
+
+ /** \todo marker content (list-style-type) */
+ switch (css_computed_list_style_type(box->style)) {
+ case CSS_LIST_STYLE_TYPE_DISC:
+ /* 2022 BULLET */
+ marker->text = (char *) "\342\200\242";
+ marker->length = 3;
+ break;
+ case CSS_LIST_STYLE_TYPE_CIRCLE:
+ /* 25CB WHITE CIRCLE */
+ marker->text = (char *) "\342\227\213";
+ marker->length = 3;
+ break;
+ case CSS_LIST_STYLE_TYPE_SQUARE:
+ /* 25AA BLACK SMALL SQUARE */
+ marker->text = (char *) "\342\226\252";
+ marker->length = 3;
+ break;
+ case CSS_LIST_STYLE_TYPE_DECIMAL:
+ case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
+ case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
+ case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
+ case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
default:
- /* not an element or text node: ignore it (eg. comment) */
- return true;
+ if (parent->last) {
+ struct box *last = parent->last;
+
+ /* Drill down into last child of parent
+ * to find the list marker (if any)
+ *
+ * Floated list boxes end up as:
+ *
+ * parent
+ * BOX_INLINE_CONTAINER
+ * BOX_FLOAT_{LEFT,RIGHT}
+ * BOX_BLOCK <-- list box
+ * ...
+ */
+ while (last != NULL) {
+ if (last->list_marker != NULL)
+ break;
+
+ last = last->last;
+ }
+
+ if (last && last->list_marker) {
+ marker->rows = last->list_marker->rows + 1;
+ }
+ }
+
+ marker->text = talloc_array(content, char, 20);
+ if (marker->text == NULL)
+ return false;
+
+ snprintf(marker->text, 20, "%u.", marker->rows);
+ marker->length = strlen(marker->text);
+ break;
+ case CSS_LIST_STYLE_TYPE_NONE:
+ marker->text = 0;
+ marker->length = 0;
+ break;
+ }
+
+ if (css_computed_list_style_image(box->style, &image_uri) ==
+ CSS_LIST_STYLE_IMAGE_URI && image_uri != NULL) {
+ if (html_fetch_object(content,
+ lwc_string_data(image_uri),
+ marker, image_types,
+ content->base.available_width,
+ 1000, false) == false)
+ return false;
+ }
+
+ box->list_marker = marker;
+ marker->parent = box;
+
+ return true;
+}
+
+/**
+ * Construct the box required for a generated element.
+ *
+ * \param n XML node of type XML_ELEMENT_NODE
+ * \param content Content of type CONTENT_HTML that is being processed
+ * \param box Box which may have generated content
+ * \param style Complete computed style for pseudo element, or NULL
+ *
+ * TODO:
+ * This is currently incomplete. It just does enough to support the clearfix
+ * hack. ( http://www.positioniseverything.net/easyclearing.html )
+ */
+static void box_construct_generate(xmlNode *n, html_content *content,
+ struct box *box, const css_computed_style *style)
+{
+ struct box *gen = NULL;
+ const css_computed_content_item *c_item;
+
+ /* Nothing to generate if the parent box is not a block */
+ if (box->type != BOX_BLOCK)
+ return;
+
+ /* To determine if an element has a pseudo element, we select
+ * for it and test to see if the returned style's content
+ * property is set to normal. */
+ if (style == NULL ||
+ css_computed_content(style, &c_item) ==
+ CSS_CONTENT_NORMAL) {
+ /* No pseudo element */
+ return;
+ }
+
+ /* create box for this element */
+ if (css_computed_display(style, box_is_root(n)) == CSS_DISPLAY_BLOCK) {
+ /* currently only support block level elements */
+
+ /** \todo Not wise to drop const from the computed style */
+ gen = box_create(NULL, (css_computed_style *) style,
+ false, NULL, NULL, NULL, NULL, content);
+ if (gen == NULL) {
+ return;
+ }
+
+ /* set box type from computed display */
+ gen->type = box_map[css_computed_display(
+ style, box_is_root(n))];
+
+ box_add_child(box, gen);
}
}
+/**
+ * Extract transient construction properties
+ *
+ * \param n Current DOM node to convert
+ * \param props Property object to populate
+ */
+static void box_extract_properties(const xmlNode *n,
+ struct box_construct_props *props)
+{
+ memset(props, 0, sizeof(*props));
+
+ props->node_is_root = box_is_root(n);
+
+ /* Extract properties from containing DOM node */
+ if (props->node_is_root == false) {
+ struct box *parent_box;
+
+ /* Find ancestor node containing parent box */
+ while (n->parent != NULL && box_for_node(n->parent) == NULL)
+ n = n->parent;
+
+ parent_box = box_for_node(n->parent);
+
+ props->parent_style = parent_box->style;
+ props->href = parent_box->href;
+ props->target = parent_box->target;
+ props->title = parent_box->title;
+
+ /* Find containing block (may be parent) */
+ for (n = n->parent; n != NULL; n = n->parent) {
+ struct box *b = box_for_node(n);
+
+ /* Children of nodes that created an inline box
+ * will generate boxes which are attached as
+ * _siblings_ of the box generated for their
+ * parent node. Note, however, that we'll still
+ * use the parent node's styling as the parent
+ * style, above. */
+ if (b != NULL && b->type != BOX_INLINE &&
+ b->type != BOX_BR) {
+ props->containing_block = b;
+ break;
+ }
+ }
+ }
+
+ /* Compute current inline container, if any */
+ if (props->containing_block != NULL &&
+ props->containing_block->last != NULL &&
+ props->containing_block->last->type ==
+ BOX_INLINE_CONTAINER)
+ props->inline_container = props->containing_block->last;
+}
/**
* Construct the box tree for an XML element.
*
- * \param n XML node of type XML_ELEMENT_NODE
- * \param content content of type CONTENT_HTML that is being processed
- * \param parent_style style at this point in xml tree, or NULL for root node
- * \param parent parent in box tree
- * \param inline_container current inline container box, or 0, updated to
- * new current inline container on exit
- * \param href current link URL, or 0 if not in a link
- * \param target current link target, or 0 if none
- * \param title current title, or 0 if none
+ * \param ctx Tree construction context
+ * \param convert_children Whether to convert children
* \return true on success, false on memory exhaustion
*/
-bool box_construct_element(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title)
+bool box_construct_element(struct box_construct_ctx *ctx,
+ bool *convert_children)
{
- bool convert_children = true;
- char *id = 0;
- char *s;
- struct box *box = 0;
- struct box *inline_container_c;
- struct box *inline_end;
+ xmlChar *title0, *s;
+ char *id = NULL;
+ struct box *box = NULL;
css_select_results *styles = NULL;
struct element_entry *element;
- xmlChar *title0;
- xmlNode *c;
lwc_string *bgimage_uri;
+ struct box_construct_props props;
- assert(n);
- assert(n->type == XML_ELEMENT_NODE);
- assert(parent);
- assert(inline_container);
+ assert(ctx->n != NULL);
+ assert(ctx->n->type == XML_ELEMENT_NODE);
- /* In case the parent is a pre block, we clear the
- * PRE_STRIP flag since it is not used if we
- * follow the pre with a tag
- */
- parent->flags &= ~PRE_STRIP;
+ box_extract_properties(ctx->n, &props);
- styles = box_get_style(content, parent_style, n);
- if (!styles)
+ if (props.containing_block != NULL) {
+ /* In case the containing block is a pre block, we clear
+ * the PRE_STRIP flag since it is not used if we follow
+ * the pre with a tag */
+ props.containing_block->flags &= ~PRE_STRIP;
+ }
+
+ styles = box_get_style(ctx->content, props.parent_style, ctx->n);
+ if (styles == NULL)
return false;
- /* extract title attribute, if present */
- if ((title0 = xmlGetProp(n, (const xmlChar *) "title"))) {
- char *title1 = squash_whitespace((char *) title0);
+ /* Extract title attribute, if present */
+ if ((title0 = xmlGetProp(ctx->n, (const xmlChar *) "title")) != NULL) {
+ char *t = squash_whitespace((char *) title0);
xmlFree(title0);
- if (!title1)
+ if (t == NULL)
return false;
- title = talloc_strdup(content, title1);
+ props.title = talloc_strdup(ctx->content, t);
- free(title1);
+ free(t);
- if (!title)
+ if (props.title == NULL)
return false;
}
- /* extract id attribute, if present */
- if (!box_get_attribute(n, "id", content, &id))
+ /* Extract id attribute, if present */
+ if (box_get_attribute(ctx->n, "id", ctx->content, &id) == false)
return false;
- /* create box for this element */
box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false,
- href, target, title, id, content);
- if (!box)
+ props.href, props.target, props.title, id,
+ ctx->content);
+ if (box == NULL)
return false;
- /* set box type from computed display */
+
+ /* If this is the root box, add it to the context */
+ if (props.node_is_root)
+ ctx->root_box = box;
+
+ /* Deal with colspan/rowspan */
+ if ((s = xmlGetProp(ctx->n, (const xmlChar *) "colspan")) != NULL) {
+ if ('0' <= s[0] && s[0] <= '9')
+ box->columns = strtol((char *) s, NULL, 10);
+
+ xmlFree(s);
+ }
+
+ if ((s = xmlGetProp(ctx->n, (const xmlChar *) "rowspan")) != NULL) {
+ if ('0' <= s[0] && s[0] <= '9')
+ box->rows = strtol((char *) s, NULL, 10);
+
+ xmlFree(s);
+ }
+
+ /* Set box type from computed display */
if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE ||
css_computed_position(box->style) ==
CSS_POSITION_FIXED) &&
- (css_computed_display_static(box->style) ==
+ (css_computed_display_static(box->style) ==
CSS_DISPLAY_INLINE ||
- css_computed_display_static(box->style) ==
+ css_computed_display_static(box->style) ==
CSS_DISPLAY_INLINE_BLOCK ||
- css_computed_display_static(box->style) ==
+ css_computed_display_static(box->style) ==
CSS_DISPLAY_INLINE_TABLE)) {
/* Special case for absolute positioning: make absolute inlines
- * into inline block so that the boxes are constructed in an
- * inline container as if they were not absolutely positioned.
+ * into inline block so that the boxes are constructed in an
+ * inline container as if they were not absolutely positioned.
* Layout expects and handles this. */
box->type = box_map[CSS_DISPLAY_INLINE_BLOCK];
} else {
/* Normal mapping */
box->type = box_map[css_computed_display(box->style,
- n->parent == NULL)];
+ props.node_is_root)];
}
- /* handle the :before pseudo element */
-
- /* TODO: Replace with true implementation.
- * Currently we only implement enough of this to support the
- * 'clearfix' hack, which is in widespread use and the layout
- * of many sites depend on. As such, only bother if box is a
- * block for now. */
- if (box->type == BOX_BLOCK) {
- box_construct_generate(n, content, box,
- box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
- }
+ /* Handle the :before pseudo element */
+ box_construct_generate(ctx->n, ctx->content, box,
+ box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
- /* special elements */
- element = bsearch((const char *) n->name, element_table,
+ /* Special elements */
+ element = bsearch((const char *) ctx->n->name, element_table,
ELEMENT_TABLE_COUNT, sizeof(element_table[0]),
(int (*)(const void *, const void *)) strcmp);
- if (element) {
- /* a special convert function exists for this element */
- if (!element->convert(n, content, box, &convert_children))
+ if (element != NULL) {
+ /* A special convert function exists for this element */
+ if (element->convert(ctx->n, ctx->content, box,
+ convert_children) == false)
return false;
-
- href = box->href;
- target = box->target;
}
if (box->type == BOX_NONE || css_computed_display(box->style,
- n->parent == NULL) == CSS_DISPLAY_NONE) {
- /* Free style and invalidate box's style pointer */
+ props.node_is_root) == CSS_DISPLAY_NONE) {
css_select_results_destroy(styles);
box->styles = NULL;
box->style = NULL;
- /* If this box has an associated gadget, invalidate the
- * gadget's box pointer and our pointer to the gadget. */
- if (box->gadget) {
+ /* Invalidate associated gadget, if any */
+ if (box->gadget != NULL) {
box->gadget->box = NULL;
box->gadget = NULL;
}
- /* We can't do this, as it will destroy any gadget
- * associated with the box, thus making any form usage
- * access freed memory. The box is in the talloc context,
- * anyway, so will get cleaned up with the content. */
+ /* Can't do this, because the lifetimes of boxes and gadgets
+ * are inextricably linked. Fortunately, talloc will save us
+ * (for now) */
/* box_free_box(box); */
+
+ *convert_children = false;
+
return true;
}
- if (!*inline_container &&
+ /* Attach box to DOM node */
+ ((binding_private *) ctx->n->_private)->box = box;
+
+ if (props.inline_container == NULL &&
(box->type == BOX_INLINE ||
- box->type == BOX_BR ||
- box->type == BOX_INLINE_BLOCK ||
- css_computed_float(box->style) == CSS_FLOAT_LEFT ||
- css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
- /* this is the first inline in a block: make a container */
- *inline_container = box_create(NULL, 0, false, 0, 0, 0, 0,
- content);
- if (!*inline_container)
+ box->type == BOX_BR ||
+ box->type == BOX_INLINE_BLOCK ||
+ css_computed_float(box->style) == CSS_FLOAT_LEFT ||
+ css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
+ /* Found an inline child of a block without a current container
+ * (i.e. this box is the first child of its parent, or was
+ * preceded by block-level siblings) */
+ assert(props.containing_block != NULL &&
+ "Root box must not be inline or floated");
+
+ props.inline_container = box_create(NULL, NULL, false, NULL,
+ NULL, NULL, NULL, ctx->content);
+ if (props.inline_container == NULL)
return false;
- (*inline_container)->type = BOX_INLINE_CONTAINER;
+ props.inline_container->type = BOX_INLINE_CONTAINER;
- box_add_child(parent, *inline_container);
+ box_add_child(props.containing_block, props.inline_container);
}
- if (box->type == BOX_INLINE || box->type == BOX_BR) {
- /* inline box: add to tree and recurse */
- box_add_child(*inline_container, box);
-
- if (convert_children && n->children) {
- for (c = n->children; c; c = c->next)
- if (!convert_xml_to_box(c, content, box->style,
- parent, inline_container,
- href, target, title))
- return false;
-
- inline_end = box_create(NULL, box->style, false, href,
- target, title, id, content);
- if (!inline_end)
- return false;
-
- inline_end->type = BOX_INLINE_END;
-
- if (*inline_container)
- box_add_child(*inline_container, inline_end);
- else
- box_add_child(box->parent, inline_end);
+ /* Kick off fetch for any background image */
+ if (css_computed_background_image(box->style, &bgimage_uri) ==
+ CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL) {
+ if (html_fetch_object(ctx->content,
+ lwc_string_data(bgimage_uri),
+ box, image_types,
+ ctx->content->base.available_width, 1000,
+ true) == false)
+ return false;
+ }
- box->inline_end = inline_end;
- inline_end->inline_end = box;
- }
- } else if (box->type == BOX_INLINE_BLOCK) {
- /* inline block box: add to tree and recurse */
- box_add_child(*inline_container, box);
+ if (*convert_children)
+ box->flags |= CONVERT_CHILDREN;
- inline_container_c = 0;
+ if (box->type == BOX_INLINE || box->type == BOX_BR ||
+ box->type == BOX_INLINE_BLOCK) {
+ /* Inline container must exist, as we'll have
+ * created it above if it didn't */
+ assert(props.inline_container != NULL);
- for (c = n->children; convert_children && c; c = c->next)
- if (!convert_xml_to_box(c, content, box->style, box,
- &inline_container_c,
- href, target, title))
- return false;
+ box_add_child(props.inline_container, box);
} else {
- /* list item: compute marker, then treat as non-inline box */
- if (css_computed_display(box->style, n->parent == NULL) ==
+ if (css_computed_display(box->style, props.node_is_root) ==
CSS_DISPLAY_LIST_ITEM) {
- lwc_string *image_uri;
- struct box *marker;
-
- marker = box_create(NULL, box->style, false, 0, 0,
- title, 0, content);
- if (!marker)
+ /* List item: compute marker */
+ if (box_construct_marker(box, props.title, ctx->content,
+ props.containing_block) == false)
return false;
-
- marker->type = BOX_BLOCK;
-
- /** \todo marker content (list-style-type) */
- switch (css_computed_list_style_type(box->style)) {
- case CSS_LIST_STYLE_TYPE_DISC:
- /* 2022 BULLET */
- marker->text = (char *) "\342\200\242";
- marker->length = 3;
- break;
- case CSS_LIST_STYLE_TYPE_CIRCLE:
- /* 25CB WHITE CIRCLE */
- marker->text = (char *) "\342\227\213";
- marker->length = 3;
- break;
- case CSS_LIST_STYLE_TYPE_SQUARE:
- /* 25AA BLACK SMALL SQUARE */
- marker->text = (char *) "\342\226\252";
- marker->length = 3;
- break;
- case CSS_LIST_STYLE_TYPE_DECIMAL:
- case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
- case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
- case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
- case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
- default:
- if (parent->last) {
- struct box *last = parent->last;
-
- /* Drill down into last child of parent
- * to find the list marker (if any)
- *
- * Floated list boxes end up as:
- *
- * parent
- * BOX_INLINE_CONTAINER
- * BOX_FLOAT_{LEFT,RIGHT}
- * BOX_BLOCK <-- list box
- * ...
- */
- while (last != NULL) {
- if (last->list_marker != NULL)
- break;
-
- last = last->last;
- }
-
- if (last && last->list_marker) {
- marker->rows = last->
- list_marker->rows + 1;
- }
- }
-
- marker->text = talloc_array(content, char, 20);
- if (!marker->text)
- return false;
-
- snprintf(marker->text, 20, "%u.", marker->rows);
- marker->length = strlen(marker->text);
- break;
- case CSS_LIST_STYLE_TYPE_NONE:
- marker->text = 0;
- marker->length = 0;
- break;
- }
-
- if (css_computed_list_style_image(box->style,
- &image_uri) ==
- CSS_LIST_STYLE_IMAGE_URI &&
- image_uri != NULL) {
- if (!html_fetch_object(content,
- lwc_string_data(image_uri),
- marker, image_types,
- content->base.available_width,
- 1000, false))
- return false;
- }
-
- box->list_marker = marker;
- marker->parent = box;
}
- /* float: insert a float box between the parent and
- * current node. Note: new parent will be the float */
if (css_computed_float(box->style) == CSS_FLOAT_LEFT ||
- css_computed_float(box->style) ==
+ css_computed_float(box->style) ==
CSS_FLOAT_RIGHT) {
- parent = box_create(NULL, 0, false, href, target, title,
- 0, content);
- if (!parent)
+ /* Float: insert a float between the parent and box. */
+ struct box *flt = box_create(NULL, NULL, false,
+ props.href, props.target, props.title,
+ NULL, ctx->content);
+ if (flt == NULL)
return false;
if (css_computed_float(box->style) == CSS_FLOAT_LEFT)
- parent->type = BOX_FLOAT_LEFT;
+ flt->type = BOX_FLOAT_LEFT;
else
- parent->type = BOX_FLOAT_RIGHT;
-
- box_add_child(*inline_container, parent);
- }
-
- /* non-inline box: add to tree and recurse */
- box_add_child(parent, box);
-
- inline_container_c = 0;
-
- for (c = n->children; convert_children && c; c = c->next)
- if (!convert_xml_to_box(c, content, box->style, box,
- &inline_container_c,
- href, target, title))
- return false;
-
- if (css_computed_float(box->style) == CSS_FLOAT_NONE)
- /* new inline container unless this is a float */
- *inline_container = 0;
- }
-
- /* misc. attributes that can't be handled in box_get_style() */
- if ((s = (char *) xmlGetProp(n, (const xmlChar *) "colspan"))) {
- if (isdigit(s[0])) {
- box->columns = strtol(s, NULL, 10);
- }
- xmlFree(s);
- }
+ flt->type = BOX_FLOAT_RIGHT;
- if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rowspan"))) {
- if (isdigit(s[0])) {
- box->rows = strtol(s, NULL, 10);
+ box_add_child(props.inline_container, flt);
+ box_add_child(flt, box);
+ } else {
+ /* Non-floated block-level box: add to containing block
+ * if there is one. If we're the root box, then there
+ * won't be. */
+ if (props.containing_block != NULL)
+ box_add_child(props.containing_block, box);
}
- xmlFree(s);
- }
-
- /* fetch any background image for this box */
- if (css_computed_background_image(box->style, &bgimage_uri) ==
- CSS_BACKGROUND_IMAGE_IMAGE &&
- bgimage_uri != NULL) {
- if (!html_fetch_object(content,
- lwc_string_data(bgimage_uri),
- box, image_types, content->base.available_width,
- 1000, true))
- return false;
- }
-
- /* handle the :after pseudo element */
-
- /* TODO: Replace with true implementation.
- * Currently we only implement enough of this to support the
- * 'clearfix' hack, which is in widespread use and the layout
- * of many sites depend on. As such, only bother if box is a
- * block for now. */
- if (box->type == BOX_BLOCK) {
- box_construct_generate(n, content, box,
- box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]);
}
return true;
}
/**
- * Construct the box required for a generated element.
+ * Complete construction of the box tree for an element.
*
- * \param n XML node of type XML_ELEMENT_NODE
- * \param content content of type CONTENT_HTML that is being processed
- * \param box box which may have generated content
- * \param style complete computed style for pseudo element
+ * \param n DOM node to construct for
+ * \param content Containing document
*
- * TODO:
- * This is currently incomplete. It just does enough to support the clearfix
- * hack. ( http://www.positioniseverything.net/easyclearing.html )
- *
- * To determine if an element has a pseudo element, we select for it and test to
- * see if the returned style's content property is set to normal.
- *
- * We don't actually support generated content yet.
+ * This will be called after all children of an element have been processed
*/
-
-void box_construct_generate(xmlNode *n, html_content *content,
- struct box *box, const css_computed_style *style)
+void box_construct_element_after(xmlNode *n, html_content *content)
{
- struct box *gen = NULL;
- const css_computed_content_item *c_item;
+ struct box_construct_props props;
+ struct box *box = box_for_node(n);
- if (style == NULL ||
- css_computed_content(style, &c_item) ==
- CSS_CONTENT_NORMAL) {
- /* No pseudo element */
- return;
- }
+ assert(box != NULL);
- /* create box for this element */
- if (css_computed_display(style, n->parent == NULL) ==
- CSS_DISPLAY_BLOCK) {
- /* currently only support block level after elements */
+ box_extract_properties(n, &props);
- /** \todo Not wise to drop const from the computed style */
- gen = box_create(NULL, (css_computed_style *) style,
- false, NULL, NULL, NULL, NULL, content);
- if (gen == NULL) {
+ if (box->type == BOX_INLINE || box->type == BOX_BR) {
+ /* Insert INLINE_END into containing block */
+ struct box *inline_end;
+
+ if (n->children == NULL ||
+ (box->flags & CONVERT_CHILDREN) == 0) {
+ /* No children, or didn't want children converted */
return;
}
- /* set box type from computed display */
- gen->type = box_map[css_computed_display(
- style, n->parent == NULL)];
+ if (props.inline_container == NULL) {
+ /* Create inline container if we don't have one */
+ props.inline_container = box_create(NULL, NULL, false,
+ NULL, NULL, NULL, NULL, content);
+ if (props.inline_container == NULL)
+ return;
- box_add_child(box, gen);
+ props.inline_container->type = BOX_INLINE_CONTAINER;
+
+ box_add_child(props.containing_block,
+ props.inline_container);
+ }
+
+ inline_end = box_create(NULL, box->style, false,
+ box->href, box->target, box->title,
+ box->id, content);
+ if (inline_end != NULL) {
+ inline_end->type = BOX_INLINE_END;
+
+ assert(props.inline_container != NULL);
+
+ box_add_child(props.inline_container, inline_end);
+
+ box->inline_end = inline_end;
+ inline_end->inline_end = box;
+ }
+ } else {
+ /* Handle the :after pseudo element */
+ box_construct_generate(n, content, box,
+ box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]);
}
}
-
/**
* Construct the box tree for an XML text node.
*
- * \param n XML node of type XML_TEXT_NODE
- * \param content content of type CONTENT_HTML that is being processed
- * \param parent_style style at this point in xml tree
- * \param parent parent in box tree
- * \param inline_container current inline container box, or 0, updated to
- * new current inline container on exit
- * \param href current link URL, or 0 if not in a link
- * \param target current link target, or 0 if none
- * \param title current title, or 0 if none
+ * \param ctx Tree construction context
* \return true on success, false on memory exhaustion
*/
-bool box_construct_text(xmlNode *n, html_content *content,
- const css_computed_style *parent_style,
- struct box *parent, struct box **inline_container,
- char *href, const char *target, char *title)
+bool box_construct_text(struct box_construct_ctx *ctx)
{
- struct box *box = 0;
+ struct box_construct_props props;
+ struct box *box = NULL;
+
+ assert(ctx->n != NULL);
+ assert(ctx->n->type == XML_TEXT_NODE);
+
+ box_extract_properties(ctx->n, &props);
- assert(n);
- assert(n->type == XML_TEXT_NODE);
- assert(parent_style);
- assert(parent);
- assert(inline_container);
+ assert(props.containing_block != NULL);
- if (css_computed_white_space(parent_style) == CSS_WHITE_SPACE_NORMAL ||
- css_computed_white_space(parent_style) ==
+ if (css_computed_white_space(props.parent_style) ==
+ CSS_WHITE_SPACE_NORMAL ||
+ css_computed_white_space(props.parent_style) ==
CSS_WHITE_SPACE_NOWRAP) {
- char *text = squash_whitespace((char *) n->content);
- if (!text)
+ char *text = squash_whitespace((char *) ctx->n->content);
+ if (text == NULL)
return false;
/* if the text is just a space, combine it with the preceding
* text node, if any */
if (text[0] == ' ' && text[1] == 0) {
- if (*inline_container) {
- if ((*inline_container)->last == 0) {
- LOG(("empty inline_container %p",
- *inline_container));
- while (parent->parent &&
- parent->parent->parent)
- parent = parent->parent;
- box_dump(stderr, parent, 0);
- }
+ if (props.inline_container != NULL) {
+ assert(props.inline_container->last != NULL);
- assert((*inline_container)->last != 0);
-
- (*inline_container)->last->space =
+ props.inline_container->last->space =
UNKNOWN_WIDTH;
}
@@ -692,33 +843,38 @@ bool box_construct_text(xmlNode *n, html_content *content,
return true;
}
- if (!*inline_container) {
- /* this is the first inline node: make a container */
- *inline_container = box_create(NULL, 0, false, 0, 0, 0,
- 0, content);
- if (!*inline_container) {
+ if (props.inline_container == NULL) {
+ /* Child of a block without a current container
+ * (i.e. this box is the first child of its parent, or
+ * was preceded by block-level siblings) */
+ props.inline_container = box_create(NULL, NULL, false,
+ NULL, NULL, NULL, NULL, ctx->content);
+ if (props.inline_container == NULL) {
free(text);
return false;
}
- (*inline_container)->type = BOX_INLINE_CONTAINER;
+ props.inline_container->type = BOX_INLINE_CONTAINER;
- box_add_child(parent, *inline_container);
+ box_add_child(props.containing_block,
+ props.inline_container);
}
/** \todo Dropping const here is not clever */
- box = box_create(NULL, (css_computed_style *) parent_style,
- false, href, target, title, 0, content);
- if (!box) {
+ box = box_create(NULL,
+ (css_computed_style *) props.parent_style,
+ false, props.href, props.target, props.title,
+ NULL, ctx->content);
+ if (box == NULL) {
free(text);
return false;
}
box->type = BOX_TEXT;
- box->text = talloc_strdup(content, text);
+ box->text = talloc_strdup(ctx->content, text);
free(text);
- if (!box->text)
+ if (box->text == NULL)
return false;
box->length = strlen(box->text);
@@ -729,12 +885,13 @@ bool box_construct_text(xmlNode *n, html_content *content,
box->length--;
}
- if (css_computed_text_transform(parent_style) !=
+ if (css_computed_text_transform(props.parent_style) !=
CSS_TEXT_TRANSFORM_NONE)
box_text_transform(box->text, box->length,
- css_computed_text_transform(parent_style));
+ css_computed_text_transform(
+ props.parent_style));
- if (css_computed_white_space(parent_style) ==
+ if (css_computed_white_space(props.parent_style) ==
CSS_WHITE_SPACE_NOWRAP) {
unsigned int i;
@@ -755,7 +912,7 @@ bool box_construct_text(xmlNode *n, html_content *content,
}
}
- box_add_child(*inline_container, box);
+ box_add_child(props.inline_container, box);
if (box->text[0] == ' ') {
box->length--;
@@ -765,40 +922,42 @@ bool box_construct_text(xmlNode *n, html_content *content,
if (box->prev != NULL)
box->prev->space = UNKNOWN_WIDTH;
}
-
} else {
/* white-space: pre */
- char *text = cnv_space2nbsp((char *) n->content);
+ char *text = cnv_space2nbsp((char *) ctx->n->content);
char *current;
enum css_white_space_e white_space =
- css_computed_white_space(parent_style);
+ css_computed_white_space(props.parent_style);
/* note: pre-wrap/pre-line are unimplemented */
assert(white_space == CSS_WHITE_SPACE_PRE ||
white_space == CSS_WHITE_SPACE_PRE_LINE ||
white_space == CSS_WHITE_SPACE_PRE_WRAP);
- if (!text)
+ if (text == NULL)
return false;
- if (css_computed_text_transform(parent_style) !=
+ if (css_computed_text_transform(props.parent_style) !=
CSS_TEXT_TRANSFORM_NONE)
box_text_transform(text, strlen(text),
- css_computed_text_transform(parent_style));
+ css_computed_text_transform(
+ props.parent_style));
current = text;
/* swallow a single leading new line */
- if (parent->flags & PRE_STRIP) {
+ if (props.containing_block->flags & PRE_STRIP) {
switch (*current) {
case '\n':
- current++; break;
+ current++;
+ break;
case '\r':
current++;
- if (*current == '\n') current++;
+ if (*current == '\n')
+ current++;
break;
}
- parent->flags &= ~PRE_STRIP;
+ props.containing_block->flags &= ~PRE_STRIP;
}
do {
@@ -807,40 +966,47 @@ bool box_construct_text(xmlNode *n, html_content *content,
current[len] = 0;
- if (!*inline_container) {
- *inline_container = box_create(NULL, 0, false,
- 0, 0, 0, 0, content);
- if (!*inline_container) {
+ if (props.inline_container == NULL) {
+ /* Child of a block without a current container
+ * (i.e. this box is the first child of its
+ * parent, or was preceded by block-level
+ * siblings) */
+ props.inline_container = box_create(NULL, NULL,
+ false, NULL, NULL, NULL, NULL,
+ ctx->content);
+ if (props.inline_container == NULL) {
free(text);
return false;
}
- (*inline_container)->type =
+ props.inline_container->type =
BOX_INLINE_CONTAINER;
- box_add_child(parent, *inline_container);
+ box_add_child(props.containing_block,
+ props.inline_container);
}
/** \todo Dropping const isn't clever */
box = box_create(NULL,
- (css_computed_style *) parent_style,
- false, href, target, title, 0, content);
- if (!box) {
+ (css_computed_style *) props.parent_style,
+ false, props.href, props.target, props.title,
+ NULL, ctx->content);
+ if (box == NULL) {
free(text);
return false;
}
box->type = BOX_TEXT;
- box->text = talloc_strdup(content, current);
- if (!box->text) {
+ box->text = talloc_strdup(ctx->content, current);
+ if (box->text == NULL) {
free(text);
return false;
}
box->length = strlen(box->text);
- box_add_child(*inline_container, box);
+ box_add_child(props.inline_container, box);
current[len] = old;
@@ -848,10 +1014,10 @@ bool box_construct_text(xmlNode *n, html_content *content,
if (current[0] == '\r' && current[1] == '\n') {
current += 2;
- *inline_container = 0;
+ props.inline_container = NULL;
} else if (current[0] != 0) {
current++;
- *inline_container = 0;
+ props.inline_container = NULL;
}
} while (*current);
diff --git a/render/html.c b/render/html.c
index 392fec18e..4231b7bae 100644
--- a/render/html.c
+++ b/render/html.c
@@ -83,6 +83,7 @@ static nserror html_clone(const struct content *old, struct content **newc);
static content_type html_content_type(void);
static void html_finish_conversion(html_content *c);
+static void html_box_convert_done(html_content *c, bool success);
static nserror html_convert_css_callback(hlcache_handle *css,
const hlcache_event *event, void *pw);
static bool html_meta_refresh(html_content *c, xmlNode *head);
@@ -91,6 +92,7 @@ static bool html_find_stylesheets(html_content *c, xmlNode *html);
static bool html_process_style_element(html_content *c, unsigned int *index,
xmlNode *style);
static void html_inline_style_done(struct content_css_data *css, void *pw);
+static bool html_fetch_objects(html_content *c);
static bool html_replace_object(struct content_html_object *object,
const char *url);
static nserror html_object_callback(hlcache_handle *object,
@@ -632,16 +634,46 @@ void html_finish_conversion(html_content *c)
}
/* convert xml tree to box tree */
- LOG(("XML to box"));
+ LOG(("XML to box (%p)", c));
content_set_status(&c->base, messages_get("Processing"));
content_broadcast(&c->base, CONTENT_MSG_STATUS, msg_data);
- if (xml_to_box(html, c) == false) {
+ if (xml_to_box(html, c, html_box_convert_done) == false) {
html_destroy_objects(c);
msg_data.error = messages_get("NoMemory");
content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
content_set_error(&c->base);
return;
}
+}
+
+/**
+ * Perform post-box-creation conversion of a document
+ *
+ * \param c HTML content to complete conversion of
+ * \param success Whether box tree construction was successful
+ */
+void html_box_convert_done(html_content *c, bool success)
+{
+ union content_msg_data msg_data;
+ xmlNode *html;
+
+ LOG(("Done XML to box (%p)", c));
+
+ /* Clean up and report error if unsuccessful or aborted */
+ if (success == false || c->aborted) {
+ html_destroy_objects(c);
+ if (success == false)
+ msg_data.error = messages_get("NoMemory");
+ else
+ msg_data.error = messages_get("Stopped");
+ content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ content_set_error(&c->base);
+ return;
+ }
+
+ html = xmlDocGetRootElement(c->document);
+ assert(html != NULL);
+
#if ALWAYS_DUMP_BOX
box_dump(stderr, c->layout->children, 0);
#endif
@@ -665,6 +697,9 @@ void html_finish_conversion(html_content *c)
binding_destroy_tree(c->parser_binding);
c->parser_binding = NULL;
+ /* Spawn object fetches */
+ html_fetch_objects(c);
+
content_set_ready(&c->base);
if (c->base.active == 0)
@@ -1334,7 +1369,7 @@ nserror html_convert_css_callback(hlcache_handle *css,
/**
- * Start a fetch for an object required by a page.
+ * Queue a fetch for an object required by a page.
*
* \param c content of type CONTENT_HTML
* \param url URL of object to fetch (copied)
@@ -1352,18 +1387,13 @@ bool html_fetch_object(html_content *c, const char *url, struct box *box,
bool background)
{
struct content_html_object *object;
- hlcache_child_context child;
char *url2;
url_func_result res;
- nserror error;
/* If we've already been aborted, don't bother attempting the fetch */
if (c->aborted)
return true;
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
/* Normalize the URL */
res = url_normalize(url, &url2);
if (res != URL_FUNC_OK) {
@@ -1383,28 +1413,43 @@ bool html_fetch_object(html_content *c, const char *url, struct box *box,
object->box = box;
object->permitted_types = permitted_types;
object->background = background;
+ object->url = url2;
- error = hlcache_handle_retrieve(url2, HLCACHE_RETRIEVE_SNIFF_TYPE,
- content__get_url(&c->base), NULL,
- html_object_callback, object, &child,
- permitted_types, &object->content);
-
- /* No longer need normalized url */
- free(url2);
-
- if (error != NSERROR_OK) {
- talloc_free(object);
- return error != NSERROR_NOMEM;
- }
-
/* add to object list */
object->next = c->object_list;
c->object_list = object;
c->num_objects++;
- c->base.active++;
- return error != NSERROR_NOMEM;
+ return true;
+}
+
+/**
+ * Start fetches for objects requested by a page
+ *
+ * \param c Content object
+ * \return true on success, false otherwise
+ */
+bool html_fetch_objects(html_content *c)
+{
+ struct content_html_object *object;
+ hlcache_child_context child;
+ nserror error;
+
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ for (object = c->object_list; object != NULL; object = object->next) {
+ error = hlcache_handle_retrieve(object->url,
+ HLCACHE_RETRIEVE_SNIFF_TYPE,
+ content__get_url(&c->base), NULL,
+ html_object_callback, object, &child,
+ object->permitted_types, &object->content);
+ if (error == NSERROR_OK)
+ c->base.active++;
+ }
+
+ return c->base.active == c->num_objects;
}
@@ -1857,7 +1902,7 @@ void html_destroy(struct content *c)
binding_destroy_tree(html->parser_binding);
if (html->document != NULL)
- xmlFreeDoc(html->document);
+ binding_destroy_document(html->document);
/* Free base target */
if (html->base_target != NULL) {
@@ -1916,6 +1961,8 @@ void html_destroy_objects(html_content *html)
hlcache_handle_release(victim->content);
}
+ free(victim->url);
+
html->object_list = victim->next;
talloc_free(victim);
}
diff --git a/render/html.h b/render/html.h
index c3f36fda3..1c530ec51 100644
--- a/render/html.h
+++ b/render/html.h
@@ -78,6 +78,7 @@ struct content_html_object {
struct content *parent; /**< Parent document */
struct content_html_object *next; /**< Next in chain */
+ char *url; /**< URL of content */
struct hlcache_handle *content; /**< Content, or 0. */
struct box *box; /**< Node in box tree containing it. */
/** Bitmap of acceptable content types */
diff --git a/render/html_interaction.c b/render/html_interaction.c
index f6861a79f..51e251698 100644
--- a/render/html_interaction.c
+++ b/render/html_interaction.c
@@ -167,7 +167,7 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
{
html_content *html = (html_content *) c;
enum { ACTION_NONE, ACTION_SUBMIT, ACTION_GO } action = ACTION_NONE;
- char *title = 0;
+ const char *title = 0;
const char *url = 0;
const char *target = 0;
char status_buffer[200];
diff --git a/render/hubbub_binding.c b/render/hubbub_binding.c
index a35ebb3ab..5012b4b41 100644
--- a/render/hubbub_binding.c
+++ b/render/hubbub_binding.c
@@ -34,6 +34,15 @@
#include "utils/log.h"
#include "utils/talloc.h"
+/**
+ * Private data attached to each DOM node
+ */
+typedef struct hubbub_private {
+ binding_private base;
+
+ uint32_t refcnt;
+} hubbub_private;
+
typedef struct hubbub_ctx {
hubbub_parser *parser;
@@ -69,6 +78,7 @@ static struct {
{ "xmlns", "http://www.w3.org/2000/xmlns/" }
};
+static hubbub_private *create_private(uint32_t refcnt);
static inline char *c_string_from_hubbub_string(hubbub_ctx *ctx,
const hubbub_string *str);
static void create_namespaces(hubbub_ctx *ctx, xmlNode *root);
@@ -168,7 +178,13 @@ binding_error binding_create_tree(void *arena, const char *charset, void **ctx)
free(c);
return BINDING_NOMEM;
}
- c->document->_private = (void *) 0;
+ c->document->_private = create_private(0);
+ if (c->document->_private == NULL) {
+ xmlFreeDoc(c->document);
+ hubbub_parser_destroy(c->parser);
+ free(c);
+ return BINDING_NOMEM;
+ }
for (i = 0; i < sizeof(c->namespaces) / sizeof(c->namespaces[0]); i++) {
c->namespaces[i] = NULL;
@@ -200,7 +216,7 @@ binding_error binding_destroy_tree(void *ctx)
hubbub_parser_destroy(c->parser);
if (c->owns_doc)
- xmlFreeDoc(c->document);
+ binding_destroy_document(c->document);
c->parser = NULL;
c->encoding = NULL;
@@ -290,8 +306,43 @@ struct form_control *binding_get_control_for_node(void *ctx, xmlNodePtr node)
return ctl;
}
+void binding_destroy_document(xmlDocPtr doc)
+{
+ xmlNode *n = (xmlNode *) doc;
+
+ while (n != NULL) {
+ free(n->_private);
+
+ if (n->children != NULL) {
+ n = n->children;
+ } else if (n->next != NULL) {
+ n = n->next;
+ } else {
+ while (n->parent != NULL && n->parent->next == NULL)
+ n = n->parent;
+
+ if (n->parent != NULL)
+ n = n->parent->next;
+ else
+ n = NULL;
+ }
+ }
+
+ xmlFreeDoc(doc);
+}
+
/*****************************************************************************/
+hubbub_private *create_private(uint32_t refcnt)
+{
+ hubbub_private *pvt = calloc(1, sizeof(*pvt));
+
+ if (pvt != NULL)
+ pvt->refcnt = refcnt;
+
+ return pvt;
+}
+
char *c_string_from_hubbub_string(hubbub_ctx *ctx, const hubbub_string *str)
{
return strndup((const char *) str->ptr, (int) str->len);
@@ -328,7 +379,12 @@ hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result)
free(content);
return HUBBUB_NOMEM;
}
- n->_private = (void *) (uintptr_t) 1;
+ n->_private = create_private(1);
+ if (n->_private == NULL) {
+ xmlFreeNode(n);
+ free(content);
+ return HUBBUB_NOMEM;
+ }
free(content);
@@ -374,7 +430,14 @@ hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype,
free(name);
return HUBBUB_NOMEM;
}
- n->_private = (void *) (uintptr_t) 1;
+ n->_private = create_private(1);
+ if (n->_private == NULL) {
+ xmlFreeDtd(n);
+ free(system);
+ free(public);
+ free(name);
+ return HUBBUB_NOMEM;
+ }
*result = (void *) n;
@@ -413,10 +476,16 @@ hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
free(name);
return HUBBUB_NOMEM;
}
- n->_private = (void *) (uintptr_t) 1;
+ n->_private = create_private(1);
+ if (n->_private == NULL) {
+ xmlFreeNode(n);
+ free(name);
+ return HUBBUB_NOMEM;
+ }
if (tag->n_attributes > 0 && add_attributes(ctx, (void *) n,
tag->attributes, tag->n_attributes) != HUBBUB_OK) {
+ free(n->_private);
xmlFreeNode(n);
free(name);
return HUBBUB_NOMEM;
@@ -427,6 +496,7 @@ hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
/* Memory exhaustion */
if (form == NULL) {
+ free(n->_private);
xmlFreeNode(n);
free(name);
return HUBBUB_NOMEM;
@@ -453,7 +523,11 @@ hubbub_error create_text(void *ctx, const hubbub_string *data, void **result)
if (n == NULL) {
return HUBBUB_NOMEM;
}
- n->_private = (void *) (uintptr_t) 1;
+ n->_private = create_private(1);
+ if (n->_private == NULL) {
+ xmlFreeNode(n);
+ return HUBBUB_NOMEM;
+ }
*result = (void *) n;
@@ -463,17 +537,18 @@ hubbub_error create_text(void *ctx, const hubbub_string *data, void **result)
hubbub_error ref_node(void *ctx, void *node)
{
hubbub_ctx *c = (hubbub_ctx *) ctx;
+ hubbub_private *pvt;
if (node == c->document) {
xmlDoc *n = (xmlDoc *) node;
- uintptr_t count = (uintptr_t) n->_private;
+ pvt = n->_private;
- n->_private = (void *) (++count);
+ pvt->refcnt++;
} else {
xmlNode *n = (xmlNode *) node;
- uintptr_t count = (uintptr_t) n->_private;
+ pvt = n->_private;
- n->_private = (void *) (++count);
+ pvt->refcnt++;
}
return HUBBUB_OK;
@@ -482,23 +557,25 @@ hubbub_error ref_node(void *ctx, void *node)
hubbub_error unref_node(void *ctx, void *node)
{
hubbub_ctx *c = (hubbub_ctx *) ctx;
+ hubbub_private *pvt;
if (node == c->document) {
xmlDoc *n = (xmlDoc *) node;
- uintptr_t count = (uintptr_t) n->_private;
+ pvt = n->_private;
- assert(count != 0 && "Node has refcount of zero");
+ assert(pvt->refcnt != 0 && "Node has refcount of zero");
- n->_private = (void *) (--count);
+ pvt->refcnt--;
} else {
xmlNode *n = (xmlNode *) node;
- uintptr_t count = (uintptr_t) n->_private;
+ pvt = n->_private;
- assert(count != 0 && "Node has refcount of zero");
+ assert(pvt->refcnt != 0 && "Node has refcount of zero");
- n->_private = (void *) (--count);
+ pvt->refcnt--;
- if (count == 0 && n->parent == NULL) {
+ if (pvt->refcnt == 0 && n->parent == NULL) {
+ free(pvt);
xmlFreeNode(n);
}
}
@@ -585,13 +662,18 @@ hubbub_error remove_child(void *ctx, void *parent, void *child, void **result)
hubbub_error clone_node(void *ctx, void *node, bool deep, void **result)
{
xmlNode *n = (xmlNode *) node;
+ xmlNode *copy = xmlCopyNode(n, deep ? 1 : 2);
- *result = xmlCopyNode(n, deep ? 1 : 2);
+ if (copy == NULL)
+ return HUBBUB_NOMEM;
- if (*result == NULL)
+ copy->_private = create_private(1);
+ if (copy->_private == NULL) {
+ xmlFreeNode(copy);
return HUBBUB_NOMEM;
+ }
- ((xmlNode *)(*result))->_private = (void *) (uintptr_t) 1;
+ *result = copy;
return HUBBUB_OK;
}
diff --git a/render/parser_binding.h b/render/parser_binding.h
index 1641058bd..afab250eb 100644
--- a/render/parser_binding.h
+++ b/render/parser_binding.h
@@ -23,9 +23,17 @@
#include <libxml/tree.h>
+struct box;
struct form;
struct form_control;
+/**
+ * Private data attached to each DOM node
+ */
+typedef struct binding_private {
+ struct box *box; /**< Root box if ELEMENT node, or NULL */
+} binding_private;
+
typedef enum binding_error {
BINDING_OK,
BINDING_NOMEM,
@@ -57,5 +65,7 @@ xmlDocPtr binding_get_document(void *ctx, binding_quirks_mode *quirks);
struct form *binding_get_forms(void *ctx);
struct form_control *binding_get_control_for_node(void *ctx, xmlNodePtr node);
+void binding_destroy_document(xmlDocPtr doc);
+
#endif