///|
/// One activation of a recursive "walk the children of `node`" routine,
/// modelled on an explicit heap-allocated stack so deeply nested DOM trees do
/// not overflow the native call stack.
///
/// `index` is the loop cursor over `node.children`, `skip` carries the
/// inherited skip state (linkify and whitespace walks), and `entered` records
/// whether the node's template contents have already been scheduled, since the
/// recursive walks visit template contents before the regular children.
/// `pending` holds a child whose subtree walk is in progress and which still
/// needs post-order work once that walk finishes (used by `prune_empty`).
priv struct WalkFrame {
  node : @dom.Node
  skip : Bool
  mut entered : Bool
  mut index : Int32
  mut pending : @dom.Node?
}

///|
fn WalkFrame::new(node : @dom.Node, skip~ : Bool = false) -> WalkFrame {
  { node, skip, entered: false, index: 0, pending: None, }
}

///|
/// On the first visit of `self`, schedule its template contents (inheriting
/// `self.skip`) ahead of its children. Returns `true` when a template frame was
/// pushed, in which case the caller must resume from the top of the stack.
fn WalkFrame::schedule_template_first(
  self : WalkFrame,
  stack : Array[WalkFrame],
) -> Bool {
  if self.entered {
    return false
  }
  self.entered = true
  match self.node.template_contents {
    Some(contents) => {
      stack.push(WalkFrame::new(contents, skip=self.skip))
      true
    }
    None => false
  }
}