1 /*
2  * This file is part of Hubbub.
3  * Licensed under the MIT License,
4  *                http://www.opensource.org/licenses/mit-license.php
5  * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org>
6  */
7 
8 #include <assert.h>
9 #include <string.h>
10 #include <strings.h>
11 
12 #include <stdio.h>
13 
14 #include "treebuilder/modes.h"
15 #include "treebuilder/internal.h"
16 #include "treebuilder/treebuilder.h"
17 #include "utils/utils.h"
18 #include "utils/string.h"
19 
20 
21 #define S(x)   x, SLEN(x)
22 
23 static const struct {
24 	const char *name;
25 	size_t len;
26 	element_type type;
27 } name_type_map[] = {
28 	{ S("address"), ADDRESS },
29 	{ S("area"), AREA },
30 	{ S("article"), ARTICLE },
31 	{ S("aside"), ASIDE },
32 	{ S("base"), BASE },
33 	{ S("basefont"), BASEFONT },
34 	{ S("bgsound"), BGSOUND },
35 	{ S("blockquote"), BLOCKQUOTE },
36 	{ S("body"), BODY },
37 	{ S("br"), BR },
38 	{ S("center"), CENTER },
39 	{ S("col"), COL },
40 	{ S("colgroup"), COLGROUP },
41 	{ S("command"), COMMAND },
42 	{ S("dd"), DD },
43 	{ S("details"), DETAILS },
44 	{ S("dialog"), DIALOG },
45 	{ S("dir"), DIR },
46 	{ S("div"), DIV },
47 	{ S("dl"), DL },
48 	{ S("dt"), DT },
49 	{ S("embed"), EMBED },
50 	{ S("fieldset"), FIELDSET },
51 	{ S("figcaption"), FIGCAPTION },
52 	{ S("figure"), FIGURE },
53 	{ S("footer"), FOOTER },
54 	{ S("form"), FORM },
55 	{ S("frame"), FRAME },
56 	{ S("frameset"), FRAMESET },
57 	{ S("h1"), H1 },
58 	{ S("h2"), H2 },
59 	{ S("h3"), H3 },
60 	{ S("h4"), H4 },
61 	{ S("h5"), H5 },
62 	{ S("h6"), H6 },
63 	{ S("head"), HEAD },
64 	{ S("hr"), HR },
65 	{ S("iframe"), IFRAME },
66 	{ S("image"), IMAGE },
67 	{ S("img"), IMG },
68 	{ S("input"), INPUT },
69 	{ S("isindex"), ISINDEX },
70 	{ S("li"), LI },
71 	{ S("link"), LINK },
72 	{ S("listing"), LISTING },
73 	{ S("menu"), MENU },
74 	{ S("meta"), META },
75 	{ S("noembed"), NOEMBED },
76 	{ S("noframes"), NOFRAMES },
77 	{ S("noscript"), NOSCRIPT },
78 	{ S("ol"), OL },
79 	{ S("optgroup"), OPTGROUP },
80 	{ S("option"), OPTION },
81 	{ S("output"), OUTPUT },
82 	{ S("p"), P },
83 	{ S("param"), PARAM },
84 	{ S("plaintext"), PLAINTEXT },
85 	{ S("pre"), PRE },
86 	{ S("script"), SCRIPT },
87 	{ S("select"), SELECT },
88 	{ S("spacer"), SPACER },
89 	{ S("style"), STYLE },
90 	{ S("summary"), SUMMARY },
91 	{ S("tbody"), TBODY },
92 	{ S("textarea"), TEXTAREA },
93 	{ S("tfoot"), TFOOT },
94 	{ S("thead"), THEAD },
95 	{ S("title"), TITLE },
96 	{ S("tr"), TR },
97 	{ S("ul"), UL },
98 	{ S("wbr"), WBR },
99 
100 	{ S("applet"), APPLET },
101 	{ S("button"), BUTTON },
102 	{ S("caption"), CAPTION },
103 	{ S("html"), HTML },
104 	{ S("marquee"), MARQUEE },
105 	{ S("object"), OBJECT },
106 	{ S("table"), TABLE },
107 	{ S("td"), TD },
108 	{ S("th"), TH },
109 
110 	{ S("a"), A },
111 	{ S("b"), B },
112 	{ S("big"), BIG },
113 	{ S("em"), EM },
114 	{ S("font"), FONT },
115 	{ S("i"), I },
116 	{ S("nobr"), NOBR },
117 	{ S("s"), S },
118 	{ S("small"), SMALL },
119 	{ S("strike"), STRIKE },
120 	{ S("strong"), STRONG },
121 	{ S("tt"), TT },
122 	{ S("u"), U },
123 
124 	{ S("xmp"), XMP },
125 
126 	{ S("math"), MATH },
127 	{ S("mglyph"), MGLYPH },
128 	{ S("malignmark"), MALIGNMARK },
129 	{ S("mi"), MI },
130 	{ S("mo"), MO },
131 	{ S("mn"), MN },
132 	{ S("ms"), MS },
133 	{ S("mtext"), MTEXT },
134 	{ S("annotation-xml"), ANNOTATION_XML },
135 
136 	{ S("svg"), SVG },
137 	{ S("desc"), DESC },
138 	{ S("foreignobject"), FOREIGNOBJECT },
139 };
140 
141 static bool is_form_associated(element_type type);
142 
143 /**
144  * Create a hubbub treebuilder
145  *
146  * \param tokeniser    Underlying tokeniser instance
147  * \param treebuilder  Pointer to location to receive treebuilder instance
148  * \return HUBBUB_OK on success,
149  *         HUBBUB_BADPARM on bad parameters
150  *         HUBBUB_NOMEM on memory exhaustion
151  */
hubbub_treebuilder_create(hubbub_tokeniser * tokeniser,hubbub_treebuilder ** treebuilder)152 hubbub_error hubbub_treebuilder_create(hubbub_tokeniser *tokeniser,
153 		hubbub_treebuilder **treebuilder)
154 {
155 	hubbub_error error;
156 	hubbub_treebuilder *tb;
157 	hubbub_tokeniser_optparams tokparams;
158 
159 	if (tokeniser == NULL || treebuilder == NULL)
160 		return HUBBUB_BADPARM;
161 
162 	tb = malloc(sizeof(hubbub_treebuilder));
163 	if (tb == NULL)
164 		return HUBBUB_NOMEM;
165 
166 	tb->tokeniser = tokeniser;
167 
168 	tb->tree_handler = NULL;
169 
170 	memset(&tb->context, 0, sizeof(hubbub_treebuilder_context));
171 	tb->context.mode = INITIAL;
172 
173 	tb->context.element_stack = malloc(
174 			ELEMENT_STACK_CHUNK * sizeof(element_context));
175 	if (tb->context.element_stack == NULL) {
176 		free(tb);
177 		return HUBBUB_NOMEM;
178 	}
179 	tb->context.stack_alloc = ELEMENT_STACK_CHUNK;
180 	/* We rely on HTML not being equal to zero to determine
181 	 * if the first item in the stack is in use. Assert this here. */
182 	assert(HTML != 0);
183 	tb->context.element_stack[0].type = (element_type) 0;
184 
185 	tb->context.strip_leading_lr = false;
186 	tb->context.frameset_ok = true;
187 
188 	tb->error_handler = NULL;
189 	tb->error_pw = NULL;
190 
191 	tokparams.token_handler.handler = hubbub_treebuilder_token_handler;
192 	tokparams.token_handler.pw = tb;
193 
194 	error = hubbub_tokeniser_setopt(tokeniser,
195 			HUBBUB_TOKENISER_TOKEN_HANDLER, &tokparams);
196 	if (error != HUBBUB_OK) {
197 		free(tb->context.element_stack);
198 		free(tb);
199 		return error;
200 	}
201 
202 	*treebuilder = tb;
203 
204 	return HUBBUB_OK;
205 }
206 
207 /**
208  * Destroy a hubbub treebuilder
209  *
210  * \param treebuilder  The treebuilder instance to destroy
211  * \return HUBBUB_OK on success, appropriate error otherwise
212  */
hubbub_treebuilder_destroy(hubbub_treebuilder * treebuilder)213 hubbub_error hubbub_treebuilder_destroy(hubbub_treebuilder *treebuilder)
214 {
215 	formatting_list_entry *entry, *next;
216 	hubbub_tokeniser_optparams tokparams;
217 
218 	if (treebuilder == NULL)
219 		return HUBBUB_BADPARM;
220 
221 	tokparams.token_handler.handler = NULL;
222 	tokparams.token_handler.pw = NULL;
223 
224 	hubbub_tokeniser_setopt(treebuilder->tokeniser,
225 			HUBBUB_TOKENISER_TOKEN_HANDLER, &tokparams);
226 
227 	/* Clean up context */
228 	if (treebuilder->tree_handler != NULL) {
229 		uint32_t n;
230 
231 		if (treebuilder->context.head_element != NULL) {
232 			treebuilder->tree_handler->unref_node(
233 					treebuilder->tree_handler->ctx,
234 					treebuilder->context.head_element);
235 		}
236 
237 		if (treebuilder->context.form_element != NULL) {
238 			treebuilder->tree_handler->unref_node(
239 					treebuilder->tree_handler->ctx,
240 					treebuilder->context.form_element);
241 		}
242 
243 		if (treebuilder->context.document != NULL) {
244 			treebuilder->tree_handler->unref_node(
245 					treebuilder->tree_handler->ctx,
246 					treebuilder->context.document);
247 		}
248 
249 		for (n = treebuilder->context.current_node;
250 				n > 0; n--) {
251 			treebuilder->tree_handler->unref_node(
252 				treebuilder->tree_handler->ctx,
253 				treebuilder->context.element_stack[n].node);
254 		}
255 		if (treebuilder->context.element_stack[0].type == HTML) {
256 			treebuilder->tree_handler->unref_node(
257 				treebuilder->tree_handler->ctx,
258 				treebuilder->context.element_stack[0].node);
259 		}
260 	}
261 	free(treebuilder->context.element_stack);
262 	treebuilder->context.element_stack = NULL;
263 
264 	for (entry = treebuilder->context.formatting_list; entry != NULL;
265 			entry = next) {
266 		next = entry->next;
267 
268 		if (treebuilder->tree_handler != NULL) {
269 			treebuilder->tree_handler->unref_node(
270 					treebuilder->tree_handler->ctx,
271 					entry->details.node);
272 		}
273 
274 		free(entry);
275 	}
276 
277 	free(treebuilder);
278 
279 	return HUBBUB_OK;
280 }
281 
282 /**
283  * Configure a hubbub treebuilder
284  *
285  * \param treebuilder  The treebuilder instance to configure
286  * \param type         The option type to configure
287  * \param params       Pointer to option-specific parameters
288  * \return HUBBUB_OK on success, appropriate error otherwise.
289  */
hubbub_treebuilder_setopt(hubbub_treebuilder * treebuilder,hubbub_treebuilder_opttype type,hubbub_treebuilder_optparams * params)290 hubbub_error hubbub_treebuilder_setopt(hubbub_treebuilder *treebuilder,
291 		hubbub_treebuilder_opttype type,
292 		hubbub_treebuilder_optparams *params)
293 {
294 	if (treebuilder == NULL || params == NULL)
295 		return HUBBUB_BADPARM;
296 
297 	switch (type) {
298 	case HUBBUB_TREEBUILDER_ERROR_HANDLER:
299 		treebuilder->error_handler = params->error_handler.handler;
300 		treebuilder->error_pw = params->error_handler.pw;
301 		break;
302 	case HUBBUB_TREEBUILDER_TREE_HANDLER:
303 		treebuilder->tree_handler = params->tree_handler;
304 		break;
305 	case HUBBUB_TREEBUILDER_DOCUMENT_NODE:
306 		treebuilder->context.document = params->document_node;
307 		break;
308 	case HUBBUB_TREEBUILDER_ENABLE_SCRIPTING:
309 		treebuilder->context.enable_scripting =
310 				params->enable_scripting;
311 		break;
312 	}
313 
314 	return HUBBUB_OK;
315 }
316 
317 /**
318  * Handle tokeniser emitting a token
319  *
320  * \param token  The emitted token
321  * \param pw     Pointer to treebuilder instance
322  */
hubbub_treebuilder_token_handler(const hubbub_token * token,void * pw)323 hubbub_error hubbub_treebuilder_token_handler(const hubbub_token *token,
324 		void *pw)
325 {
326 	hubbub_treebuilder *treebuilder = (hubbub_treebuilder *) pw;
327 	hubbub_error err = HUBBUB_REPROCESS;
328 
329 	/* Do nothing if we have no document node or there's no tree handler */
330 	if (treebuilder->context.document == NULL ||
331 			treebuilder->tree_handler == NULL)
332 		return HUBBUB_OK;
333 
334 	assert((signed) treebuilder->context.current_node >= 0);
335 
336 /* A slightly nasty debugging hook, but very useful */
337 #ifdef NDEBUG
338 # define mode(x) \
339 		case x:
340 #else
341 # define mode(x) \
342 		case x: \
343 			printf( #x "\n");
344 #endif
345 
346 	while (err == HUBBUB_REPROCESS) {
347 		switch (treebuilder->context.mode) {
348 		mode(INITIAL)
349 			err = handle_initial(treebuilder, token);
350 			break;
351 		mode(BEFORE_HTML)
352 			err = handle_before_html(treebuilder, token);
353 			break;
354 		mode(BEFORE_HEAD)
355 			err = handle_before_head(treebuilder, token);
356 			break;
357 		mode(IN_HEAD)
358 			err = handle_in_head(treebuilder, token);
359 			break;
360 		mode(IN_HEAD_NOSCRIPT)
361 			err = handle_in_head_noscript(treebuilder, token);
362 			break;
363 		mode(AFTER_HEAD)
364 			err = handle_after_head(treebuilder, token);
365 			break;
366 		mode(IN_BODY)
367 			err = handle_in_body(treebuilder, token);
368 			break;
369 		mode(IN_TABLE)
370 			err = handle_in_table(treebuilder, token);
371 			break;
372 		mode(IN_CAPTION)
373 			err = handle_in_caption(treebuilder, token);
374 			break;
375 		mode(IN_COLUMN_GROUP)
376 			err = handle_in_column_group(treebuilder, token);
377 			break;
378 		mode(IN_TABLE_BODY)
379 			err = handle_in_table_body(treebuilder, token);
380 			break;
381 		mode(IN_ROW)
382 			err = handle_in_row(treebuilder, token);
383 			break;
384 		mode(IN_CELL)
385 			err = handle_in_cell(treebuilder, token);
386 			break;
387 		mode(IN_SELECT)
388 			err = handle_in_select(treebuilder, token);
389 			break;
390 		mode(IN_SELECT_IN_TABLE)
391 			err = handle_in_select_in_table(treebuilder, token);
392 			break;
393 		mode(IN_FOREIGN_CONTENT)
394 			err = handle_in_foreign_content(treebuilder, token);
395 			break;
396 		mode(AFTER_BODY)
397 			err = handle_after_body(treebuilder, token);
398 			break;
399 		mode(IN_FRAMESET)
400 			err = handle_in_frameset(treebuilder, token);
401 			break;
402 		mode(AFTER_FRAMESET)
403 			err = handle_after_frameset(treebuilder, token);
404 			break;
405 		mode(AFTER_AFTER_BODY)
406 			err = handle_after_after_body(treebuilder, token);
407 			break;
408 		mode(AFTER_AFTER_FRAMESET)
409 			err = handle_after_after_frameset(treebuilder, token);
410 			break;
411 		mode(GENERIC_RCDATA)
412 			err = handle_generic_rcdata(treebuilder, token);
413 			break;
414 		}
415 	}
416 
417 	return err;
418 }
419 
420 
421 /**
422  * Process a character token in cases where we expect only whitespace
423  *
424  * \param treebuilder               The treebuilder instance
425  * \param token                     The character token
426  * \param insert_into_current_node  Whether to insert whitespace into
427  *                                  current node
428  * \return HUBBUB_REPROCESS if the token needs reprocessing
429  *                          (token data updated to skip any leading whitespace),
430  *         HUBBUB_OK if it contained only whitespace,
431  *         appropriate error otherwise
432  */
process_characters_expect_whitespace(hubbub_treebuilder * treebuilder,const hubbub_token * token,bool insert_into_current_node)433 hubbub_error process_characters_expect_whitespace(
434 		hubbub_treebuilder *treebuilder,
435 		const hubbub_token *token, bool insert_into_current_node)
436 {
437 	const uint8_t *data = token->data.character.ptr;
438 	size_t len = token->data.character.len;
439 	size_t c;
440 
441 	for (c = 0; c < len; c++) {
442 		if (data[c] != 0x09 && data[c] != 0x0A &&
443 				data[c] != 0x0C && data[c] != 0x20)
444 			break;
445 	}
446 
447 	if (c > 0 && insert_into_current_node) {
448 		hubbub_error error;
449 		hubbub_string temp;
450 
451 		temp.ptr = data;
452 		temp.len = c;
453 
454 		error = append_text(treebuilder, &temp);
455 		if (error != HUBBUB_OK)
456 			return error;
457 	}
458 
459 	/* Non-whitespace characters in token, so reprocess */
460 	if (c != len) {
461 		/* Update token data to strip leading whitespace */
462 		((hubbub_token *) token)->data.character.ptr += c;
463 		((hubbub_token *) token)->data.character.len -= c;
464 
465 		return HUBBUB_REPROCESS;
466 	}
467 
468 	return HUBBUB_OK;
469 }
470 
471 /**
472  * Process a comment token, appending it to the given parent
473  *
474  * \param treebuilder  The treebuilder instance
475  * \param token        The comment token
476  * \param parent       The node to append to
477  * \return HUBBUB_OK on success, appropriate error otherwise
478  */
process_comment_append(hubbub_treebuilder * treebuilder,const hubbub_token * token,void * parent)479 hubbub_error process_comment_append(hubbub_treebuilder *treebuilder,
480 		const hubbub_token *token, void *parent)
481 {
482 	hubbub_error error = HUBBUB_OK;
483 	element_type type = current_node(treebuilder);
484 	void *comment, *appended;
485 
486 	error = treebuilder->tree_handler->create_comment(
487 			treebuilder->tree_handler->ctx,
488 			&token->data.comment, &comment);
489 	if (error != HUBBUB_OK)
490 		return error;
491 
492 	if (treebuilder->context.in_table_foster &&
493 			(type == TABLE || type == TBODY || type == TFOOT ||
494 			type == THEAD || type == TR)) {
495 		error = aa_insert_into_foster_parent(treebuilder, comment,
496 				&appended);
497 	} else {
498 		error = treebuilder->tree_handler->append_child(
499 				treebuilder->tree_handler->ctx,
500 				parent, comment, &appended);
501 	}
502 
503 	if (error == HUBBUB_OK) {
504 		treebuilder->tree_handler->unref_node(
505 				treebuilder->tree_handler->ctx, appended);
506 	}
507 
508 	treebuilder->tree_handler->unref_node(
509 			treebuilder->tree_handler->ctx, comment);
510 
511 	return error;
512 }
513 
514 /**
515  * Trigger parsing of generic (R)CDATA
516  *
517  * \param treebuilder  The treebuilder instance
518  * \param token        The current token
519  * \param rcdata       True for RCDATA, false for CDATA
520  * \return HUBBUB_OK on success, appropriate error otherwise
521  */
parse_generic_rcdata(hubbub_treebuilder * treebuilder,const hubbub_token * token,bool rcdata)522 hubbub_error parse_generic_rcdata(hubbub_treebuilder *treebuilder,
523 		const hubbub_token *token, bool rcdata)
524 {
525 	hubbub_error error;
526 	element_type type;
527 	hubbub_tokeniser_optparams params;
528 
529 	type = element_type_from_name(treebuilder, &token->data.tag.name);
530 
531 	error = insert_element(treebuilder, &token->data.tag, true);
532 	if (error != HUBBUB_OK)
533 		return error;
534 
535 	params.content_model.model = rcdata ? HUBBUB_CONTENT_MODEL_RCDATA
536 					    : HUBBUB_CONTENT_MODEL_CDATA;
537 	error = hubbub_tokeniser_setopt(treebuilder->tokeniser,
538 				HUBBUB_TOKENISER_CONTENT_MODEL, &params);
539 	/* There is no way that setopt can fail. Ensure this. */
540 	assert(error == HUBBUB_OK);
541 
542 	treebuilder->context.collect.mode = treebuilder->context.mode;
543 	treebuilder->context.collect.type = type;
544 
545 	treebuilder->context.mode = GENERIC_RCDATA;
546 
547 	return HUBBUB_OK;
548 }
549 
550 /**
551  * Determine if an element is in (table) scope
552  *
553  * \param treebuilder  Treebuilder to look in
554  * \param type         Element type to find
555  * \param in_table     Whether we're looking in table scope
556  * \return Element stack index, or 0 if not in scope
557  */
element_in_scope(hubbub_treebuilder * treebuilder,element_type type,bool in_table)558 uint32_t element_in_scope(hubbub_treebuilder *treebuilder,
559 		element_type type, bool in_table)
560 {
561 	uint32_t node;
562 
563 	if (treebuilder->context.element_stack == NULL)
564 		return 0;
565 
566 	assert((signed) treebuilder->context.current_node >= 0);
567 
568 	for (node = treebuilder->context.current_node; node > 0; node--) {
569 		hubbub_ns node_ns =
570 				treebuilder->context.element_stack[node].ns;
571 		element_type node_type =
572 				treebuilder->context.element_stack[node].type;
573 
574 		if (node_type == type)
575 			return node;
576 
577 		if (node_type == TABLE)
578 			break;
579 
580 		/* The list of element types given in the spec here are the
581 		 * scoping elements excluding TABLE and HTML. TABLE is handled
582 		 * in the previous conditional and HTML should only occur
583 		 * as the first node in the stack, which is never processed
584 		 * in this loop. */
585 		if (!in_table && (is_scoping_element(node_type) ||
586 				(node_type == FOREIGNOBJECT &&
587 						node_ns == HUBBUB_NS_SVG))) {
588 			break;
589 		}
590 	}
591 
592 	return 0;
593 }
594 
595 /**
596  * Reconstruct the list of active formatting elements
597  *
598  * \param treebuilder  Treebuilder instance containing list
599  * \return HUBBUB_OK on success, appropriate error otherwise.
600  */
reconstruct_active_formatting_list(hubbub_treebuilder * treebuilder)601 hubbub_error reconstruct_active_formatting_list(hubbub_treebuilder *treebuilder)
602 {
603 	hubbub_error error = HUBBUB_OK;
604 	formatting_list_entry *entry, *initial_entry;
605 	uint32_t sp = treebuilder->context.current_node;
606 
607 	if (treebuilder->context.formatting_list == NULL)
608 		return HUBBUB_OK;
609 
610 	entry = treebuilder->context.formatting_list_end;
611 
612 	/* Assumption: HTML and TABLE elements are not inserted into the list */
613 	if (is_scoping_element(entry->details.type) || entry->stack_index != 0)
614 		return HUBBUB_OK;
615 
616 	while (entry->prev != NULL) {
617 		entry = entry->prev;
618 
619 		if (is_scoping_element(entry->details.type) ||
620 				entry->stack_index != 0) {
621 			entry = entry->next;
622 			break;
623 		}
624 	}
625 
626 	/* Save initial entry for later */
627 	initial_entry = entry;
628 
629 	/* Process formatting list entries, cloning nodes and
630 	 * inserting them into the DOM and element stack */
631 	while (entry != NULL) {
632 		void *clone, *appended;
633 		bool foster;
634 		element_type type = current_node(treebuilder);
635 
636 		error = treebuilder->tree_handler->clone_node(
637 				treebuilder->tree_handler->ctx,
638 				entry->details.node,
639 				false,
640 				&clone);
641 		if (error != HUBBUB_OK)
642 			goto cleanup;
643 
644 		foster = treebuilder->context.in_table_foster &&
645 				(type == TABLE || type == TBODY ||
646 					type == TFOOT || type == THEAD ||
647 					type == TR);
648 
649 		if (foster) {
650 			error = aa_insert_into_foster_parent(treebuilder,
651 					clone, &appended);
652 		} else {
653 			error = treebuilder->tree_handler->append_child(
654 					treebuilder->tree_handler->ctx,
655 					treebuilder->context.element_stack[
656 					treebuilder->context.current_node].node,
657 					clone,
658 					&appended);
659 		}
660 
661 		/* No longer interested in clone */
662 		treebuilder->tree_handler->unref_node(
663 				treebuilder->tree_handler->ctx,
664 				clone);
665 
666 		if (error != HUBBUB_OK)
667 			goto cleanup;
668 
669 		error = element_stack_push(treebuilder, entry->details.ns,
670 				entry->details.type, appended);
671 		if (error != HUBBUB_OK) {
672 			remove_node_from_dom(treebuilder, appended);
673 
674 			treebuilder->tree_handler->unref_node(
675 					treebuilder->tree_handler->ctx,
676 					appended);
677 
678 			goto cleanup;
679 		}
680 
681 		entry = entry->next;
682 	}
683 
684 	/* Now, replace the formatting list entries */
685 	for (entry = initial_entry; entry != NULL; entry = entry->next) {
686 		void *node;
687 		hubbub_ns prev_ns;
688 		element_type prev_type;
689 		void *prev_node;
690 		uint32_t prev_stack_index;
691 
692 		node = treebuilder->context.element_stack[++sp].node;
693 
694 		treebuilder->tree_handler->ref_node(
695 				treebuilder->tree_handler->ctx, node);
696 
697 		error = formatting_list_replace(treebuilder, entry,
698 				entry->details.ns, entry->details.type,
699 				node, sp,
700 				&prev_ns, &prev_type, &prev_node,
701 				&prev_stack_index);
702 		/* Cannot fail. Ensure this. */
703 		assert(error == HUBBUB_OK);
704 
705 		treebuilder->tree_handler->unref_node(
706 				treebuilder->tree_handler->ctx,
707 				prev_node);
708 	}
709 
710 	return HUBBUB_OK;
711 
712 cleanup:
713 	/* An error occurred while cloning nodes and inserting them.
714 	 * We must restore the state on entry here. */
715 	while (treebuilder->context.current_node > sp) {
716 		hubbub_ns ns;
717 		element_type type;
718 		void *node;
719 
720 		element_stack_pop(treebuilder, &ns, &type, &node);
721 
722 		remove_node_from_dom(treebuilder, node);
723 
724 		treebuilder->tree_handler->unref_node(
725 				treebuilder->tree_handler->ctx,
726 				node);
727 	}
728 
729 	return error;
730 }
731 
732 /**
733  * Remove a node from the DOM
734  *
735  * \param treebuilder  Treebuilder instance
736  * \param node         Node to remove
737  * \return HUBBUB_OK on success, appropriate error otherwise.
738  */
remove_node_from_dom(hubbub_treebuilder * treebuilder,void * node)739 hubbub_error remove_node_from_dom(hubbub_treebuilder *treebuilder, void *node)
740 {
741 	hubbub_error err;
742 	void *parent = NULL;
743 	void *removed;
744 
745 	err = treebuilder->tree_handler->get_parent(
746 			treebuilder->tree_handler->ctx,
747 			node, false, &parent);
748 	if (err != HUBBUB_OK)
749 		return err;
750 
751 	if (parent != NULL) {
752 		err = treebuilder->tree_handler->remove_child(
753 				treebuilder->tree_handler->ctx,
754 				parent, node, &removed);
755 		if (err != HUBBUB_OK)
756 			return err;
757 
758 		treebuilder->tree_handler->unref_node(
759 				treebuilder->tree_handler->ctx,
760 				parent);
761 
762 		treebuilder->tree_handler->unref_node(
763 				treebuilder->tree_handler->ctx,
764 				removed);
765 	}
766 
767 	return HUBBUB_OK;
768 }
769 
770 /**
771  * Clear the list of active formatting elements up to the last marker
772  *
773  * \param treebuilder  The treebuilder instance containing the list
774  */
clear_active_formatting_list_to_marker(hubbub_treebuilder * treebuilder)775 void clear_active_formatting_list_to_marker(hubbub_treebuilder *treebuilder)
776 {
777 	formatting_list_entry *entry;
778 	bool done = false;
779 
780 	while ((entry = treebuilder->context.formatting_list_end) != NULL) {
781 		hubbub_ns ns;
782 		element_type type;
783 		void *node;
784 		uint32_t stack_index;
785 
786 		if (is_scoping_element(entry->details.type))
787 			done = true;
788 
789 		formatting_list_remove(treebuilder, entry,
790 				&ns, &type, &node, &stack_index);
791 
792 		treebuilder->tree_handler->unref_node(
793 				treebuilder->tree_handler->ctx,
794 				node);
795 
796 		if (done)
797 			break;
798 	}
799 }
800 
801 /**
802  * Create element and insert it into the DOM,
803  * potentially pushing it on the stack
804  *
805  * \param treebuilder  The treebuilder instance
806  * \param tag          The element to insert
807  * \param push         Whether to push the element onto the stack
808  * \return HUBBUB_OK on success, appropriate error otherwise.
809  */
insert_element(hubbub_treebuilder * treebuilder,const hubbub_tag * tag,bool push)810 hubbub_error insert_element(hubbub_treebuilder *treebuilder,
811 		const hubbub_tag *tag, bool push)
812 {
813 	element_type type = current_node(treebuilder);
814 	hubbub_error error;
815 	void *node, *appended;
816 
817 	error = treebuilder->tree_handler->create_element(
818 			treebuilder->tree_handler->ctx, tag, &node);
819 	if (error != HUBBUB_OK)
820 		return error;
821 
822 	if (treebuilder->context.in_table_foster &&
823 			(type == TABLE || type == TBODY || type == TFOOT ||
824 			type == THEAD || type == TR)) {
825 		error = aa_insert_into_foster_parent(treebuilder, node,
826 				&appended);
827 	} else {
828 		error = treebuilder->tree_handler->append_child(
829 				treebuilder->tree_handler->ctx,
830 				treebuilder->context.element_stack[
831 					treebuilder->context.current_node].node,
832 				node, &appended);
833 	}
834 
835 	/* No longer interested in node */
836 	treebuilder->tree_handler->unref_node(
837 			treebuilder->tree_handler->ctx, node);
838 
839 	if (error != HUBBUB_OK)
840 		return error;
841 
842 	type = element_type_from_name(treebuilder, &tag->name);
843 	if (treebuilder->context.form_element != NULL &&
844 			is_form_associated(type)) {
845 		/* Consideration of @form is left to the client */
846 		error = treebuilder->tree_handler->form_associate(
847 				treebuilder->tree_handler->ctx,
848 				treebuilder->context.form_element,
849 				appended);
850 		if (error != HUBBUB_OK) {
851 			remove_node_from_dom(treebuilder, appended);
852 
853 			treebuilder->tree_handler->unref_node(
854 					treebuilder->tree_handler->ctx,
855 					appended);
856 
857 			return error;
858 		}
859 	}
860 
861 	if (push) {
862 		error = element_stack_push(treebuilder,
863 				tag->ns, type, appended);
864 		if (error != HUBBUB_OK) {
865 			remove_node_from_dom(treebuilder, appended);
866 
867 			treebuilder->tree_handler->unref_node(
868 					treebuilder->tree_handler->ctx,
869 					appended);
870 			return error;
871 		}
872 	} else {
873 		treebuilder->tree_handler->unref_node(
874 				treebuilder->tree_handler->ctx, appended);
875 	}
876 
877 	return HUBBUB_OK;
878 }
879 
880 /**
881  * Close implied end tags
882  *
883  * \param treebuilder  The treebuilder instance
884  * \param except       Tag type to exclude from processing [DD,DT,LI,OPTION,
885  *                     OPTGROUP,P,RP,RT], UNKNOWN to exclude nothing
886  */
close_implied_end_tags(hubbub_treebuilder * treebuilder,element_type except)887 void close_implied_end_tags(hubbub_treebuilder *treebuilder,
888 		element_type except)
889 {
890 	element_type type;
891 
892 	type = treebuilder->context.element_stack[
893 			treebuilder->context.current_node].type;
894 
895 	while (type == DD || type == DT || type == LI || type == OPTION ||
896 			type == OPTGROUP || type == P || type == RP ||
897 			type == RT) {
898 		hubbub_ns ns;
899 		element_type otype;
900 		void *node;
901 
902 		if (except != UNKNOWN && type == except)
903 			break;
904 
905 		element_stack_pop(treebuilder, &ns, &otype, &node);
906 
907 		treebuilder->tree_handler->unref_node(
908 				treebuilder->tree_handler->ctx,
909 				node);
910 
911 		type = treebuilder->context.element_stack[
912 				treebuilder->context.current_node].type;
913 	}
914 }
915 
916 /**
917  * Reset the insertion mode
918  *
919  * \param treebuilder  The treebuilder to reset
920  */
reset_insertion_mode(hubbub_treebuilder * treebuilder)921 void reset_insertion_mode(hubbub_treebuilder *treebuilder)
922 {
923 	uint32_t node;
924 	element_context *stack = treebuilder->context.element_stack;
925 
926 	/** \todo fragment parsing algorithm */
927 
928 	for (node = treebuilder->context.current_node; node > 0; node--) {
929 		if (stack[node].ns != HUBBUB_NS_HTML) {
930 			treebuilder->context.mode = IN_FOREIGN_CONTENT;
931 			treebuilder->context.second_mode = IN_BODY;
932 			break;
933 		}
934 
935 		switch (stack[node].type) {
936 		case SELECT:
937 			/* fragment case */
938 			break;
939 		case TD:
940 		case TH:
941 			treebuilder->context.mode = IN_CELL;
942 			return;
943 		case TR:
944 			treebuilder->context.mode = IN_ROW;
945 			return;
946 		case TBODY:
947 		case TFOOT:
948 		case THEAD:
949 			treebuilder->context.mode = IN_TABLE_BODY;
950 			return;
951 		case CAPTION:
952 			treebuilder->context.mode = IN_CAPTION;
953 			return;
954 		case COLGROUP:
955 			/* fragment case */
956 			break;
957 		case TABLE:
958 			treebuilder->context.mode = IN_TABLE;
959 			return;
960 		case HEAD:
961 			/* fragment case */
962 			break;
963 		case BODY:
964 			treebuilder->context.mode = IN_BODY;
965 			return;
966 		case FRAMESET:
967 			/* fragment case */
968 			break;
969 		case HTML:
970 			/* fragment case */
971 			break;
972 		default:
973 			break;
974 		}
975 	}
976 }
977 
978 /**
979  * Script processing and execution
980  *
981  * \param treebuilder  The treebuilder instance
982  * \return HUBBUB_OK on success, appropriate error otherwise
983  */
complete_script(hubbub_treebuilder * treebuilder)984 hubbub_error complete_script(hubbub_treebuilder *treebuilder)
985 {
986 	hubbub_error error = HUBBUB_OK;
987 	error = treebuilder->tree_handler->complete_script(
988 		treebuilder->tree_handler->ctx,
989 		treebuilder->context.element_stack[
990 			treebuilder->context.current_node].node);
991 	return error;
992 }
993 
994 /**
995  * Append text to the current node, inserting into the last child of the
996  * current node, iff it's a Text node.
997  *
998  * \param treebuilder  The treebuilder instance
999  * \param string       The string to append
1000  * \return HUBBUB_OK on success, appropriate error otherwise
1001  */
append_text(hubbub_treebuilder * treebuilder,const hubbub_string * string)1002 hubbub_error append_text(hubbub_treebuilder *treebuilder,
1003 		const hubbub_string *string)
1004 {
1005 	element_type type = current_node(treebuilder);
1006 	hubbub_error error = HUBBUB_OK;
1007 	void *text, *appended;
1008 
1009 	error = treebuilder->tree_handler->create_text(
1010 			treebuilder->tree_handler->ctx, string, &text);
1011 	if (error != HUBBUB_OK)
1012 		return error;
1013 
1014 	if (treebuilder->context.in_table_foster &&
1015 			(type == TABLE || type == TBODY || type == TFOOT ||
1016 			type == THEAD || type == TR)) {
1017 		error = aa_insert_into_foster_parent(treebuilder, text,
1018 				&appended);
1019 	} else {
1020 		error = treebuilder->tree_handler->append_child(
1021 				treebuilder->tree_handler->ctx,
1022 				treebuilder->context.element_stack[
1023 					treebuilder->context.current_node].node,
1024 						text, &appended);
1025 	}
1026 
1027 	if (error == HUBBUB_OK) {
1028 		treebuilder->tree_handler->unref_node(
1029 				treebuilder->tree_handler->ctx, appended);
1030 	}
1031 
1032 	treebuilder->tree_handler->unref_node(
1033 			treebuilder->tree_handler->ctx, text);
1034 
1035 	return error;
1036 }
1037 
1038 /**
1039  * Convert an element name into an element type
1040  *
1041  * \param treebuilder  The treebuilder instance
1042  * \param tag_name     The tag name to consider
1043  * \return The corresponding element type
1044  */
element_type_from_name(hubbub_treebuilder * treebuilder,const hubbub_string * tag_name)1045 element_type element_type_from_name(hubbub_treebuilder *treebuilder,
1046 		const hubbub_string *tag_name)
1047 {
1048 	const uint8_t *name = tag_name->ptr;
1049 	size_t len = tag_name->len;
1050 	uint32_t i;
1051 
1052 	UNUSED(treebuilder);
1053 
1054 	/** \todo optimise this */
1055 
1056 	for (i = 0; i < N_ELEMENTS(name_type_map); i++) {
1057 		if (name_type_map[i].len != len)
1058 			continue;
1059 
1060 		if (strncasecmp(name_type_map[i].name,
1061 				(const char *) name, len) == 0)
1062 			return name_type_map[i].type;
1063 	}
1064 
1065 	return UNKNOWN;
1066 }
1067 
1068 /**
1069  * Determine if a node is a special element
1070  *
1071  * \param type  Node type to consider
1072  * \return True iff node is a special element
1073  */
is_special_element(element_type type)1074 bool is_special_element(element_type type)
1075 {
1076 	return (type <= WBR);
1077 }
1078 
1079 /**
1080  * Determine if a node is a scoping element
1081  *
1082  * \param type  Node type to consider
1083  * \return True iff node is a scoping element
1084  */
is_scoping_element(element_type type)1085 bool is_scoping_element(element_type type)
1086 {
1087 	return (type >= APPLET && type <= TH);
1088 }
1089 
1090 /**
1091  * Determine if a node is a formatting element
1092  *
1093  * \param type  Node type to consider
1094  * \return True iff node is a formatting element
1095  */
is_formatting_element(element_type type)1096 bool is_formatting_element(element_type type)
1097 {
1098 	return (type >= A && type <= U);
1099 }
1100 
1101 /**
1102  * Determine if a node is a phrasing element
1103  *
1104  * \param type  Node type to consider
1105  * \return True iff node is a phrasing element
1106  */
is_phrasing_element(element_type type)1107 bool is_phrasing_element(element_type type)
1108 {
1109 	return (type > U);
1110 }
1111 
1112 /**
1113  * Determine if a node is form associated
1114  *
1115  * \param type  Node type to consider
1116  * \return True iff node is form associated
1117  */
is_form_associated(element_type type)1118 bool is_form_associated(element_type type)
1119 {
1120 	return type == FIELDSET || type == LABEL || type == INPUT ||
1121 			type == BUTTON || type == SELECT || type == TEXTAREA ||
1122 			type == OUTPUT;
1123 }
1124 
1125 /**
1126  * Push an element onto the stack of open elements
1127  *
1128  * \param treebuilder  The treebuilder instance containing the stack
1129  * \param ns           The namespace of element being pushed
1130  * \param type         The type of element being pushed
1131  * \param node         The node to push
1132  * \return HUBBUB_OK on success, appropriate error otherwise.
1133  */
element_stack_push(hubbub_treebuilder * treebuilder,hubbub_ns ns,element_type type,void * node)1134 hubbub_error element_stack_push(hubbub_treebuilder *treebuilder,
1135 		hubbub_ns ns, element_type type, void *node)
1136 {
1137 	uint32_t slot = treebuilder->context.current_node + 1;
1138 
1139 	if (slot >= treebuilder->context.stack_alloc) {
1140 		element_context *temp = realloc(
1141 				treebuilder->context.element_stack,
1142 				(treebuilder->context.stack_alloc +
1143 					ELEMENT_STACK_CHUNK) *
1144 					sizeof(element_context));
1145 
1146 		if (temp == NULL)
1147 			return HUBBUB_NOMEM;
1148 
1149 		treebuilder->context.element_stack = temp;
1150 		treebuilder->context.stack_alloc += ELEMENT_STACK_CHUNK;
1151 	}
1152 
1153 	treebuilder->context.element_stack[slot].ns = ns;
1154 	treebuilder->context.element_stack[slot].type = type;
1155 	treebuilder->context.element_stack[slot].node = node;
1156 
1157 	treebuilder->context.current_node = slot;
1158 
1159 	return HUBBUB_OK;
1160 }
1161 
1162 /**
1163  * Pop an element off the stack of open elements
1164  *
1165  * \param treebuilder  The treebuilder instance containing the stack
1166  * \param ns           Pointer to location to receive element namespace
1167  * \param type         Pointer to location to receive element type
1168  * \param node         Pointer to location to receive node
1169  * \return HUBBUB_OK on success, appropriate error otherwise.
1170  */
element_stack_pop(hubbub_treebuilder * treebuilder,hubbub_ns * ns,element_type * type,void ** node)1171 hubbub_error element_stack_pop(hubbub_treebuilder *treebuilder,
1172 		hubbub_ns *ns, element_type *type, void **node)
1173 {
1174 	element_context *stack = treebuilder->context.element_stack;
1175 	uint32_t slot = treebuilder->context.current_node;
1176 	formatting_list_entry *entry;
1177 
1178 	/* We're popping a table, find previous */
1179 	if (stack[slot].type == TABLE) {
1180 		uint32_t t;
1181 		for (t = slot - 1; t > 0; t--) {
1182 			if (stack[t].type == TABLE)
1183 				break;
1184 		}
1185 	}
1186 
1187 	if (is_formatting_element(stack[slot].type) ||
1188 			(is_scoping_element(stack[slot].type) &&
1189 			stack[slot].type != HTML &&
1190 			stack[slot].type != TABLE)) {
1191 		/* Find occurrences of the node we're about to pop in the list
1192 		 * of active formatting elements. We need to invalidate their
1193 		 * stack index information. */
1194 		for (entry = treebuilder->context.formatting_list_end;
1195 				entry != NULL; entry = entry->prev) {
1196 			/** \todo Can we optimise this?
1197 			 * (i.e. by not traversing the entire list) */
1198 			if (entry->stack_index == slot)
1199 				entry->stack_index = 0;
1200 		}
1201 	}
1202 
1203 	*ns = stack[slot].ns;
1204 	*type = stack[slot].type;
1205 	*node = stack[slot].node;
1206 
1207 	/** \todo reduce allocated stack size once there's enough free */
1208 
1209 	treebuilder->context.current_node = slot - 1;
1210 	assert((signed) treebuilder->context.current_node >= 0);
1211 
1212 	return HUBBUB_OK;
1213 }
1214 
1215 /**
1216  * Pop elements until an element of type "element" has been popped.
1217  *
1218  * \return HUBBUB_OK on success, appropriate error otherwise.
1219  */
element_stack_pop_until(hubbub_treebuilder * treebuilder,element_type type)1220 hubbub_error element_stack_pop_until(hubbub_treebuilder *treebuilder,
1221 		element_type type)
1222 {
1223 	element_type otype = UNKNOWN;
1224 	void *node;
1225 	hubbub_ns ns;
1226 
1227 	while (otype != type) {
1228 		element_stack_pop(treebuilder, &ns, &otype, &node);
1229 
1230 		treebuilder->tree_handler->unref_node(
1231 				treebuilder->tree_handler->ctx, node);
1232 
1233 		assert((signed) treebuilder->context.current_node >= 0);
1234 	}
1235 
1236 	return HUBBUB_OK;
1237 }
1238 
1239 /**
1240  * Remove a node from the stack of open elements
1241  *
1242  * \param treebuilder  The treebuilder instance
1243  * \param index        The index of the node to remove
1244  * \param ns           Pointer to location to receive namespace
1245  * \param type         Pointer to location to receive type
1246  * \param removed      Pointer to location to receive removed node
1247  * \return HUBBUB_OK on success, appropriate error otherwise.
1248  */
element_stack_remove(hubbub_treebuilder * treebuilder,uint32_t index,hubbub_ns * ns,element_type * type,void ** removed)1249 hubbub_error element_stack_remove(hubbub_treebuilder *treebuilder,
1250 		uint32_t index, hubbub_ns *ns, element_type *type,
1251 		void **removed)
1252 {
1253 	element_context *stack = treebuilder->context.element_stack;
1254 	uint32_t n;
1255 
1256 	assert(index <= treebuilder->context.current_node);
1257 
1258 	/* Scan over subsequent entries in the stack,
1259 	 * searching for them in the list of active formatting
1260 	 * entries. If found, update the corresponding
1261 	 * formatting list entry's stack index to match the
1262 	 * new stack location */
1263 	for (n = index + 1; n <= treebuilder->context.current_node; n++) {
1264 		if (is_formatting_element(stack[n].type) ||
1265 				(is_scoping_element(stack[n].type) &&
1266 				stack[n].type != HTML &&
1267 				stack[n].type != TABLE)) {
1268 			formatting_list_entry *e;
1269 
1270 			for (e = treebuilder->context.formatting_list_end;
1271 					e != NULL; e = e->prev) {
1272 				if (e->stack_index == n)
1273 					e->stack_index--;
1274 			}
1275 		}
1276 	}
1277 
1278 	*ns = stack[index].ns;
1279 	*type = stack[index].type;
1280 	*removed = stack[index].node;
1281 
1282 	/* Now, shuffle the stack up one, removing node in the process */
1283 	if (index < treebuilder->context.current_node) {
1284 		memmove(&stack[index], &stack[index + 1],
1285 				(treebuilder->context.current_node - index) *
1286 				sizeof(element_context));
1287 	}
1288 
1289 	treebuilder->context.current_node--;
1290 
1291 	return HUBBUB_OK;
1292 }
1293 
1294 /**
1295  * Find the stack index of the current table.
1296  */
current_table(hubbub_treebuilder * treebuilder)1297 uint32_t current_table(hubbub_treebuilder *treebuilder)
1298 {
1299 	element_context *stack = treebuilder->context.element_stack;
1300 	size_t t;
1301 
1302 	for (t = treebuilder->context.current_node; t != 0; t--) {
1303 		if (stack[t].type == TABLE)
1304 			return t;
1305 	}
1306 
1307 	/* fragment case */
1308 	return 0;
1309 }
1310 
1311 /**
1312  * Peek at the top element of the element stack.
1313  *
1314  * \param treebuilder  Treebuilder instance
1315  * \return Element type on the top of the stack
1316  */
current_node(hubbub_treebuilder * treebuilder)1317 element_type current_node(hubbub_treebuilder *treebuilder)
1318 {
1319 	return treebuilder->context.element_stack
1320 			[treebuilder->context.current_node].type;
1321 }
1322 
1323 /**
1324  * Peek at the element below the top of the element stack.
1325  *
1326  * \param treebuilder  Treebuilder instance
1327  * \return Element type of the element one below the top of the stack
1328  */
prev_node(hubbub_treebuilder * treebuilder)1329 element_type prev_node(hubbub_treebuilder *treebuilder)
1330 {
1331 	if (treebuilder->context.current_node == 0)
1332 		return UNKNOWN;
1333 
1334 	return treebuilder->context.element_stack
1335 			[treebuilder->context.current_node - 1].type;
1336 }
1337 
1338 
1339 
1340 /**
1341  * Append an element to the end of the list of active formatting elements
1342  *
1343  * \param treebuilder  Treebuilder instance containing list
1344  * \param ns           Namespace of node being inserted
1345  * \param type         Type of node being inserted
1346  * \param node         Node being inserted
1347  * \param stack_index  Index into stack of open elements
1348  * \return HUBBUB_OK on success, appropriate error otherwise
1349  */
formatting_list_append(hubbub_treebuilder * treebuilder,hubbub_ns ns,element_type type,void * node,uint32_t stack_index)1350 hubbub_error formatting_list_append(hubbub_treebuilder *treebuilder,
1351 		hubbub_ns ns, element_type type, void *node,
1352 		uint32_t stack_index)
1353 {
1354 	formatting_list_entry *entry;
1355 
1356 	entry = malloc(sizeof(formatting_list_entry));
1357 	if (entry == NULL)
1358 		return HUBBUB_NOMEM;
1359 
1360 	entry->details.ns = ns;
1361 	entry->details.type = type;
1362 	entry->details.node = node;
1363 	entry->stack_index = stack_index;
1364 
1365 	entry->prev = treebuilder->context.formatting_list_end;
1366 	entry->next = NULL;
1367 
1368 	if (entry->prev != NULL)
1369 		entry->prev->next = entry;
1370 	else
1371 		treebuilder->context.formatting_list = entry;
1372 
1373 	treebuilder->context.formatting_list_end = entry;
1374 
1375 	return HUBBUB_OK;
1376 }
1377 
1378 /**
1379  * Insert an element into the list of active formatting elements
1380  *
1381  * \param treebuilder  Treebuilder instance containing list
1382  * \param prev         Previous entry
1383  * \param next         Next entry
1384  * \param ns           Namespace of node being inserted
1385  * \param type         Type of node being inserted
1386  * \param node         Node being inserted
1387  * \param stack_index  Index into stack of open elements
1388  * \return HUBBUB_OK on success, appropriate error otherwise
1389  */
formatting_list_insert(hubbub_treebuilder * treebuilder,formatting_list_entry * prev,formatting_list_entry * next,hubbub_ns ns,element_type type,void * node,uint32_t stack_index)1390 hubbub_error formatting_list_insert(hubbub_treebuilder *treebuilder,
1391 		formatting_list_entry *prev, formatting_list_entry *next,
1392 		hubbub_ns ns, element_type type, void *node,
1393 		uint32_t stack_index)
1394 {
1395 	formatting_list_entry *entry;
1396 
1397 	if (prev != NULL) {
1398 		assert(prev->next == next);
1399 	}
1400 
1401 	if (next != NULL) {
1402 		assert(next->prev == prev);
1403 	}
1404 
1405 	entry = malloc(sizeof(formatting_list_entry));
1406 	if (entry == NULL)
1407 		return HUBBUB_NOMEM;
1408 
1409 	entry->details.ns = ns;
1410 	entry->details.type = type;
1411 	entry->details.node = node;
1412 	entry->stack_index = stack_index;
1413 
1414 	entry->prev = prev;
1415 	entry->next = next;
1416 
1417 	if (entry->prev != NULL)
1418 		entry->prev->next = entry;
1419 	else
1420 		treebuilder->context.formatting_list = entry;
1421 
1422 	if (entry->next != NULL)
1423 		entry->next->prev = entry;
1424 	else
1425 		treebuilder->context.formatting_list_end = entry;
1426 
1427 	return HUBBUB_OK;
1428 }
1429 
1430 
1431 /**
1432  * Remove an element from the list of active formatting elements
1433  *
1434  * \param treebuilder  Treebuilder instance containing list
1435  * \param entry        The item to remove
1436  * \param ns           Pointer to location to receive namespace of node
1437  * \param type         Pointer to location to receive type of node
1438  * \param node         Pointer to location to receive node
1439  * \param stack_index  Pointer to location to receive stack index
1440  * \return HUBBUB_OK on success, appropriate error otherwise.
1441  */
formatting_list_remove(hubbub_treebuilder * treebuilder,formatting_list_entry * entry,hubbub_ns * ns,element_type * type,void ** node,uint32_t * stack_index)1442 hubbub_error formatting_list_remove(hubbub_treebuilder *treebuilder,
1443 		formatting_list_entry *entry,
1444 		hubbub_ns *ns, element_type *type, void **node,
1445 		uint32_t *stack_index)
1446 {
1447 	*ns = entry->details.ns;
1448 	*type = entry->details.type;
1449 	*node = entry->details.node;
1450 	*stack_index = entry->stack_index;
1451 
1452 	if (entry->prev == NULL) {
1453 		assert(treebuilder->context.formatting_list == entry);
1454 		treebuilder->context.formatting_list = entry->next;
1455 	} else {
1456 		assert(treebuilder->context.formatting_list != entry);
1457 		entry->prev->next = entry->next;
1458 	}
1459 
1460 	if (entry->next == NULL) {
1461 		assert(treebuilder->context.formatting_list_end == entry);
1462 		treebuilder->context.formatting_list_end = entry->prev;
1463 	} else {
1464 		assert(treebuilder->context.formatting_list_end != entry);
1465 		entry->next->prev = entry->prev;
1466 	}
1467 
1468 	free(entry);
1469 
1470 	return HUBBUB_OK;
1471 }
1472 
1473 /**
1474  * Remove an element from the list of active formatting elements
1475  *
1476  * \param treebuilder   Treebuilder instance containing list
1477  * \param entry         The item to replace
1478  * \param ns            Replacement node namespace
1479  * \param type          Replacement node type
1480  * \param node          Replacement node
1481  * \param stack_index   Replacement stack index
1482  * \param ons           Pointer to location to receive old namespace
1483  * \param otype         Pointer to location to receive old type
1484  * \param onode         Pointer to location to receive old node
1485  * \param ostack_index  Pointer to location to receive old stack index
1486  * \return HUBBUB_OK on success, appropriate error otherwise
1487  */
formatting_list_replace(hubbub_treebuilder * treebuilder,formatting_list_entry * entry,hubbub_ns ns,element_type type,void * node,uint32_t stack_index,hubbub_ns * ons,element_type * otype,void ** onode,uint32_t * ostack_index)1488 hubbub_error formatting_list_replace(hubbub_treebuilder *treebuilder,
1489 		formatting_list_entry *entry,
1490 		hubbub_ns ns, element_type type, void *node,
1491 		uint32_t stack_index,
1492 		hubbub_ns *ons, element_type *otype, void **onode,
1493 		uint32_t *ostack_index)
1494 {
1495 	UNUSED(treebuilder);
1496 
1497 	*ons = entry->details.ns;
1498 	*otype = entry->details.type;
1499 	*onode = entry->details.node;
1500 	*ostack_index = entry->stack_index;
1501 
1502 	entry->details.ns = ns;
1503 	entry->details.type = type;
1504 	entry->details.node = node;
1505 	entry->stack_index = stack_index;
1506 
1507 	return HUBBUB_OK;
1508 }
1509 
1510 
1511 
1512 #ifndef NDEBUG
1513 
1514 /**
1515  * Dump an element stack to the given file pointer
1516  *
1517  * \param treebuilder  The treebuilder instance
1518  * \param fp           The file to dump to
1519  */
element_stack_dump(hubbub_treebuilder * treebuilder,FILE * fp)1520 void element_stack_dump(hubbub_treebuilder *treebuilder, FILE *fp)
1521 {
1522 	element_context *stack = treebuilder->context.element_stack;
1523 	uint32_t i;
1524 
1525 	for (i = 0; i <= treebuilder->context.current_node; i++) {
1526 		fprintf(fp, "%u: %s %p\n",
1527 				i,
1528 				element_type_to_name(stack[i].type),
1529 				stack[i].node);
1530 	}
1531 }
1532 
1533 /**
1534  * Dump a formatting list to the given file pointer
1535  *
1536  * \param treebuilder  The treebuilder instance
1537  * \param fp           The file to dump to
1538  */
formatting_list_dump(hubbub_treebuilder * treebuilder,FILE * fp)1539 void formatting_list_dump(hubbub_treebuilder *treebuilder, FILE *fp)
1540 {
1541 	formatting_list_entry *entry;
1542 
1543 	for (entry = treebuilder->context.formatting_list; entry != NULL;
1544 			entry = entry->next) {
1545 		fprintf(fp, "%s %p %u\n",
1546 				element_type_to_name(entry->details.type),
1547 				entry->details.node, entry->stack_index);
1548 	}
1549 }
1550 
1551 /**
1552  * Convert an element type to a name
1553  *
1554  * \param type  The element type
1555  * \return Pointer to name
1556  */
element_type_to_name(element_type type)1557 const char *element_type_to_name(element_type type)
1558 {
1559 	size_t i;
1560 
1561 	for (i = 0;
1562 			i < sizeof(name_type_map) / sizeof(name_type_map[0]);
1563 			i++) {
1564 		if (name_type_map[i].type == type)
1565 			return name_type_map[i].name;
1566 	}
1567 
1568 	return "UNKNOWN";
1569 }
1570 #endif
1571 
1572