///|
/// HTML5 Tree Construction Algorithm
/// Based on WHATWG HTML5 Parsing Specification

///|
/// Insertion mode for tree construction
pub(all) enum InsertionMode {
  Initial
  BeforeHtml
  BeforeHead
  InHead
  InHeadNoscript
  AfterHead
  InBody
  Text
  InTable
  InTableText
  InCaption
  InColumnGroup
  InTableBody
  InRow
  InCell
  InSelect
  InSelectInTable
  InTemplate
  AfterBody
  InFrameset
  AfterFrameset
  AfterAfterBody
  AfterAfterFrameset
}

///|
/// Formatting element marker or element
pub(all) enum FormattingElement {
  Element(Element)
  Marker
}

///|
/// Tree Builder state machine
pub(all) struct TreeBuilder {
  tokenizer : Tokenizer
  mut insertion_mode : InsertionMode
  mut original_insertion_mode : InsertionMode?
  open_elements : Array[Element]
  active_formatting : Array[FormattingElement]
  mut foster_parenting : Bool
  mut frameset_ok : Bool
  mut head_element : Element?
  mut pending_table_chars : Array[Char]
  // Stack of "template insertion modes" parallel to the open elements stack:
  // one entry is pushed per open ` closes its (inert, scope-bounded) template subtree regardless
  // of the insertion mode its content switched into (a nested , 
` inside a template /// builds correctly; `` is handled by the uniform intercept above. fn TreeBuilder::handle_in_template(self : TreeBuilder, token : Token) -> Unit { match token { Token::StartTag(tag, _, _) => { // Table-section start tags switch into the matching table sub-mode so a //
built inside a template is constructed correctly. let sub_mode = match tag { "caption" | "colgroup" | "tbody" | "tfoot" | "thead" => Some(InsertionMode::InTable) "col" => Some(InsertionMode::InColumnGroup) "tr" => Some(InsertionMode::InTableBody) "td" | "th" => Some(InsertionMode::InRow) _ => None } match sub_mode { Some(mode) => { let n = self.template_insertion_modes.length() if n > 0 { self.template_insertion_modes[n - 1] = mode } self.insertion_mode = mode self.reprocess_token(token) } None => self.handle_in_body(token) } } Token::EOF => if self.has_element_in_scope("template") { // Parse error in the spec; close the template and stop. self.end_template() } // Everything else (incl. all end tags except , handled by the // intercept in process_token_single) is processed with the in-body rules. _ => self.handle_in_body(token) } } ///| /// Handle text insertion mode (for script/style content) fn TreeBuilder::handle_text(self : TreeBuilder, token : Token) -> Unit { match token { Token::Character(c) => self.insert_character(c) Token::Characters(s) => self.insert_characters(s) Token::EOF => { // Parse error - flush text and pop self.flush_pending_text() let _ = self.open_elements.pop() match self.original_insertion_mode { Some(mode) => self.insertion_mode = mode None => self.insertion_mode = InsertionMode::InBody } } Token::EndTag(_) => { // Flush pending text before popping self.flush_pending_text() // Pop current element and switch back to original mode let _ = self.open_elements.pop() match self.original_insertion_mode { Some(mode) => self.insertion_mode = mode None => self.insertion_mode = InsertionMode::InBody } } _ => () } }