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