///|
fn Parser::has_foreign_table_cell_on_stack(self : Parser) -> Bool {
  for node in self.stack {
    if node.kind == Element &&
      (node.name == "td" || node.name == "th") &&
      !(node.ns is Some("html")) {
      return true
    }
  }
  false
}

///|
fn Parser::has_foreign_table_row_on_stack(self : Parser) -> Bool {
  for node in self.stack {
    if node.kind == Element && node.name == "tr" && !(node.ns is Some("html")) {
      return true
    }
  }
  false
}

///|
fn Parser::clear_open_elements_to_root(self : Parser) -> Unit {
  while self.stack.length() > 1 {
    ignore(self.stack.pop())
  }
}

///|
fn Parser::handle_select_reprocessed_foreign_cell_start_tag(
  self : Parser,
  name : String,
  attrs : Map[String, String?],
  start : Int,
  self_closing : Bool,
) -> Bool {
  if name != "td" && name != "th" {
    return false
  }
  if self.has_open_element("table") || !self.has_foreign_table_cell_on_stack() {
    return false
  }
  self.clear_open_elements_to_root()
  let node = self.node_with_origin(@dom.element(name, attrs~), start)
  self.post_body_html_children.push(node)
  if !self_closing {
    self.stack.push(node)
  }
  true
}

///|
fn Parser::handle_recovered_post_body_cell_start_tag(
  self : Parser,
  name : String,
  attrs : Map[String, String?],
  start : Int,
  self_closing : Bool,
) -> Bool {
  if name != "td" && name != "th" {
    return false
  }
  if self.has_open_element("table") ||
    (!self.has_open_element("td") && !self.has_open_element("th")) {
    return false
  }
  self.close_open_table_cell()
  let node = self.node_with_origin(@dom.element(name, attrs~), start)
  self.post_body_html_children.push(node)
  if !self_closing {
    self.stack.push(node)
  }
  true
}

///|
fn Parser::handle_foreign_table_row_recovery_start_tag(
  self : Parser,
  name : String,
  attrs : Map[String, String?],
  start : Int,
  self_closing : Bool,
) -> Bool {
  if name != "tr" {
    return false
  }
  if self.has_open_element("table") || !self.has_foreign_table_row_on_stack() {
    return false
  }
  self.clear_open_elements_to_root()
  let node = self.node_with_origin(@dom.element("tr", attrs~), start)
  self.post_body_html_children.push(node)
  self.foreign_table_row_recovery_mode = !self_closing
  if !self_closing {
    self.stack.push(node)
  }
  true
}

///|
fn Parser::report_foreign_table_row_recovery_text(
  self : Parser,
  value : StringView,
  start : Int,
) -> Unit {
  if !self.foreign_table_row_recovery_mode ||
    !text_has_non_ascii_whitespace(value) {
    return
  }
  let current = self.current_node()
  if current.kind != Element || current.name != "tr" {
    return
  }
  let whitespace = leading_ascii_whitespace(value)
  self.error_at(
    "unexpected-character-implies-table-voodoo",
    start + whitespace.length(),
  )
}