// Copyright 2014-2017 The html5ever Project Developers. See the // COPYRIGHT file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[macro_use] extern crate html5ever; use std::borrow::Cow; use std::cell::{Cell, Ref, RefCell}; use std::collections::HashMap; use std::io; use html5ever::parse_document; use html5ever::tendril::*; use html5ever::tree_builder::{ AppendNode, AppendText, ElementFlags, NodeOrText, QuirksMode, TreeSink, }; use html5ever::{Attribute, QualName}; struct Sink { next_id: Cell, names: RefCell>, } impl Sink { fn get_id(&self) -> usize { let id = self.next_id.get(); self.next_id.set(id + 2); id } } impl TreeSink for Sink { type Handle = usize; type Output = Self; type ElemName<'a> = Ref<'a, QualName>; fn finish(self) -> Self { self } fn parse_error(&self, msg: Cow<'static, str>) { println!("Parse error: {}", msg); } fn get_document(&self) -> usize { 0 } fn get_template_contents(&self, target: &usize) -> usize { if let Some(expanded_name!(html "template")) = self.names.borrow().get(target).map(|n| n.expanded()) { target + 1 } else { panic!("not a template element") } } fn set_quirks_mode(&self, mode: QuirksMode) { println!("Set quirks mode to {:?}", mode); } fn same_node(&self, x: &usize, y: &usize) -> bool { x == y } fn elem_name(&self, target: &usize) -> Self::ElemName<'_> { Ref::map(self.names.borrow(), |map| { *map.get(target).expect("not an element") }) } fn create_element(&self, name: QualName, _: Vec, _: ElementFlags) -> usize { let id = self.get_id(); println!("Created {:?} as {}", name, id); // N.B. We intentionally leak memory here to minimize the implementation complexity // of this example code. A real implementation would either want to use a real // real DOM tree implentation, or else use an arena as the backing store for // memory used by the parser. self.names .borrow_mut() .insert(id, Box::leak(Box::new(name))); id } fn create_comment(&self, text: StrTendril) -> usize { let id = self.get_id(); println!("Created comment \"{}\" as {}", text.escape_default(), id); id } #[allow(unused_variables)] fn create_pi(&self, target: StrTendril, value: StrTendril) -> usize { unimplemented!() } fn append(&self, parent: &usize, child: NodeOrText) { match child { AppendNode(n) => println!("Append node {} to {}", n, parent), AppendText(t) => println!("Append text to {}: \"{}\"", parent, t.escape_default()), } } fn append_before_sibling(&self, sibling: &usize, new_node: NodeOrText) { match new_node { AppendNode(n) => println!("Append node {} before {}", n, sibling), AppendText(t) => println!("Append text before {}: \"{}\"", sibling, t.escape_default()), } } fn append_based_on_parent_node( &self, element: &Self::Handle, _prev_element: &Self::Handle, child: NodeOrText, ) { self.append_before_sibling(element, child); } fn append_doctype_to_document( &self, name: StrTendril, public_id: StrTendril, system_id: StrTendril, ) { println!("Append doctype: {} {} {}", name, public_id, system_id); } fn add_attrs_if_missing(&self, target: &usize, attrs: Vec) { assert!(self.names.borrow().contains_key(target), "not an element"); println!("Add missing attributes to {}:", target); for attr in attrs.into_iter() { println!(" {:?} = {}", attr.name, attr.value); } } fn associate_with_form( &self, _target: &usize, _form: &usize, _nodes: (&usize, Option<&usize>), ) { // No form owner support. } fn remove_from_parent(&self, target: &usize) { println!("Remove {} from parent", target); } fn reparent_children(&self, node: &usize, new_parent: &usize) { println!("Move children from {} to {}", node, new_parent); } fn mark_script_already_started(&self, node: &usize) { println!("Mark script {} as already started", node); } fn set_current_line(&self, line_number: u64) { println!("Set current line to {}", line_number); } fn pop(&self, elem: &usize) { println!("Popped element {}", elem); } } /// Same example as the "noop-tree-builder", but this time every function implemented in our /// Sink object prints a log, so it's easier to get an understating of when each function is /// called. fn main() { let sink = Sink { next_id: Cell::new(1), names: RefCell::new(HashMap::new()), }; let stdin = io::stdin(); parse_document(sink, Default::default()) .from_utf8() .read_from(&mut stdin.lock()) .unwrap(); }