Hubbub
treebuilder.c
Go to the documentation of this file.
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 
11 #include <stdio.h>
12 
13 #include "treebuilder/modes.h"
14 #include "treebuilder/internal.h"
16 #include "utils/utils.h"
17 #include "utils/string.h"
18 
19 
20 #define S(x) x, SLEN(x)
21 
22 static const struct {
23  const char *name;
24  size_t len;
26 } name_type_map[] = {
27  { S("address"), ADDRESS }, { S("area"), AREA },
28  { S("base"), BASE }, { S("basefont"), BASEFONT },
29  { S("bgsound"), BGSOUND }, { S("blockquote"), BLOCKQUOTE },
30  { S("body"), BODY }, { S("br"), BR },
31  { S("center"), CENTER }, { S("col"), COL },
32  { S("colgroup"), COLGROUP }, { S("dd"), DD },
33  { S("dir"), DIR }, { S("div"), DIV },
34  { S("dl"), DL }, { S("dt"), DT },
35  { S("embed"), EMBED }, { S("fieldset"), FIELDSET },
36  { S("form"), FORM }, { S("frame"), FRAME },
37  { S("frameset"), FRAMESET }, { S("h1"), H1 },
38  { S("h2"), H2 }, { S("h3"), H3 },
39  { S("h4"), H4 }, { S("h5"), H5 },
40  { S("h6"), H6 }, { S("head"), HEAD },
41  { S("hr"), HR }, { S("iframe"), IFRAME },
42  { S("image"), IMAGE }, { S("img"), IMG },
43  { S("input"), INPUT }, { S("isindex"), ISINDEX },
44  { S("li"), LI }, { S("link"), LINK },
45  { S("listing"), LISTING },
46  { S("menu"), MENU },
47  { S("meta"), META }, { S("noembed"), NOEMBED },
48  { S("noframes"), NOFRAMES }, { S("noscript"), NOSCRIPT },
49  { S("ol"), OL }, { S("optgroup"), OPTGROUP },
50  { S("option"), OPTION }, { S("output"), OUTPUT },
51  { S("p"), P }, { S("param"), PARAM },
52  { S("plaintext"), PLAINTEXT }, { S("pre"), PRE },
53  { S("script"), SCRIPT }, { S("select"), SELECT },
54  { S("spacer"), SPACER }, { S("style"), STYLE },
55  { S("tbody"), TBODY }, { S("textarea"), TEXTAREA },
56  { S("tfoot"), TFOOT }, { S("thead"), THEAD },
57  { S("title"), TITLE }, { S("tr"), TR },
58  { S("ul"), UL }, { S("wbr"), WBR },
59  { S("applet"), APPLET }, { S("button"), BUTTON },
60  { S("caption"), CAPTION }, { S("html"), HTML },
61  { S("marquee"), MARQUEE }, { S("object"), OBJECT },
62  { S("table"), TABLE }, { S("td"), TD },
63  { S("th"), TH },
64  { S("a"), A }, { S("b"), B },
65  { S("big"), BIG }, { S("em"), EM },
66  { S("font"), FONT }, { S("i"), I },
67  { S("nobr"), NOBR }, { S("s"), S },
68  { S("small"), SMALL }, { S("strike"), STRIKE },
69  { S("strong"), STRONG }, { S("tt"), TT },
70  { S("u"), U }, { S("xmp"), XMP },
71 
72  { S("math"), MATH }, { S("mglyph"), MGLYPH },
73  { S("malignmark"), MALIGNMARK },
74  { S("mi"), MI }, { S("mo"), MO },
75  { S("mn"), MN }, { S("ms"), MS },
76  { S("mtext"), MTEXT }, { S("annotation-xml"), ANNOTATION_XML },
77 
78  { S("svg"), SVG }, { S("desc"), DESC },
79  { S("foreignobject"), FOREIGNOBJECT },
80 };
81 
83 
94  hubbub_treebuilder **treebuilder)
95 {
96  hubbub_error error;
99 
100  if (tokeniser == NULL || treebuilder == NULL)
101  return HUBBUB_BADPARM;
102 
103  tb = malloc(sizeof(hubbub_treebuilder));
104  if (tb == NULL)
105  return HUBBUB_NOMEM;
106 
107  tb->tokeniser = tokeniser;
108 
109  tb->tree_handler = NULL;
110 
111  memset(&tb->context, 0, sizeof(hubbub_treebuilder_context));
112  tb->context.mode = INITIAL;
113 
114  tb->context.element_stack = malloc(
116  if (tb->context.element_stack == NULL) {
117  free(tb);
118  return HUBBUB_NOMEM;
119  }
121  /* We rely on HTML not being equal to zero to determine
122  * if the first item in the stack is in use. Assert this here. */
123  assert(HTML != 0);
124  tb->context.element_stack[0].type = (element_type) 0;
125 
126  tb->context.strip_leading_lr = false;
127  tb->context.frameset_ok = true;
128 
129  tb->error_handler = NULL;
130  tb->error_pw = NULL;
131 
133  tokparams.token_handler.pw = tb;
134 
135  error = hubbub_tokeniser_setopt(tokeniser,
136  HUBBUB_TOKENISER_TOKEN_HANDLER, &tokparams);
137  if (error != HUBBUB_OK) {
138  free(tb->context.element_stack);
139  free(tb);
140  return error;
141  }
142 
143  *treebuilder = tb;
144 
145  return HUBBUB_OK;
146 }
147 
155 {
156  formatting_list_entry *entry, *next;
157  hubbub_tokeniser_optparams tokparams;
158 
159  if (treebuilder == NULL)
160  return HUBBUB_BADPARM;
161 
162  tokparams.token_handler.handler = NULL;
163  tokparams.token_handler.pw = NULL;
164 
165  hubbub_tokeniser_setopt(treebuilder->tokeniser,
166  HUBBUB_TOKENISER_TOKEN_HANDLER, &tokparams);
167 
168  /* Clean up context */
169  if (treebuilder->tree_handler != NULL) {
170  uint32_t n;
171 
172  if (treebuilder->context.head_element != NULL) {
173  treebuilder->tree_handler->unref_node(
174  treebuilder->tree_handler->ctx,
175  treebuilder->context.head_element);
176  }
177 
178  if (treebuilder->context.form_element != NULL) {
179  treebuilder->tree_handler->unref_node(
180  treebuilder->tree_handler->ctx,
181  treebuilder->context.form_element);
182  }
183 
184  if (treebuilder->context.document != NULL) {
185  treebuilder->tree_handler->unref_node(
186  treebuilder->tree_handler->ctx,
187  treebuilder->context.document);
188  }
189 
190  for (n = treebuilder->context.current_node;
191  n > 0; n--) {
192  treebuilder->tree_handler->unref_node(
193  treebuilder->tree_handler->ctx,
194  treebuilder->context.element_stack[n].node);
195  }
196  if (treebuilder->context.element_stack[0].type == HTML) {
197  treebuilder->tree_handler->unref_node(
198  treebuilder->tree_handler->ctx,
199  treebuilder->context.element_stack[0].node);
200  }
201  }
202  free(treebuilder->context.element_stack);
203  treebuilder->context.element_stack = NULL;
204 
205  for (entry = treebuilder->context.formatting_list; entry != NULL;
206  entry = next) {
207  next = entry->next;
208 
209  if (treebuilder->tree_handler != NULL) {
210  treebuilder->tree_handler->unref_node(
211  treebuilder->tree_handler->ctx,
212  entry->details.node);
213  }
214 
215  free(entry);
216  }
217 
218  free(treebuilder);
219 
220  return HUBBUB_OK;
221 }
222 
234 {
235  if (treebuilder == NULL || params == NULL)
236  return HUBBUB_BADPARM;
237 
238  switch (type) {
240  treebuilder->error_handler = params->error_handler.handler;
241  treebuilder->error_pw = params->error_handler.pw;
242  break;
244  treebuilder->tree_handler = params->tree_handler;
245  break;
247  treebuilder->context.document = params->document_node;
248  break;
250  treebuilder->context.enable_scripting =
251  params->enable_scripting;
252  break;
253  }
254 
255  return HUBBUB_OK;
256 }
257 
265  void *pw)
266 {
267  hubbub_treebuilder *treebuilder = (hubbub_treebuilder *) pw;
269 
270  /* Do nothing if we have no document node or there's no tree handler */
271  if (treebuilder->context.document == NULL ||
272  treebuilder->tree_handler == NULL)
273  return HUBBUB_OK;
274 
275  assert((signed) treebuilder->context.current_node >= 0);
276 
277 /* A slightly nasty debugging hook, but very useful */
278 #ifdef NDEBUG
279 # define mode(x) \
280  case x:
281 #else
282 # define mode(x) \
283  case x: \
284  printf( #x "\n");
285 #endif
286 
287  while (err == HUBBUB_REPROCESS) {
288  switch (treebuilder->context.mode) {
289  mode(INITIAL)
290  err = handle_initial(treebuilder, token);
291  break;
293  err = handle_before_html(treebuilder, token);
294  break;
296  err = handle_before_head(treebuilder, token);
297  break;
298  mode(IN_HEAD)
299  err = handle_in_head(treebuilder, token);
300  break;
302  err = handle_in_head_noscript(treebuilder, token);
303  break;
305  err = handle_after_head(treebuilder, token);
306  break;
307  mode(IN_BODY)
308  err = handle_in_body(treebuilder, token);
309  break;
310  mode(IN_TABLE)
311  err = handle_in_table(treebuilder, token);
312  break;
314  err = handle_in_caption(treebuilder, token);
315  break;
317  err = handle_in_column_group(treebuilder, token);
318  break;
320  err = handle_in_table_body(treebuilder, token);
321  break;
322  mode(IN_ROW)
323  err = handle_in_row(treebuilder, token);
324  break;
325  mode(IN_CELL)
326  err = handle_in_cell(treebuilder, token);
327  break;
328  mode(IN_SELECT)
329  err = handle_in_select(treebuilder, token);
330  break;
332  err = handle_in_select_in_table(treebuilder, token);
333  break;
335  err = handle_in_foreign_content(treebuilder, token);
336  break;
338  err = handle_after_body(treebuilder, token);
339  break;
341  err = handle_in_frameset(treebuilder, token);
342  break;
344  err = handle_after_frameset(treebuilder, token);
345  break;
347  err = handle_after_after_body(treebuilder, token);
348  break;
350  err = handle_after_after_frameset(treebuilder, token);
351  break;
353  err = handle_generic_rcdata(treebuilder, token);
354  break;
355  }
356  }
357 
358  return err;
359 }
360 
361 
375  hubbub_treebuilder *treebuilder,
376  const hubbub_token *token, bool insert_into_current_node)
377 {
378  const uint8_t *data = token->data.character.ptr;
379  size_t len = token->data.character.len;
380  size_t c;
381 
382  for (c = 0; c < len; c++) {
383  if (data[c] != 0x09 && data[c] != 0x0A &&
384  data[c] != 0x0C && data[c] != 0x20)
385  break;
386  }
387 
388  if (c > 0 && insert_into_current_node) {
389  hubbub_error error;
390  hubbub_string temp;
391 
392  temp.ptr = data;
393  temp.len = c;
394 
395  error = append_text(treebuilder, &temp);
396  if (error != HUBBUB_OK)
397  return error;
398  }
399 
400  /* Non-whitespace characters in token, so reprocess */
401  if (c != len) {
402  /* Update token data to strip leading whitespace */
403  ((hubbub_token *) token)->data.character.ptr += c;
404  ((hubbub_token *) token)->data.character.len -= c;
405 
406  return HUBBUB_REPROCESS;
407  }
408 
409  return HUBBUB_OK;
410 }
411 
421  const hubbub_token *token, void *parent)
422 {
423  hubbub_error error = HUBBUB_OK;
424  element_type type = current_node(treebuilder);
425  void *comment, *appended;
426 
427  error = treebuilder->tree_handler->create_comment(
428  treebuilder->tree_handler->ctx,
429  &token->data.comment, &comment);
430  if (error != HUBBUB_OK)
431  return error;
432 
433  if (treebuilder->context.in_table_foster &&
434  (type == TABLE || type == TBODY || type == TFOOT ||
435  type == THEAD || type == TR)) {
436  error = aa_insert_into_foster_parent(treebuilder, comment,
437  &appended);
438  } else {
439  error = treebuilder->tree_handler->append_child(
440  treebuilder->tree_handler->ctx,
441  parent, comment, &appended);
442  }
443 
444  if (error == HUBBUB_OK) {
445  treebuilder->tree_handler->unref_node(
446  treebuilder->tree_handler->ctx, appended);
447  }
448 
449  treebuilder->tree_handler->unref_node(
450  treebuilder->tree_handler->ctx, comment);
451 
452  return error;
453 }
454 
464  const hubbub_token *token, bool rcdata)
465 {
466  hubbub_error error;
469 
470  type = element_type_from_name(treebuilder, &token->data.tag.name);
471 
472  error = insert_element(treebuilder, &token->data.tag, true);
473  if (error != HUBBUB_OK)
474  return error;
475 
478  error = hubbub_tokeniser_setopt(treebuilder->tokeniser,
480  /* There is no way that setopt can fail. Ensure this. */
481  assert(error == HUBBUB_OK);
482 
483  treebuilder->context.collect.mode = treebuilder->context.mode;
484  treebuilder->context.collect.type = type;
485 
486  treebuilder->context.mode = GENERIC_RCDATA;
487 
488  return HUBBUB_OK;
489 }
490 
499 uint32_t element_in_scope(hubbub_treebuilder *treebuilder,
500  element_type type, bool in_table)
501 {
502  uint32_t node;
503 
504  if (treebuilder->context.element_stack == NULL)
505  return 0;
506 
507  assert((signed) treebuilder->context.current_node >= 0);
508 
509  for (node = treebuilder->context.current_node; node > 0; node--) {
510  hubbub_ns node_ns =
511  treebuilder->context.element_stack[node].ns;
512  element_type node_type =
513  treebuilder->context.element_stack[node].type;
514 
515  if (node_type == type)
516  return node;
517 
518  if (node_type == TABLE)
519  break;
520 
521  /* The list of element types given in the spec here are the
522  * scoping elements excluding TABLE and HTML. TABLE is handled
523  * in the previous conditional and HTML should only occur
524  * as the first node in the stack, which is never processed
525  * in this loop. */
526  if (!in_table && (is_scoping_element(node_type) ||
527  (node_type == FOREIGNOBJECT &&
528  node_ns == HUBBUB_NS_SVG))) {
529  break;
530  }
531  }
532 
533  return 0;
534 }
535 
543 {
544  hubbub_error error = HUBBUB_OK;
545  formatting_list_entry *entry, *initial_entry;
546  uint32_t sp = treebuilder->context.current_node;
547 
548  if (treebuilder->context.formatting_list == NULL)
549  return HUBBUB_OK;
550 
551  entry = treebuilder->context.formatting_list_end;
552 
553  /* Assumption: HTML and TABLE elements are not inserted into the list */
554  if (is_scoping_element(entry->details.type) || entry->stack_index != 0)
555  return HUBBUB_OK;
556 
557  while (entry->prev != NULL) {
558  entry = entry->prev;
559 
560  if (is_scoping_element(entry->details.type) ||
561  entry->stack_index != 0) {
562  entry = entry->next;
563  break;
564  }
565  }
566 
567  /* Save initial entry for later */
568  initial_entry = entry;
569 
570  /* Process formatting list entries, cloning nodes and
571  * inserting them into the DOM and element stack */
572  while (entry != NULL) {
573  void *clone, *appended;
574  bool foster;
575  element_type type = current_node(treebuilder);
576 
577  error = treebuilder->tree_handler->clone_node(
578  treebuilder->tree_handler->ctx,
579  entry->details.node,
580  false,
581  &clone);
582  if (error != HUBBUB_OK)
583  goto cleanup;
584 
585  foster = treebuilder->context.in_table_foster &&
586  (type == TABLE || type == TBODY ||
587  type == TFOOT || type == THEAD ||
588  type == TR);
589 
590  if (foster) {
591  error = aa_insert_into_foster_parent(treebuilder,
592  clone, &appended);
593  } else {
594  error = treebuilder->tree_handler->append_child(
595  treebuilder->tree_handler->ctx,
596  treebuilder->context.element_stack[
597  treebuilder->context.current_node].node,
598  clone,
599  &appended);
600  }
601 
602  /* No longer interested in clone */
603  treebuilder->tree_handler->unref_node(
604  treebuilder->tree_handler->ctx,
605  clone);
606 
607  if (error != HUBBUB_OK)
608  goto cleanup;
609 
610  error = element_stack_push(treebuilder, entry->details.ns,
611  entry->details.type, appended);
612  if (error != HUBBUB_OK) {
613  remove_node_from_dom(treebuilder, appended);
614 
615  treebuilder->tree_handler->unref_node(
616  treebuilder->tree_handler->ctx,
617  appended);
618 
619  goto cleanup;
620  }
621 
622  entry = entry->next;
623  }
624 
625  /* Now, replace the formatting list entries */
626  for (entry = initial_entry; entry != NULL; entry = entry->next) {
627  void *node;
628  hubbub_ns prev_ns;
629  element_type prev_type;
630  void *prev_node;
631  uint32_t prev_stack_index;
632 
633  node = treebuilder->context.element_stack[++sp].node;
634 
635  treebuilder->tree_handler->ref_node(
636  treebuilder->tree_handler->ctx, node);
637 
638  error = formatting_list_replace(treebuilder, entry,
639  entry->details.ns, entry->details.type,
640  node, sp,
641  &prev_ns, &prev_type, &prev_node,
642  &prev_stack_index);
643  /* Cannot fail. Ensure this. */
644  assert(error == HUBBUB_OK);
645 
646  treebuilder->tree_handler->unref_node(
647  treebuilder->tree_handler->ctx,
648  prev_node);
649  }
650 
651  return HUBBUB_OK;
652 
653 cleanup:
654  /* An error occurred while cloning nodes and inserting them.
655  * We must restore the state on entry here. */
656  while (treebuilder->context.current_node > sp) {
657  hubbub_ns ns;
659  void *node;
660 
661  element_stack_pop(treebuilder, &ns, &type, &node);
662 
663  remove_node_from_dom(treebuilder, node);
664 
665  treebuilder->tree_handler->unref_node(
666  treebuilder->tree_handler->ctx,
667  node);
668  }
669 
670  return error;
671 }
672 
681 {
682  hubbub_error err;
683  void *parent = NULL;
684  void *removed;
685 
686  err = treebuilder->tree_handler->get_parent(
687  treebuilder->tree_handler->ctx,
688  node, false, &parent);
689  if (err != HUBBUB_OK)
690  return err;
691 
692  if (parent != NULL) {
693  err = treebuilder->tree_handler->remove_child(
694  treebuilder->tree_handler->ctx,
695  parent, node, &removed);
696  if (err != HUBBUB_OK)
697  return err;
698 
699  treebuilder->tree_handler->unref_node(
700  treebuilder->tree_handler->ctx,
701  parent);
702 
703  treebuilder->tree_handler->unref_node(
704  treebuilder->tree_handler->ctx,
705  removed);
706  }
707 
708  return HUBBUB_OK;
709 }
710 
717 {
718  formatting_list_entry *entry;
719  bool done = false;
720 
721  while ((entry = treebuilder->context.formatting_list_end) != NULL) {
722  hubbub_ns ns;
724  void *node;
725  uint32_t stack_index;
726 
727  if (is_scoping_element(entry->details.type))
728  done = true;
729 
730  formatting_list_remove(treebuilder, entry,
731  &ns, &type, &node, &stack_index);
732 
733  treebuilder->tree_handler->unref_node(
734  treebuilder->tree_handler->ctx,
735  node);
736 
737  if (done)
738  break;
739  }
740 }
741 
752  const hubbub_tag *tag, bool push)
753 {
754  element_type type = current_node(treebuilder);
755  hubbub_error error;
756  void *node, *appended;
757 
758  error = treebuilder->tree_handler->create_element(
759  treebuilder->tree_handler->ctx, tag, &node);
760  if (error != HUBBUB_OK)
761  return error;
762 
763  if (treebuilder->context.in_table_foster &&
764  (type == TABLE || type == TBODY || type == TFOOT ||
765  type == THEAD || type == TR)) {
766  error = aa_insert_into_foster_parent(treebuilder, node,
767  &appended);
768  } else {
769  error = treebuilder->tree_handler->append_child(
770  treebuilder->tree_handler->ctx,
771  treebuilder->context.element_stack[
772  treebuilder->context.current_node].node,
773  node, &appended);
774  }
775 
776  /* No longer interested in node */
777  treebuilder->tree_handler->unref_node(
778  treebuilder->tree_handler->ctx, node);
779 
780  if (error != HUBBUB_OK)
781  return error;
782 
783  type = element_type_from_name(treebuilder, &tag->name);
784  if (treebuilder->context.form_element != NULL &&
785  is_form_associated(type)) {
786  /* Consideration of @form is left to the client */
787  error = treebuilder->tree_handler->form_associate(
788  treebuilder->tree_handler->ctx,
789  treebuilder->context.form_element,
790  appended);
791  if (error != HUBBUB_OK) {
792  remove_node_from_dom(treebuilder, appended);
793 
794  treebuilder->tree_handler->unref_node(
795  treebuilder->tree_handler->ctx,
796  appended);
797 
798  return error;
799  }
800  }
801 
802  if (push) {
803  error = element_stack_push(treebuilder,
804  tag->ns, type, appended);
805  if (error != HUBBUB_OK) {
806  remove_node_from_dom(treebuilder, appended);
807 
808  treebuilder->tree_handler->unref_node(
809  treebuilder->tree_handler->ctx,
810  appended);
811  return error;
812  }
813  } else {
814  treebuilder->tree_handler->unref_node(
815  treebuilder->tree_handler->ctx, appended);
816  }
817 
818  return HUBBUB_OK;
819 }
820 
829  element_type except)
830 {
832 
833  type = treebuilder->context.element_stack[
834  treebuilder->context.current_node].type;
835 
836  while (type == DD || type == DT || type == LI || type == OPTION ||
837  type == OPTGROUP || type == P || type == RP ||
838  type == RT) {
839  hubbub_ns ns;
840  element_type otype;
841  void *node;
842 
843  if (except != UNKNOWN && type == except)
844  break;
845 
846  element_stack_pop(treebuilder, &ns, &otype, &node);
847 
848  treebuilder->tree_handler->unref_node(
849  treebuilder->tree_handler->ctx,
850  node);
851 
852  type = treebuilder->context.element_stack[
853  treebuilder->context.current_node].type;
854  }
855 }
856 
863 {
864  uint32_t node;
865  element_context *stack = treebuilder->context.element_stack;
866 
869  for (node = treebuilder->context.current_node; node > 0; node--) {
870  if (stack[node].ns != HUBBUB_NS_HTML) {
871  treebuilder->context.mode = IN_FOREIGN_CONTENT;
872  treebuilder->context.second_mode = IN_BODY;
873  break;
874  }
875 
876  switch (stack[node].type) {
877  case SELECT:
878  /* fragment case */
879  break;
880  case TD:
881  case TH:
882  treebuilder->context.mode = IN_CELL;
883  return;
884  case TR:
885  treebuilder->context.mode = IN_ROW;
886  return;
887  case TBODY:
888  case TFOOT:
889  case THEAD:
890  treebuilder->context.mode = IN_TABLE_BODY;
891  return;
892  case CAPTION:
893  treebuilder->context.mode = IN_CAPTION;
894  return;
895  case COLGROUP:
896  /* fragment case */
897  break;
898  case TABLE:
899  treebuilder->context.mode = IN_TABLE;
900  return;
901  case HEAD:
902  /* fragment case */
903  break;
904  case BODY:
905  treebuilder->context.mode = IN_BODY;
906  return;
907  case FRAMESET:
908  /* fragment case */
909  break;
910  case HTML:
911  /* fragment case */
912  break;
913  default:
914  break;
915  }
916  }
917 }
918 
926 {
927  hubbub_error error = HUBBUB_OK;
928  error = treebuilder->tree_handler->complete_script(
929  treebuilder->tree_handler->ctx,
930  treebuilder->context.element_stack[
931  treebuilder->context.current_node].node);
932  return error;
933 }
934 
944  const hubbub_string *string)
945 {
946  element_type type = current_node(treebuilder);
947  hubbub_error error = HUBBUB_OK;
948  void *text, *appended;
949 
950  error = treebuilder->tree_handler->create_text(
951  treebuilder->tree_handler->ctx, string, &text);
952  if (error != HUBBUB_OK)
953  return error;
954 
955  if (treebuilder->context.in_table_foster &&
956  (type == TABLE || type == TBODY || type == TFOOT ||
957  type == THEAD || type == TR)) {
958  error = aa_insert_into_foster_parent(treebuilder, text,
959  &appended);
960  } else {
961  error = treebuilder->tree_handler->append_child(
962  treebuilder->tree_handler->ctx,
963  treebuilder->context.element_stack[
964  treebuilder->context.current_node].node,
965  text, &appended);
966  }
967 
968  if (error == HUBBUB_OK) {
969  treebuilder->tree_handler->unref_node(
970  treebuilder->tree_handler->ctx, appended);
971  }
972 
973  treebuilder->tree_handler->unref_node(
974  treebuilder->tree_handler->ctx, text);
975 
976  return error;
977 }
978 
987  const hubbub_string *tag_name)
988 {
989  const uint8_t *name = tag_name->ptr;
990  size_t len = tag_name->len;
991  uint32_t i;
992 
993  UNUSED(treebuilder);
994 
997  for (i = 0; i < N_ELEMENTS(name_type_map); i++) {
998  if (name_type_map[i].len != len)
999  continue;
1000 
1001  if (strncasecmp(name_type_map[i].name,
1002  (const char *) name, len) == 0)
1003  return name_type_map[i].type;
1004  }
1005 
1006  return UNKNOWN;
1007 }
1008 
1016 {
1017  return (type <= WBR);
1018 }
1019 
1027 {
1028  return (type >= APPLET && type <= TH);
1029 }
1030 
1038 {
1039  return (type >= A && type <= U);
1040 }
1041 
1049 {
1050  return (type > U);
1051 }
1052 
1060 {
1061  return type == FIELDSET || type == LABEL || type == INPUT ||
1062  type == BUTTON || type == SELECT || type == TEXTAREA ||
1063  type == OUTPUT;
1064 }
1065 
1076  hubbub_ns ns, element_type type, void *node)
1077 {
1078  uint32_t slot = treebuilder->context.current_node + 1;
1079 
1080  if (slot >= treebuilder->context.stack_alloc) {
1081  element_context *temp = realloc(
1082  treebuilder->context.element_stack,
1083  (treebuilder->context.stack_alloc +
1085  sizeof(element_context));
1086 
1087  if (temp == NULL)
1088  return HUBBUB_NOMEM;
1089 
1090  treebuilder->context.element_stack = temp;
1091  treebuilder->context.stack_alloc += ELEMENT_STACK_CHUNK;
1092  }
1093 
1094  treebuilder->context.element_stack[slot].ns = ns;
1095  treebuilder->context.element_stack[slot].type = type;
1096  treebuilder->context.element_stack[slot].node = node;
1097 
1098  treebuilder->context.current_node = slot;
1099 
1100  return HUBBUB_OK;
1101 }
1102 
1113  hubbub_ns *ns, element_type *type, void **node)
1114 {
1115  element_context *stack = treebuilder->context.element_stack;
1116  uint32_t slot = treebuilder->context.current_node;
1117  formatting_list_entry *entry;
1118 
1119  /* We're popping a table, find previous */
1120  if (stack[slot].type == TABLE) {
1121  uint32_t t;
1122  for (t = slot - 1; t > 0; t--) {
1123  if (stack[t].type == TABLE)
1124  break;
1125  }
1126  }
1127 
1128  if (is_formatting_element(stack[slot].type) ||
1129  (is_scoping_element(stack[slot].type) &&
1130  stack[slot].type != HTML &&
1131  stack[slot].type != TABLE)) {
1132  /* Find occurrences of the node we're about to pop in the list
1133  * of active formatting elements. We need to invalidate their
1134  * stack index information. */
1135  for (entry = treebuilder->context.formatting_list_end;
1136  entry != NULL; entry = entry->prev) {
1139  if (entry->stack_index == slot)
1140  entry->stack_index = 0;
1141  }
1142  }
1143 
1144  *ns = stack[slot].ns;
1145  *type = stack[slot].type;
1146  *node = stack[slot].node;
1147 
1150  treebuilder->context.current_node = slot - 1;
1151  assert((signed) treebuilder->context.current_node >= 0);
1152 
1153  return HUBBUB_OK;
1154 }
1155 
1163 {
1164  element_type otype = UNKNOWN;
1165  void *node;
1166  hubbub_ns ns;
1167 
1168  while (otype != type) {
1169  element_stack_pop(treebuilder, &ns, &otype, &node);
1170 
1171  treebuilder->tree_handler->unref_node(
1172  treebuilder->tree_handler->ctx, node);
1173 
1174  assert((signed) treebuilder->context.current_node >= 0);
1175  }
1176 
1177  return HUBBUB_OK;
1178 }
1179 
1191  uint32_t index, hubbub_ns *ns, element_type *type,
1192  void **removed)
1193 {
1194  element_context *stack = treebuilder->context.element_stack;
1195  uint32_t n;
1196 
1197  assert(index <= treebuilder->context.current_node);
1198 
1199  /* Scan over subsequent entries in the stack,
1200  * searching for them in the list of active formatting
1201  * entries. If found, update the corresponding
1202  * formatting list entry's stack index to match the
1203  * new stack location */
1204  for (n = index + 1; n <= treebuilder->context.current_node; n++) {
1205  if (is_formatting_element(stack[n].type) ||
1206  (is_scoping_element(stack[n].type) &&
1207  stack[n].type != HTML &&
1208  stack[n].type != TABLE)) {
1210 
1211  for (e = treebuilder->context.formatting_list_end;
1212  e != NULL; e = e->prev) {
1213  if (e->stack_index == n)
1214  e->stack_index--;
1215  }
1216  }
1217  }
1218 
1219  *ns = stack[index].ns;
1220  *type = stack[index].type;
1221  *removed = stack[index].node;
1222 
1223  /* Now, shuffle the stack up one, removing node in the process */
1224  if (index < treebuilder->context.current_node) {
1225  memmove(&stack[index], &stack[index + 1],
1226  (treebuilder->context.current_node - index) *
1227  sizeof(element_context));
1228  }
1229 
1230  treebuilder->context.current_node--;
1231 
1232  return HUBBUB_OK;
1233 }
1234 
1238 uint32_t current_table(hubbub_treebuilder *treebuilder)
1239 {
1240  element_context *stack = treebuilder->context.element_stack;
1241  size_t t;
1242 
1243  for (t = treebuilder->context.current_node; t != 0; t--) {
1244  if (stack[t].type == TABLE)
1245  return t;
1246  }
1247 
1248  /* fragment case */
1249  return 0;
1250 }
1251 
1259 {
1260  return treebuilder->context.element_stack
1261  [treebuilder->context.current_node].type;
1262 }
1263 
1271 {
1272  if (treebuilder->context.current_node == 0)
1273  return UNKNOWN;
1274 
1275  return treebuilder->context.element_stack
1276  [treebuilder->context.current_node - 1].type;
1277 }
1278 
1279 
1280 
1292  hubbub_ns ns, element_type type, void *node,
1293  uint32_t stack_index)
1294 {
1295  formatting_list_entry *entry;
1296 
1297  entry = malloc(sizeof(formatting_list_entry));
1298  if (entry == NULL)
1299  return HUBBUB_NOMEM;
1300 
1301  entry->details.ns = ns;
1302  entry->details.type = type;
1303  entry->details.node = node;
1304  entry->stack_index = stack_index;
1305 
1306  entry->prev = treebuilder->context.formatting_list_end;
1307  entry->next = NULL;
1308 
1309  if (entry->prev != NULL)
1310  entry->prev->next = entry;
1311  else
1312  treebuilder->context.formatting_list = entry;
1313 
1314  treebuilder->context.formatting_list_end = entry;
1315 
1316  return HUBBUB_OK;
1317 }
1318 
1333  hubbub_ns ns, element_type type, void *node,
1334  uint32_t stack_index)
1335 {
1336  formatting_list_entry *entry;
1337 
1338  if (prev != NULL) {
1339  assert(prev->next == next);
1340  }
1341 
1342  if (next != NULL) {
1343  assert(next->prev == prev);
1344  }
1345 
1346  entry = malloc(sizeof(formatting_list_entry));
1347  if (entry == NULL)
1348  return HUBBUB_NOMEM;
1349 
1350  entry->details.ns = ns;
1351  entry->details.type = type;
1352  entry->details.node = node;
1353  entry->stack_index = stack_index;
1354 
1355  entry->prev = prev;
1356  entry->next = next;
1357 
1358  if (entry->prev != NULL)
1359  entry->prev->next = entry;
1360  else
1361  treebuilder->context.formatting_list = entry;
1362 
1363  if (entry->next != NULL)
1364  entry->next->prev = entry;
1365  else
1366  treebuilder->context.formatting_list_end = entry;
1367 
1368  return HUBBUB_OK;
1369 }
1370 
1371 
1384  formatting_list_entry *entry,
1385  hubbub_ns *ns, element_type *type, void **node,
1386  uint32_t *stack_index)
1387 {
1388  *ns = entry->details.ns;
1389  *type = entry->details.type;
1390  *node = entry->details.node;
1391  *stack_index = entry->stack_index;
1392 
1393  if (entry->prev == NULL)
1394  treebuilder->context.formatting_list = entry->next;
1395  else
1396  entry->prev->next = entry->next;
1397 
1398  if (entry->next == NULL)
1399  treebuilder->context.formatting_list_end = entry->prev;
1400  else
1401  entry->next->prev = entry->prev;
1402 
1403  free(entry);
1404 
1405  return HUBBUB_OK;
1406 }
1407 
1424  formatting_list_entry *entry,
1425  hubbub_ns ns, element_type type, void *node,
1426  uint32_t stack_index,
1427  hubbub_ns *ons, element_type *otype, void **onode,
1428  uint32_t *ostack_index)
1429 {
1430  UNUSED(treebuilder);
1431 
1432  *ons = entry->details.ns;
1433  *otype = entry->details.type;
1434  *onode = entry->details.node;
1435  *ostack_index = entry->stack_index;
1436 
1437  entry->details.ns = ns;
1438  entry->details.type = type;
1439  entry->details.node = node;
1440  entry->stack_index = stack_index;
1441 
1442  return HUBBUB_OK;
1443 }
1444 
1445 
1446 
1447 #ifndef NDEBUG
1448 
1455 void element_stack_dump(hubbub_treebuilder *treebuilder, FILE *fp)
1456 {
1457  element_context *stack = treebuilder->context.element_stack;
1458  uint32_t i;
1459 
1460  for (i = 0; i <= treebuilder->context.current_node; i++) {
1461  fprintf(fp, "%u: %s %p\n",
1462  i,
1463  element_type_to_name(stack[i].type),
1464  stack[i].node);
1465  }
1466 }
1467 
1474 void formatting_list_dump(hubbub_treebuilder *treebuilder, FILE *fp)
1475 {
1476  formatting_list_entry *entry;
1477 
1478  for (entry = treebuilder->context.formatting_list; entry != NULL;
1479  entry = entry->next) {
1480  fprintf(fp, "%s %p %u\n",
1482  entry->details.node, entry->stack_index);
1483  }
1484 }
1485 
1493 {
1494  size_t i;
1495 
1496  for (i = 0;
1497  i < sizeof(name_type_map) / sizeof(name_type_map[0]);
1498  i++) {
1499  if (name_type_map[i].type == type)
1500  return name_type_map[i].name;
1501  }
1502 
1503  return "UNKNOWN";
1504 }
1505 #endif
1506 
struct hubbub_treebuilder_context::@13 collect
Context for character collecting.
bool is_formatting_element(element_type type)
Determine if a node is a formatting element.
Definition: treebuilder.c:1037
hubbub_ns ns
Element namespace.
Definition: internal.h:44
Definition: internal.h:20
hubbub_error handle_after_body(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "after body" insertion mode.
Definition: after_body.c:24
void close_implied_end_tags(hubbub_treebuilder *treebuilder, element_type except)
Close implied end tags.
Definition: treebuilder.c:828
Definition: internal.h:25
hubbub_error complete_script(hubbub_treebuilder *treebuilder)
Script processing and execution.
Definition: treebuilder.c:925
Definition: internal.h:18
hubbub_token_handler handler
Definition: tokeniser.h:38
Definition: internal.h:30
hubbub_error handle_after_after_body(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "after after body" insertion mode.
hubbub_error handle_in_frameset(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "in frameset" insertion mode.
Definition: in_frameset.c:24
Definition: internal.h:32
struct hubbub_tokeniser_optparams::@11 content_model
Current content model.
Definition: internal.h:20
hubbub_error element_stack_push(hubbub_treebuilder *treebuilder, hubbub_ns ns, element_type type, void *node)
Push an element onto the stack of open elements.
Definition: treebuilder.c:1075
Definition: internal.h:20
hubbub_error handle_in_column_group(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in column group" insertion mode.
hubbub_error formatting_list_remove(hubbub_treebuilder *treebuilder, formatting_list_entry *entry, hubbub_ns *ns, element_type *type, void **node, uint32_t *stack_index)
Remove an element from the list of active formatting elements.
Definition: treebuilder.c:1383
void * ctx
Context pointer.
Definition: tree.h:292
hubbub_ns ns
Tag namespace.
Definition: types.h:109
Definition: internal.h:18
hubbub_error 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)
Remove an element from the list of active formatting elements.
Definition: treebuilder.c:1423
hubbub_error handle_after_head(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "after head" insertion mode.
Definition: after_head.c:24
hubbub_error handle_in_select(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "in head" insertion mode.
Definition: in_select.c:24
hubbub_tree_form_associate form_associate
Form associate.
Definition: tree.h:287
Definition: internal.h:27
uint32_t stack_index
Index into element stack.
Definition: internal.h:64
Data for a tag.
Definition: types.h:108
Token data.
Definition: types.h:119
hubbub_content_model model
Definition: tokeniser.h:48
hubbub_error handle_in_table_body(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in table body" insertion mode.
Definition: in_table_body.c:89
hubbub_error handle_generic_rcdata(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "generic rcdata" insertion mode.
hubbub_error hubbub_treebuilder_token_handler(const hubbub_token *token, void *pw)
Handle tokeniser emitting a token.
Definition: treebuilder.c:264
void * document_node
The document node.
Definition: treebuilder.h:44
bool is_phrasing_element(element_type type)
Determine if a node is a phrasing element.
Definition: treebuilder.c:1048
void clear_active_formatting_list_to_marker(hubbub_treebuilder *treebuilder)
Clear the list of active formatting elements up to the last marker.
Definition: treebuilder.c:716
hubbub_string name
Tag name.
Definition: types.h:110
Definition: internal.h:16
Entry in a formatting list.
Definition: internal.h:60
hubbub_tree_handler * tree_handler
Callback table.
Definition: internal.h:122
hubbub_error reconstruct_active_formatting_list(hubbub_treebuilder *treebuilder)
Reconstruct the list of active formatting elements.
Definition: treebuilder.c:542
const uint8_t * ptr
Pointer to data.
Definition: types.h:77
Definition: modes.h:28
Definition: modes.h:27
hubbub_error aa_insert_into_foster_parent(hubbub_treebuilder *treebuilder, void *node, void **inserted)
Adoption agency: locate foster parent and insert node into it.
Definition: in_body.c:2222
Definition: internal.h:21
element_type
Definition: internal.h:13
hubbub_error remove_node_from_dom(hubbub_treebuilder *treebuilder, void *node)
Remove a node from the DOM.
Definition: treebuilder.c:680
Definition: internal.h:30
Tokeniser string type.
Definition: types.h:76
Definition: internal.h:17
struct hubbub_treebuilder_optparams::@15 error_handler
Error handling callback.
hubbub_error element_stack_pop_until(hubbub_treebuilder *treebuilder, element_type type)
Pop elements until an element of type "element" has been popped.
Definition: treebuilder.c:1161
hubbub_error hubbub_tokeniser_setopt(hubbub_tokeniser *tokeniser, hubbub_tokeniser_opttype type, hubbub_tokeniser_optparams *params)
Configure a hubbub tokeniser.
Definition: tokeniser.c:366
hubbub_error formatting_list_append(hubbub_treebuilder *treebuilder, hubbub_ns ns, element_type type, void *node, uint32_t stack_index)
Append an element to the end of the list of active formatting elements.
Definition: treebuilder.c:1291
hubbub_error process_characters_expect_whitespace(hubbub_treebuilder *treebuilder, const hubbub_token *token, bool insert_into_current_node)
Process a character token in cases where we expect only whitespace.
Definition: treebuilder.c:374
Definition: internal.h:27
hubbub_error handle_in_caption(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in caption" insertion mode.
Definition: in_caption.c:24
struct formatting_list_entry * prev
Previous in list.
Definition: internal.h:66
#define mode(x)
Definition: internal.h:18
Definition: internal.h:17
Hubbub treebuilder option parameters.
Definition: treebuilder.h:36
Definition: internal.h:18
Definition: internal.h:25
Definition: internal.h:25
bool strip_leading_lr
Whether to strip a LR from the start of the next character sequence received.
Definition: internal.h:102
#define UNUSED(x)
Definition: utils.h:38
Definition: internal.h:22
bool in_table_foster
Whether nodes that would be inserted into the current node should be foster parented.
Definition: internal.h:106
Definition: internal.h:25
Tokeniser data structure.
Definition: tokeniser.c:165
uint32_t stack_alloc
Number of stack slots allocated.
Definition: internal.h:80
size_t len
Definition: treebuilder.c:24
void reset_insertion_mode(hubbub_treebuilder *treebuilder)
Reset the insertion mode.
Definition: treebuilder.c:862
insertion_mode mode
The current insertion mode.
Definition: internal.h:75
hubbub_error append_text(hubbub_treebuilder *treebuilder, const hubbub_string *string)
Append text to the current node, inserting into the last child of the current node, iff it's a Text node.
Definition: treebuilder.c:943
Definition: internal.h:27
hubbub_error element_stack_pop(hubbub_treebuilder *treebuilder, hubbub_ns *ns, element_type *type, void **node)
Pop an element off the stack of open elements.
Definition: treebuilder.c:1112
void formatting_list_dump(hubbub_treebuilder *treebuilder, FILE *fp)
Dump a formatting list to the given file pointer.
Definition: treebuilder.c:1474
Definition: internal.h:23
const char * element_type_to_name(element_type type)
Convert an element type to a name.
Definition: treebuilder.c:1492
void * form_element
Pointer to most recently opened FORM element.
Definition: internal.h:90
Definition: internal.h:22
Hubbub tokeniser option parameters.
Definition: tokeniser.h:36
Definition: internal.h:19
hubbub_error handle_before_head(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "before head" insertion mode.
Definition: before_head.c:24
struct hubbub_tokeniser_optparams::@9 token_handler
Token handling callback.
size_t len
Byte length of string.
Definition: types.h:78
Definition: internal.h:22
hubbub_tokeniser * tokeniser
Underlying tokeniser.
Definition: internal.h:118
Definition: internal.h:23
hubbub_error handle_in_row(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in row" insertion mode.
Definition: in_row.c:78
Definition: internal.h:22
uint32_t current_table(hubbub_treebuilder *treebuilder)
Find the stack index of the current table.
Definition: treebuilder.c:1238
Definition: internal.h:27
hubbub_error handle_in_select_in_table(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "in select in table" insertion mode.
hubbub_error handle_in_table(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "in table" insertion mode.
Definition: in_table.c:74
hubbub_error handle_in_body(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in body" insertion mode.
Definition: in_body.c:123
Definition: internal.h:19
Definition: internal.h:20
hubbub_error process_comment_append(hubbub_treebuilder *treebuilder, const hubbub_token *token, void *parent)
Process a comment token, appending it to the given parent.
Definition: treebuilder.c:420
hubbub_tree_handler * tree_handler
Tree handling callbacks.
Definition: treebuilder.h:42
hubbub_tree_clone_node clone_node
Clone node.
Definition: tree.h:283
formatting_list_entry * formatting_list_end
End of active formatting list.
Definition: internal.h:85
#define N_ELEMENTS(x)
Definition: utils.h:42
Definition: internal.h:30
bool enable_scripting
Whether scripting is enabled.
Definition: internal.h:95
Definition: modes.h:22
Context for a tree builder.
Definition: internal.h:73
Definition: internal.h:20
Definition: internal.h:19
void * head_element
Pointer to HEAD element.
Definition: internal.h:88
Definition: internal.h:34
hubbub_error handle_initial(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in initial insertion mode.
Definition: initial.c:214
element_type type
Type of node.
Definition: internal.h:99
hubbub_treebuilder_context context
Our context.
Definition: internal.h:120
Definition: internal.h:21
#define ELEMENT_STACK_CHUNK
Definition: internal.h:78
Definition: internal.h:21
Definition: internal.h:19
Definition: internal.h:18
hubbub_error handle_before_html(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "before html" insertion mode.
Definition: before_html.c:24
hubbub_error handle_in_head(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "in head" insertion mode.
Definition: in_head.c:109
hubbub_error insert_element(hubbub_treebuilder *treebuilder, const hubbub_tag *tag, bool push)
Create element and insert it into the DOM, potentially pushing it on the stack.
Definition: treebuilder.c:751
hubbub_tree_create_comment create_comment
Create comment.
Definition: tree.h:274
hubbub_tree_remove_child remove_child
Remove child.
Definition: tree.h:282
hubbub_error handle_after_after_frameset(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "after after frameset" insertion mode.
hubbub_error parse_generic_rcdata(hubbub_treebuilder *treebuilder, const hubbub_token *token, bool rcdata)
Trigger parsing of generic (R)CDATA.
Definition: treebuilder.c:463
struct formatting_list_entry * next
Next in list.
Definition: internal.h:67
formatting_list_entry * formatting_list
List of active formatting elements.
Definition: internal.h:83
hubbub_error
Definition: errors.h:18
hubbub_error_handler error_handler
Error handler.
Definition: internal.h:124
element_type element_type_from_name(hubbub_treebuilder *treebuilder, const hubbub_string *tag_name)
Convert an element name into an element type.
Definition: treebuilder.c:986
hubbub_error handle_in_head_noscript(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in head noscript" insertion mode.
hubbub_string comment
Definition: types.h:127
void * node
Node pointer.
Definition: internal.h:54
Definition: internal.h:19
hubbub_error hubbub_treebuilder_destroy(hubbub_treebuilder *treebuilder)
Destroy a hubbub treebuilder.
Definition: treebuilder.c:154
hubbub_error handle_after_frameset(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle token in "after frameset" insertion mode.
bool enable_scripting
Enable scripting.
Definition: treebuilder.h:46
Item on the element stack.
Definition: internal.h:42
Definition: modes.h:16
element_type current_node(hubbub_treebuilder *treebuilder)
Peek at the top element of the element stack.
Definition: treebuilder.c:1258
hubbub_string character
Definition: types.h:129
hubbub_tree_ref_node ref_node
Reference node.
Definition: tree.h:278
Definition: internal.h:22
Definition: internal.h:32
Definition: modes.h:19
#define S(x)
Definition: treebuilder.c:20
hubbub_tree_get_parent get_parent
Get parent.
Definition: tree.h:285
hubbub_error hubbub_treebuilder_setopt(hubbub_treebuilder *treebuilder, hubbub_treebuilder_opttype type, hubbub_treebuilder_optparams *params)
Configure a hubbub treebuilder.
Definition: treebuilder.c:231
Definition: internal.h:16
hubbub_tree_unref_node unref_node
Unreference node.
Definition: tree.h:279
Definition: internal.h:17
element_type type
Definition: treebuilder.c:25
No error.
Definition: errors.h:19
Definition: internal.h:27
hubbub_error handle_in_cell(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in cell" insertion mode.
Definition: in_cell.c:63
bool frameset_ok
Whether to process a frameset.
Definition: internal.h:110
hubbub_treebuilder_opttype
Hubbub treebuilder option types.
Definition: treebuilder.h:26
hubbub_tag tag
Definition: types.h:125
Definition: internal.h:19
void * document
Pointer to the document node.
Definition: internal.h:93
Definition: internal.h:27
hubbub_tree_create_element create_element
Create element.
Definition: tree.h:276
Definition: internal.h:19
Definition: internal.h:19
hubbub_error_handler handler
Definition: treebuilder.h:38
Definition: internal.h:19
hubbub_ns
Possible namespaces.
Definition: types.h:63
union hubbub_token::@3 data
Type-specific data.
static bool is_form_associated(element_type type)
Determine if a node is form associated.
Definition: treebuilder.c:1059
Definition: internal.h:27
hubbub_tree_create_text create_text
Create text.
Definition: tree.h:277
Definition: internal.h:27
hubbub_tree_complete_script complete_script
Script Complete.
Definition: tree.h:291
Definition: internal.h:32
Definition: modes.h:23
hubbub_error 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)
Insert an element into the list of active formatting elements.
Definition: treebuilder.c:1331
static const struct @14 name_type_map[]
insertion_mode second_mode
The secondary insertion mode.
Definition: internal.h:76
Definition: internal.h:32
bool is_scoping_element(element_type type)
Determine if a node is a scoping element.
Definition: treebuilder.c:1026
Definition: internal.h:18
Definition: internal.h:21
void * error_pw
Error handler data.
Definition: internal.h:125
Definition: internal.h:27
element_type type
Element type.
Definition: internal.h:45
uint32_t element_in_scope(hubbub_treebuilder *treebuilder, element_type type, bool in_table)
Determine if an element is in (table) scope.
Definition: treebuilder.c:499
Definition: internal.h:32
Definition: internal.h:17
Definition: internal.h:30
void element_stack_dump(hubbub_treebuilder *treebuilder, FILE *fp)
Dump an element stack to the given file pointer.
Definition: treebuilder.c:1455
hubbub_error handle_in_foreign_content(hubbub_treebuilder *treebuilder, const hubbub_token *token)
Handle tokens in "in foreign content" insertion mode.
Definition: internal.h:23
element_type prev_node(hubbub_treebuilder *treebuilder)
Peek at the element below the top of the element stack.
Definition: treebuilder.c:1270
element_context details
Entry details.
Definition: internal.h:62
Treebuilder object.
Definition: internal.h:116
hubbub_tree_append_child append_child
Append child.
Definition: tree.h:280
bool is_special_element(element_type type)
Determine if a node is a special element.
Definition: treebuilder.c:1015
Definition: internal.h:35
Definition: internal.h:27
element_context * element_stack
Stack of open elements.
Definition: internal.h:79
Definition: internal.h:18
uint32_t current_node
Index of current node in stack.
Definition: internal.h:81
hubbub_error hubbub_treebuilder_create(hubbub_tokeniser *tokeniser, hubbub_treebuilder **treebuilder)
Create a hubbub treebuilder.
Definition: treebuilder.c:93
hubbub_error element_stack_remove(hubbub_treebuilder *treebuilder, uint32_t index, hubbub_ns *ns, element_type *type, void **removed)
Remove a node from the stack of open elements.
Definition: treebuilder.c:1190
Definition: internal.h:19
const char * name
Definition: treebuilder.c:23
Definition: internal.h:32