markup5ever_rcdom-0.1.0/Cargo.toml.orig010064400007650000120000000016151357673347300162720ustar0000000000000000[package] name = "markup5ever_rcdom" version = "0.1.0" authors = [ "The html5ever Project Developers" ] license = "MIT / Apache-2.0" repository = "https://github.com/servo/html5ever" description = "Basic, unsupported DOM structure for use by tests in html5ever/xml5ever" readme = "README.md" documentation = "https://docs.rs/markup5ever_rcdom" categories = [ "parser-implementations", "web-programming" ] edition = "2018" [lib] path = "lib.rs" [dependencies] tendril = "0.4" html5ever = { version = "0.25", path = "../html5ever" } markup5ever = { version = "0.10", path = "../markup5ever" } xml5ever = { version = "0.16", path = "../xml5ever" } [dev-dependencies] serde_json = "1.0" rustc-test = "0.3" [[test]] name = "html-tokenizer" harness = false [[test]] name = "html-tree-builder" harness = false [[test]] name = "xml-tree-builder" harness = false [[test]] name = "xml-tokenizer" harness = false markup5ever_rcdom-0.1.0/Cargo.toml0000644000000026301357673350500125770ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "markup5ever_rcdom" version = "0.1.0" authors = ["The html5ever Project Developers"] description = "Basic, unsupported DOM structure for use by tests in html5ever/xml5ever" documentation = "https://docs.rs/markup5ever_rcdom" readme = "README.md" categories = ["parser-implementations", "web-programming"] license = "MIT / Apache-2.0" repository = "https://github.com/servo/html5ever" [lib] path = "lib.rs" [[test]] name = "html-tokenizer" harness = false [[test]] name = "html-tree-builder" harness = false [[test]] name = "xml-tree-builder" harness = false [[test]] name = "xml-tokenizer" harness = false [dependencies.html5ever] version = "0.25" [dependencies.markup5ever] version = "0.10" [dependencies.tendril] version = "0.4" [dependencies.xml5ever] version = "0.16" [dev-dependencies.rustc-test] version = "0.3" [dev-dependencies.serde_json] version = "1.0" markup5ever_rcdom-0.1.0/data/test/ignore010064400007650000120000000000011357673342700164640ustar0000000000000000 markup5ever_rcdom-0.1.0/examples/hello_xml.rs010064400007650000120000000024141357673342700175470ustar0000000000000000#!/usr/bin/env run-cargo-script //! This is a regular crate doc comment, but it also contains a partial //! Cargo manifest. Note the use of a *fenced* code block, and the //! `cargo` "language". //! //! ```cargo //! [dependencies] //! xml5ever = "0.2.0" //! tendril = "0.1.3" //! ``` extern crate markup5ever_rcdom as rcdom; extern crate xml5ever; use std::default::Default; use rcdom::{NodeData, RcDom}; use xml5ever::driver::parse_document; use xml5ever::tendril::TendrilSink; use xml5ever::tree_builder::TreeSink; fn main() { // To parse a string into a tree of nodes, we need to invoke // `parse_document` and supply it with a TreeSink implementation (RcDom). let dom: RcDom = parse_document(RcDom::default(), Default::default()).one("XML"); // Do some processing let doc = &dom.document; let hello_node = &doc.children.borrow()[0]; let hello_tag = &*dom.elem_name(hello_node).local; let text_node = &hello_node.children.borrow()[0]; let xml = { let mut xml = String::new(); match &text_node.data { &NodeData::Text { ref contents } => { xml.push_str(&contents.borrow()); }, _ => {}, }; xml }; println!("{:?} {:?}!", hello_tag, xml); } markup5ever_rcdom-0.1.0/examples/html2html.rs010064400007650000120000000033721357673342700175030ustar0000000000000000// 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. //! Parse and re-serialize a HTML5 document. //! //! This is meant to produce the exact same output (ignoring stderr) as //! //! java -classpath htmlparser-1.4.jar nu.validator.htmlparser.tools.HTML2HTML //! //! where htmlparser-1.4.jar comes from http://about.validator.nu/htmlparser/ extern crate html5ever; extern crate markup5ever_rcdom as rcdom; use std::default::Default; use std::io::{self, Write}; use html5ever::driver::ParseOpts; use html5ever::tendril::TendrilSink; use html5ever::tree_builder::TreeBuilderOpts; use html5ever::{parse_document, serialize}; use rcdom::{RcDom, SerializableHandle}; fn main() { let opts = ParseOpts { tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() }, ..Default::default() }; let stdin = io::stdin(); let dom = parse_document(RcDom::default(), opts) .from_utf8() .read_from(&mut stdin.lock()) .unwrap(); // The validator.nu HTML2HTML always prints a doctype at the very beginning. io::stdout() .write_all(b"\n") .ok() .expect("writing DOCTYPE failed"); let document: SerializableHandle = dom.document.clone().into(); serialize(&mut io::stdout(), &document, Default::default()) .ok() .expect("serialization failed"); } markup5ever_rcdom-0.1.0/examples/print-rcdom.rs010064400007650000120000000047451357673342700200330ustar0000000000000000// 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; extern crate markup5ever_rcdom as rcdom; use std::default::Default; use std::io; use std::iter::repeat; use std::string::String; use html5ever::parse_document; use html5ever::tendril::TendrilSink; use rcdom::{Handle, NodeData, RcDom}; // This is not proper HTML serialization, of course. fn walk(indent: usize, handle: &Handle) { let node = handle; // FIXME: don't allocate print!("{}", repeat(" ").take(indent).collect::()); match node.data { NodeData::Document => println!("#Document"), NodeData::Doctype { ref name, ref public_id, ref system_id, } => println!("", name, public_id, system_id), NodeData::Text { ref contents } => { println!("#text: {}", escape_default(&contents.borrow())) }, NodeData::Comment { ref contents } => println!("", escape_default(contents)), NodeData::Element { ref name, ref attrs, .. } => { assert!(name.ns == ns!(html)); print!("<{}", name.local); for attr in attrs.borrow().iter() { assert!(attr.name.ns == ns!()); print!(" {}=\"{}\"", attr.name.local, attr.value); } println!(">"); }, NodeData::ProcessingInstruction { .. } => unreachable!(), } for child in node.children.borrow().iter() { walk(indent + 4, child); } } // FIXME: Copy of str::escape_default from std, which is currently unstable pub fn escape_default(s: &str) -> String { s.chars().flat_map(|c| c.escape_default()).collect() } fn main() { let stdin = io::stdin(); let dom = parse_document(RcDom::default(), Default::default()) .from_utf8() .read_from(&mut stdin.lock()) .unwrap(); walk(0, &dom.document); if !dom.errors.is_empty() { println!("\nParse errors:"); for err in dom.errors.iter() { println!(" {}", err); } } } markup5ever_rcdom-0.1.0/examples/xml_tree_printer.rs010064400007650000120000000034541357673342700211530ustar0000000000000000#!/usr/bin/env run-cargo-script //! This is a regular crate doc comment, but it also contains a partial //! Cargo manifest. Note the use of a *fenced* code block, and the //! `cargo` "language". //! //! ```cargo //! [dependencies] //! xml5ever = "0.2.0" //! tendril = "0.1.3" //! ``` extern crate markup5ever_rcdom as rcdom; extern crate xml5ever; use std::default::Default; use std::io; use std::string::String; use rcdom::{Handle, NodeData, RcDom}; use xml5ever::driver::parse_document; use xml5ever::tendril::TendrilSink; fn walk(prefix: &str, handle: &Handle) { let node = handle; print!("{}", prefix); match node.data { NodeData::Document => println!("#document"), NodeData::Text { ref contents } => println!("#text {}", escape_default(&contents.borrow())), NodeData::Element { ref name, .. } => { println!("{}", name.local); }, _ => {}, } let new_indent = { let mut temp = String::new(); temp.push_str(prefix); temp.push_str(" "); temp }; for child in node .children .borrow() .iter() .filter(|child| match child.data { NodeData::Text { .. } | NodeData::Element { .. } => true, _ => false, }) { walk(&new_indent, child); } } pub fn escape_default(s: &str) -> String { s.chars().flat_map(|c| c.escape_default()).collect() } fn main() { let stdin = io::stdin(); // To parse XML into a tree form, we need a TreeSink // luckily xml5ever comes with a static RC backed tree represetation. let dom: RcDom = parse_document(RcDom::default(), Default::default()) .from_utf8() .read_from(&mut stdin.lock()) .unwrap(); // Execute our visualizer on RcDom walk("", &dom.document); } markup5ever_rcdom-0.1.0/lib.rs010064400007650000120000000355171357673342700145260ustar0000000000000000// 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. //! A simple reference-counted DOM. //! //! This is sufficient as a static parse tree, but don't build a //! web browser using it. :) //! //! A DOM is a [tree structure] with ordered children that can be represented in an XML-like //! format. For example, the following graph //! //! ```text //! div //! +- "text node" //! +- span //! ``` //! in HTML would be serialized as //! //! ```html //!
text node
//! ``` //! //! See the [document object model article on wikipedia][dom wiki] for more information. //! //! This implementation stores the information associated with each node once, and then hands out //! refs to children. The nodes themselves are reference-counted to avoid copying - you can create //! a new ref and then a node will outlive the document. Nodes own their children, but only have //! weak references to their parents. //! //! [tree structure]: https://en.wikipedia.org/wiki/Tree_(data_structure) //! [dom wiki]: https://en.wikipedia.org/wiki/Document_Object_Model extern crate markup5ever; extern crate tendril; use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::HashSet; use std::default::Default; use std::fmt; use std::io; use std::mem; use std::rc::{Rc, Weak}; use tendril::StrTendril; use markup5ever::interface::tree_builder; use markup5ever::interface::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeSink}; use markup5ever::serialize::TraversalScope; use markup5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use markup5ever::serialize::{Serialize, Serializer}; use markup5ever::Attribute; use markup5ever::ExpandedName; use markup5ever::QualName; /// The different kinds of nodes in the DOM. #[derive(Debug)] pub enum NodeData { /// The `Document` itself - the root node of a HTML document. Document, /// A `DOCTYPE` with name, public id, and system id. See /// [document type declaration on wikipedia][dtd wiki]. /// /// [dtd wiki]: https://en.wikipedia.org/wiki/Document_type_declaration Doctype { name: StrTendril, public_id: StrTendril, system_id: StrTendril, }, /// A text node. Text { contents: RefCell }, /// A comment. Comment { contents: StrTendril }, /// An element with attributes. Element { name: QualName, attrs: RefCell>, /// For HTML \ elements, the [template contents]. /// /// [template contents]: https://html.spec.whatwg.org/multipage/#template-contents template_contents: Option, /// Whether the node is a [HTML integration point]. /// /// [HTML integration point]: https://html.spec.whatwg.org/multipage/#html-integration-point mathml_annotation_xml_integration_point: bool, }, /// A Processing instruction. ProcessingInstruction { target: StrTendril, contents: StrTendril, }, } /// A DOM node. pub struct Node { /// Parent node. pub parent: Cell>, /// Child nodes of this node. pub children: RefCell>, /// Represents this node's data. pub data: NodeData, } impl Node { /// Create a new node from its contents pub fn new(data: NodeData) -> Rc { Rc::new(Node { data: data, parent: Cell::new(None), children: RefCell::new(Vec::new()), }) } } impl Drop for Node { fn drop(&mut self) { let mut nodes = mem::replace(&mut *self.children.borrow_mut(), vec![]); while let Some(node) = nodes.pop() { let children = mem::replace(&mut *node.children.borrow_mut(), vec![]); nodes.extend(children.into_iter()); } } } impl fmt::Debug for Node { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("Node") .field("data", &self.data) .field("children", &self.children) .finish() } } /// Reference to a DOM node. pub type Handle = Rc; /// Weak reference to a DOM node, used for parent pointers. pub type WeakHandle = Weak; /// Append a parentless node to another nodes' children fn append(new_parent: &Handle, child: Handle) { let previous_parent = child.parent.replace(Some(Rc::downgrade(new_parent))); // Invariant: child cannot have existing parent assert!(previous_parent.is_none()); new_parent.children.borrow_mut().push(child); } /// If the node has a parent, get it and this node's position in its children fn get_parent_and_index(target: &Handle) -> Option<(Handle, usize)> { if let Some(weak) = target.parent.take() { let parent = weak.upgrade().expect("dangling weak pointer"); target.parent.set(Some(weak)); let i = match parent .children .borrow() .iter() .enumerate() .find(|&(_, child)| Rc::ptr_eq(&child, &target)) { Some((i, _)) => i, None => panic!("have parent but couldn't find in parent's children!"), }; Some((parent, i)) } else { None } } fn append_to_existing_text(prev: &Handle, text: &str) -> bool { match prev.data { NodeData::Text { ref contents } => { contents.borrow_mut().push_slice(text); true }, _ => false, } } fn remove_from_parent(target: &Handle) { if let Some((parent, i)) = get_parent_and_index(target) { parent.children.borrow_mut().remove(i); target.parent.set(None); } } /// The DOM itself; the result of parsing. pub struct RcDom { /// The `Document` itself. pub document: Handle, /// Errors that occurred during parsing. pub errors: Vec>, /// The document's quirks mode. pub quirks_mode: QuirksMode, } impl TreeSink for RcDom { type Output = Self; fn finish(self) -> Self { self } type Handle = Handle; fn parse_error(&mut self, msg: Cow<'static, str>) { self.errors.push(msg); } fn get_document(&mut self) -> Handle { self.document.clone() } fn get_template_contents(&mut self, target: &Handle) -> Handle { if let NodeData::Element { template_contents: Some(ref contents), .. } = target.data { contents.clone() } else { panic!("not a template element!") } } fn set_quirks_mode(&mut self, mode: QuirksMode) { self.quirks_mode = mode; } fn same_node(&self, x: &Handle, y: &Handle) -> bool { Rc::ptr_eq(x, y) } fn elem_name<'a>(&self, target: &'a Handle) -> ExpandedName<'a> { return match target.data { NodeData::Element { ref name, .. } => name.expanded(), _ => panic!("not an element!"), }; } fn create_element( &mut self, name: QualName, attrs: Vec, flags: ElementFlags, ) -> Handle { Node::new(NodeData::Element { name: name, attrs: RefCell::new(attrs), template_contents: if flags.template { Some(Node::new(NodeData::Document)) } else { None }, mathml_annotation_xml_integration_point: flags.mathml_annotation_xml_integration_point, }) } fn create_comment(&mut self, text: StrTendril) -> Handle { Node::new(NodeData::Comment { contents: text }) } fn create_pi(&mut self, target: StrTendril, data: StrTendril) -> Handle { Node::new(NodeData::ProcessingInstruction { target: target, contents: data, }) } fn append(&mut self, parent: &Handle, child: NodeOrText) { // Append to an existing Text node if we have one. match child { NodeOrText::AppendText(ref text) => match parent.children.borrow().last() { Some(h) => { if append_to_existing_text(h, &text) { return; } }, _ => (), }, _ => (), } append( &parent, match child { NodeOrText::AppendText(text) => Node::new(NodeData::Text { contents: RefCell::new(text), }), NodeOrText::AppendNode(node) => node, }, ); } fn append_before_sibling(&mut self, sibling: &Handle, child: NodeOrText) { let (parent, i) = get_parent_and_index(&sibling) .expect("append_before_sibling called on node without parent"); let child = match (child, i) { // No previous node. (NodeOrText::AppendText(text), 0) => Node::new(NodeData::Text { contents: RefCell::new(text), }), // Look for a text node before the insertion point. (NodeOrText::AppendText(text), i) => { let children = parent.children.borrow(); let prev = &children[i - 1]; if append_to_existing_text(prev, &text) { return; } Node::new(NodeData::Text { contents: RefCell::new(text), }) }, // The tree builder promises we won't have a text node after // the insertion point. // Any other kind of node. (NodeOrText::AppendNode(node), _) => node, }; remove_from_parent(&child); child.parent.set(Some(Rc::downgrade(&parent))); parent.children.borrow_mut().insert(i, child); } fn append_based_on_parent_node( &mut self, element: &Self::Handle, prev_element: &Self::Handle, child: NodeOrText, ) { let parent = element.parent.take(); let has_parent = parent.is_some(); element.parent.set(parent); if has_parent { self.append_before_sibling(element, child); } else { self.append(prev_element, child); } } fn append_doctype_to_document( &mut self, name: StrTendril, public_id: StrTendril, system_id: StrTendril, ) { append( &self.document, Node::new(NodeData::Doctype { name: name, public_id: public_id, system_id: system_id, }), ); } fn add_attrs_if_missing(&mut self, target: &Handle, attrs: Vec) { let mut existing = if let NodeData::Element { ref attrs, .. } = target.data { attrs.borrow_mut() } else { panic!("not an element") }; let existing_names = existing .iter() .map(|e| e.name.clone()) .collect::>(); existing.extend( attrs .into_iter() .filter(|attr| !existing_names.contains(&attr.name)), ); } fn remove_from_parent(&mut self, target: &Handle) { remove_from_parent(&target); } fn reparent_children(&mut self, node: &Handle, new_parent: &Handle) { let mut children = node.children.borrow_mut(); let mut new_children = new_parent.children.borrow_mut(); for child in children.iter() { let previous_parent = child.parent.replace(Some(Rc::downgrade(&new_parent))); assert!(Rc::ptr_eq( &node, &previous_parent.unwrap().upgrade().expect("dangling weak") )) } new_children.extend(mem::replace(&mut *children, Vec::new())); } fn is_mathml_annotation_xml_integration_point(&self, target: &Handle) -> bool { if let NodeData::Element { mathml_annotation_xml_integration_point, .. } = target.data { mathml_annotation_xml_integration_point } else { panic!("not an element!") } } } impl Default for RcDom { fn default() -> RcDom { RcDom { document: Node::new(NodeData::Document), errors: vec![], quirks_mode: tree_builder::NoQuirks, } } } enum SerializeOp { Open(Handle), Close(QualName), } pub struct SerializableHandle(Handle); impl From for SerializableHandle { fn from(h: Handle) -> SerializableHandle { SerializableHandle(h) } } impl Serialize for SerializableHandle { fn serialize(&self, serializer: &mut S, traversal_scope: TraversalScope) -> io::Result<()> where S: Serializer, { let mut ops = match traversal_scope { IncludeNode => vec![SerializeOp::Open(self.0.clone())], ChildrenOnly(_) => self .0 .children .borrow() .iter() .map(|h| SerializeOp::Open(h.clone())) .collect(), }; while !ops.is_empty() { match ops.remove(0) { SerializeOp::Open(handle) => match &handle.data { &NodeData::Element { ref name, ref attrs, .. } => { serializer.start_elem( name.clone(), attrs.borrow().iter().map(|at| (&at.name, &at.value[..])), )?; ops.insert(0, SerializeOp::Close(name.clone())); for child in handle.children.borrow().iter().rev() { ops.insert(0, SerializeOp::Open(child.clone())); } }, &NodeData::Doctype { ref name, .. } => serializer.write_doctype(&name)?, &NodeData::Text { ref contents } => { serializer.write_text(&contents.borrow())? }, &NodeData::Comment { ref contents } => serializer.write_comment(&contents)?, &NodeData::ProcessingInstruction { ref target, ref contents, } => serializer.write_processing_instruction(target, contents)?, &NodeData::Document => panic!("Can't serialize Document node itself"), }, SerializeOp::Close(name) => { serializer.end_elem(name)?; }, } } Ok(()) } } markup5ever_rcdom-0.1.0/README.md010064400007650000120000000007031357673342700146560ustar0000000000000000# markup5ever_rcdom This crate is built for the express purpose of writing automated tests for the `html5ever` and `xml5ever` crates. It is not intended to be a production-quality DOM implementation, and has not been fuzzed or tested against arbitrary, malicious, or nontrivial inputs. No maintenance or support for any such issues will be provided. If you use this DOM implementation in a production, user-facing system, you do so at your own risk. markup5ever_rcdom-0.1.0/tests/foreach_html5lib_test/mod.rs010064400007650000120000000025551357673342700221430ustar0000000000000000// 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. use std::ffi::OsStr; use std::fs; use std::ops::FnMut; use std::path::Path; pub fn foreach_html5lib_test( src_dir: &Path, subdir: &'static str, ext: &'static OsStr, mut mk: Mk, ) where Mk: FnMut(&Path, fs::File), { let mut test_dir_path = src_dir.to_path_buf(); test_dir_path.push("html5lib-tests"); test_dir_path.push(subdir); let maybe_test_files = fs::read_dir(&test_dir_path); match maybe_test_files { Ok(test_files) => { for entry in test_files { let path = entry.unwrap().path(); if path.extension() == Some(ext) { let file = fs::File::open(&path).unwrap(); mk(&path, file); } } }, Err(_) => { panic!("Before launching the tests, please run this command:\n\n\tgit submodule update --init\n\nto retrieve an html5lib-tests snapshot."); }, } } markup5ever_rcdom-0.1.0/tests/html-driver.rs010064400007650000120000000012071357673342700173440ustar0000000000000000use html5ever::driver; use html5ever::serialize; use html5ever::tendril::TendrilSink; use markup5ever_rcdom::{RcDom, SerializableHandle}; #[test] fn from_utf8() { let dom = driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("Test".as_bytes()); let mut serialized = Vec::new(); let document: SerializableHandle = dom.document.clone().into(); serialize::serialize(&mut serialized, &document, Default::default()).unwrap(); assert_eq!( String::from_utf8(serialized).unwrap().replace(" ", ""), "<html><head><title>Test" ); } markup5ever_rcdom-0.1.0/tests/html-serializer.rs010064400007650000120000000175721357673342700202360ustar0000000000000000// 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. use html5ever::driver::ParseOpts; use html5ever::serialize::{Serialize, SerializeOpts, Serializer, TraversalScope}; use html5ever::tendril::{SliceExt, StrTendril, TendrilSink}; use html5ever::tokenizer::{TagKind, Token, TokenSink, TokenSinkResult, Tokenizer}; use html5ever::{parse_document, parse_fragment, serialize, QualName}; use markup5ever::{local_name, namespace_url, ns}; use markup5ever_rcdom::{RcDom, SerializableHandle}; use std::io; struct Tokens(Vec); impl TokenSink for Tokens { type Handle = (); fn process_token(&mut self, token: Token, _: u64) -> TokenSinkResult<()> { self.0.push(token); TokenSinkResult::Continue } } impl Serialize for Tokens { fn serialize(&self, serializer: &mut S, _: TraversalScope) -> io::Result<()> where S: Serializer, { for t in self.0.iter() { match t { // TODO: check whether this is an IE conditional comment or a spec comment &Token::TagToken(ref tag) => { let name = QualName::new( None, "http://www.w3.org/1999/xhtml".into(), tag.name.as_ref().into(), ); match tag.kind { TagKind::StartTag => serializer.start_elem( name, tag.attrs.iter().map(|at| (&at.name, &at.value[..])), )?, TagKind::EndTag => serializer.end_elem(name)?, } }, &Token::DoctypeToken(ref dt) => match dt.name { Some(ref name) => serializer.write_doctype(&name)?, None => {}, }, &Token::CommentToken(ref chars) => serializer.write_comment(&chars)?, &Token::CharacterTokens(ref chars) => serializer.write_text(&chars)?, &Token::NullCharacterToken | &Token::EOFToken => {}, &Token::ParseError(ref e) => println!("parse error: {:#?}", e), } } Ok(()) } } fn tokenize_and_serialize(input: StrTendril) -> StrTendril { let mut input = { let mut q = ::html5ever::tokenizer::BufferQueue::new(); q.push_front(input.into()); q }; let mut tokenizer = Tokenizer::new(Tokens(vec![]), Default::default()); let _ = tokenizer.feed(&mut input); tokenizer.end(); let mut output = ::std::io::Cursor::new(vec![]); serialize( &mut output, &tokenizer.sink, SerializeOpts { create_missing_parent: true, ..Default::default() }, ) .unwrap(); StrTendril::try_from_byte_slice(&output.into_inner()).unwrap() } fn parse_and_serialize(input: StrTendril) -> StrTendril { let dom = parse_fragment( RcDom::default(), ParseOpts::default(), QualName::new(None, ns!(html), local_name!("body")), vec![], ) .one(input); let inner: SerializableHandle = dom.document.children.borrow()[0].clone().into(); let mut result = vec![]; serialize(&mut result, &inner, Default::default()).unwrap(); StrTendril::try_from_byte_slice(&result).unwrap() } macro_rules! test_fn { ($f:ident, $name:ident, $input:expr, $output:expr) => { #[test] fn $name() { assert_eq!($output, &*$f($input.to_tendril())); } }; // Shorthand for $output = $input ($f:ident, $name:ident, $input:expr) => { test_fn!($f, $name, $input, $input); }; } macro_rules! test { ($($t:tt)*) => { test_fn!(parse_and_serialize, $($t)*); }; } macro_rules! test_no_parse { ($($t:tt)*) => { test_fn!(tokenize_and_serialize, $($t)*); }; } test!(empty, r#""#); test!(fuzz, "Hello, World!

"#); test!( misnest, r#"

Hello!

, World!"#, r#"

Hello!

, World!"# ); test!(attr_literal, r#""#); test!(attr_escape_amp, r#""#); test!( attr_escape_amp_2, r#""#, r#""# ); test!( attr_escape_nbsp, "", r#""# ); test!( attr_escape_quot, r#""#, r#""# ); test!( attr_escape_several, r#""#, r#""# ); test!(text_literal, r#"

"'"

"#); test!(text_escape_amp, r#"

&

"#); test!(text_escape_amp_2, r#"

&

"#, r#"

&

"#); test!(text_escape_nbsp, "

x\u{a0}y

", r#"

x y

"#); test!(text_escape_lt, r#"

<

"#); test!(text_escape_gt, r#"

>

"#); test!(text_escape_gt2, r#"

>

"#, r#"

>

"#); test!( script_literal, r#""# ); test!( style_literal, r#""# ); test!(xmp_literal, r#"(x & 1) < 2; y > "foo" + 'bar'"#); test!( iframe_literal, r#""# ); test!( noembed_literal, r#"(x & 1) < 2; y > "foo" + 'bar'"# ); test!( noframes_literal, r#"(x & 1) < 2; y > "foo" + 'bar'"# ); test!(pre_lf_0, "
foo bar
"); test!(pre_lf_1, "
\nfoo bar
", "
foo bar
"); test!(pre_lf_2, "
\n\nfoo bar
", "
\nfoo bar
"); test!(textarea_lf_0, ""); test!( textarea_lf_1, "", "" ); test!( textarea_lf_2, "", "" ); test!(listing_lf_0, "foo bar"); test!( listing_lf_1, "\nfoo bar", "foo bar" ); test!( listing_lf_2, "\n\nfoo bar", "\nfoo bar" ); test!(comment_1, r#"

hi

"#); test!(comment_2, r#"

hi

"#); test!(comment_3, r#"

hi

"#); test!(comment_4, r#"

hi

"#); // FIXME: test serialization of qualified tag/attribute names that can't be // parsed from HTML test!(attr_ns_1, r#""#); test!(attr_ns_2, r#""#); test!(attr_ns_3, r#""#); test!(attr_ns_4, r#""#); test_no_parse!(malformed_tokens, r#"foo
"#); #[test] fn doctype() { let dom = parse_document(RcDom::default(), ParseOpts::default()).one(""); dom.document.children.borrow_mut().truncate(1); // Remove let mut result = vec![]; let document: SerializableHandle = dom.document.clone().into(); serialize(&mut result, &document, Default::default()).unwrap(); assert_eq!(String::from_utf8(result).unwrap(), ""); } #[test] fn deep_tree() { let parser = parse_fragment( RcDom::default(), ParseOpts::default(), QualName::new(None, ns!(html), local_name!("div")), vec![], ); let src = String::from("".repeat(60_000)); let dom = parser.one(src); let opts = SerializeOpts::default(); let mut ret_val = Vec::new(); let document: SerializableHandle = dom.document.clone().into(); serialize(&mut ret_val, &document, opts) .expect("Writing to a string shouldn't fail (expect on OOM)"); } markup5ever_rcdom-0.1.0/tests/html-tokenizer.rs010064400007650000120000000341501357673342700200660ustar0000000000000000// 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. mod foreach_html5lib_test; use foreach_html5lib_test::foreach_html5lib_test; use html5ever::tendril::*; use html5ever::tokenizer::states::{Plaintext, RawData, Rawtext, Rcdata}; use html5ever::tokenizer::BufferQueue; use html5ever::tokenizer::{CharacterTokens, EOFToken, NullCharacterToken, ParseError}; use html5ever::tokenizer::{CommentToken, DoctypeToken, TagToken, Token}; use html5ever::tokenizer::{Doctype, EndTag, StartTag, Tag}; use html5ever::tokenizer::{TokenSink, TokenSinkResult, Tokenizer, TokenizerOpts}; use html5ever::{namespace_url, ns, Attribute, LocalName, QualName}; use rustc_test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn}; use serde_json::{Map, Value}; use std::borrow::Cow::Borrowed; use std::default::Default; use std::ffi::OsStr; use std::io::Read; use std::mem::replace; use std::path::Path; use std::{char, env}; // Return all ways of splitting the string into at most n // possibly-empty pieces. fn splits(s: &str, n: usize) -> Vec> { if n == 1 { return vec![vec![s.to_tendril()]]; } let mut points: Vec = s.char_indices().map(|(n, _)| n).collect(); points.push(s.len()); // do this with iterators? let mut out = vec![]; for p in points.into_iter() { let y = &s[p..]; for mut x in splits(&s[..p], n - 1).into_iter() { x.push(y.to_tendril()); out.push(x); } } out.extend(splits(s, n - 1).into_iter()); out } struct TokenLogger { tokens: Vec, current_str: StrTendril, exact_errors: bool, } impl TokenLogger { fn new(exact_errors: bool) -> TokenLogger { TokenLogger { tokens: vec![], current_str: StrTendril::new(), exact_errors: exact_errors, } } // Push anything other than character tokens fn push(&mut self, token: Token) { self.finish_str(); self.tokens.push(token); } fn finish_str(&mut self) { if self.current_str.len() > 0 { let s = replace(&mut self.current_str, StrTendril::new()); self.tokens.push(CharacterTokens(s)); } } fn get_tokens(mut self) -> Vec { self.finish_str(); self.tokens } } impl TokenSink for TokenLogger { type Handle = (); fn process_token(&mut self, token: Token, _line_number: u64) -> TokenSinkResult<()> { match token { CharacterTokens(b) => { self.current_str.push_slice(&b); }, NullCharacterToken => { self.current_str.push_char('\0'); }, ParseError(_) => { if self.exact_errors { self.push(ParseError(Borrowed(""))); } }, TagToken(mut t) => { // The spec seems to indicate that one can emit // erroneous end tags with attrs, but the test // cases don't contain them. match t.kind { EndTag => { t.self_closing = false; t.attrs = vec![]; }, _ => t.attrs.sort_by(|a1, a2| a1.name.cmp(&a2.name)), } self.push(TagToken(t)); }, EOFToken => (), _ => self.push(token), } TokenSinkResult::Continue } } fn tokenize(input: Vec, opts: TokenizerOpts) -> Vec { let sink = TokenLogger::new(opts.exact_errors); let mut tok = Tokenizer::new(sink, opts); let mut buffer = BufferQueue::new(); for chunk in input.into_iter() { buffer.push_back(chunk); let _ = tok.feed(&mut buffer); } let _ = tok.feed(&mut buffer); tok.end(); tok.sink.get_tokens() } trait JsonExt: Sized { fn get_str(&self) -> String; fn get_tendril(&self) -> StrTendril; fn get_nullable_tendril(&self) -> Option; fn get_bool(&self) -> bool; fn get_obj<'t>(&'t self) -> &'t Map; fn get_list<'t>(&'t self) -> &'t Vec; fn find<'t>(&'t self, key: &str) -> &'t Self; } impl JsonExt for Value { fn get_str(&self) -> String { match *self { Value::String(ref s) => s.to_string(), _ => panic!("Value::get_str: not a String"), } } fn get_tendril(&self) -> StrTendril { match *self { Value::String(ref s) => s.to_tendril(), _ => panic!("Value::get_tendril: not a String"), } } fn get_nullable_tendril(&self) -> Option { match *self { Value::Null => None, Value::String(ref s) => Some(s.to_tendril()), _ => panic!("Value::get_nullable_tendril: not a String"), } } fn get_bool(&self) -> bool { match *self { Value::Bool(b) => b, _ => panic!("Value::get_bool: not a Bool"), } } fn get_obj<'t>(&'t self) -> &'t Map { match *self { Value::Object(ref m) => &*m, _ => panic!("Value::get_obj: not an Object"), } } fn get_list<'t>(&'t self) -> &'t Vec { match *self { Value::Array(ref m) => m, _ => panic!("Value::get_list: not an Array"), } } fn find<'t>(&'t self, key: &str) -> &'t Value { self.get_obj().get(&key.to_string()).unwrap() } } // Parse a JSON object (other than "ParseError") to a token. fn json_to_token(js: &Value) -> Token { let parts = js.get_list(); // Collect refs here so we don't have to use "ref" in all the patterns below. let args: Vec<&Value> = parts[1..].iter().collect(); match &*parts[0].get_str() { "DOCTYPE" => DoctypeToken(Doctype { name: args[0].get_nullable_tendril(), public_id: args[1].get_nullable_tendril(), system_id: args[2].get_nullable_tendril(), force_quirks: !args[3].get_bool(), }), "StartTag" => TagToken(Tag { kind: StartTag, name: LocalName::from(&*args[0].get_str()), attrs: args[1] .get_obj() .iter() .map(|(k, v)| Attribute { name: QualName::new(None, ns!(), LocalName::from(&**k)), value: v.get_tendril(), }) .collect(), self_closing: match args.get(2) { Some(b) => b.get_bool(), None => false, }, }), "EndTag" => TagToken(Tag { kind: EndTag, name: LocalName::from(&*args[0].get_str()), attrs: vec![], self_closing: false, }), "Comment" => CommentToken(args[0].get_tendril()), "Character" => CharacterTokens(args[0].get_tendril()), // We don't need to produce NullCharacterToken because // the TokenLogger will convert them to CharacterTokens. _ => panic!("don't understand token {:?}", parts), } } // Parse the "output" field of the test case into a vector of tokens. fn json_to_tokens(js: &Value, exact_errors: bool) -> Vec { // Use a TokenLogger so that we combine character tokens separated // by an ignored error. let mut sink = TokenLogger::new(exact_errors); for tok in js.get_list().iter() { assert_eq!( match *tok { Value::String(ref s) if &s[..] == "ParseError" => { sink.process_token(ParseError(Borrowed("")), 0) }, _ => sink.process_token(json_to_token(tok), 0), }, TokenSinkResult::Continue ); } sink.get_tokens() } // Undo the escaping in "doubleEscaped" tests. fn unescape(s: &str) -> Option { let mut out = String::with_capacity(s.len()); let mut it = s.chars().peekable(); loop { match it.next() { None => return Some(out), Some('\\') => { if it.peek() != Some(&'u') { panic!("can't understand escape"); } drop(it.next()); let hex: String = it.by_ref().take(4).collect(); match u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32) { // Some of the tests use lone surrogates, but we have no // way to represent them in the UTF-8 input to our parser. // Since these can only come from script, we will catch // them there. None => return None, Some(c) => out.push(c), } }, Some(c) => out.push(c), } } } fn unescape_json(js: &Value) -> Value { match *js { // unwrap is OK here because the spec'd *output* of the tokenizer never // contains a lone surrogate. Value::String(ref s) => Value::String(unescape(&s).unwrap()), Value::Array(ref xs) => Value::Array(xs.iter().map(unescape_json).collect()), Value::Object(ref obj) => { let mut new_obj = Map::new(); for (k, v) in obj.iter() { new_obj.insert(k.clone(), unescape_json(v)); } Value::Object(new_obj) }, _ => js.clone(), } } fn mk_test(desc: String, input: String, expect: Value, opts: TokenizerOpts) -> TestDescAndFn { TestDescAndFn { desc: TestDesc::new(DynTestName(desc)), testfn: DynTestFn(Box::new(move || { // Split up the input at different points to test incremental tokenization. let insplits = splits(&input, 3); for input in insplits.into_iter() { // Clone 'input' so we have it for the failure message. // Also clone opts. If we don't, we get the wrong // result but the compiler doesn't catch it! // Possibly mozilla/rust#12223. let output = tokenize(input.clone(), opts.clone()); let expect_toks = json_to_tokens(&expect, opts.exact_errors); if output != expect_toks { panic!( "\ninput: {:?}\ngot: {:?}\nexpected: {:?}", input, output, expect ); } } })), } } fn mk_tests(tests: &mut Vec, filename: &str, js: &Value) { let obj = js.get_obj(); let mut input = js.find("input").get_str(); let mut expect = js.find("output").clone(); let desc = format!("tok: {}: {}", filename, js.find("description").get_str()); // "Double-escaped" tests require additional processing of // the input and output. if obj .get(&"doubleEscaped".to_string()) .map_or(false, |j| j.get_bool()) { match unescape(&input) { None => return, Some(i) => input = i, } expect = unescape_json(&expect); } // Some tests have a last start tag name. let start_tag = obj.get(&"lastStartTag".to_string()).map(|s| s.get_str()); // Some tests want to start in a state other than Data. let state_overrides = match obj.get(&"initialStates".to_string()) { Some(&Value::Array(ref xs)) => xs .iter() .map(|s| { Some(match &s.get_str()[..] { "PLAINTEXT state" => Plaintext, "RAWTEXT state" => RawData(Rawtext), "RCDATA state" => RawData(Rcdata), s => panic!("don't know state {}", s), }) }) .collect(), None => vec![None], _ => panic!("don't understand initialStates value"), }; // Build the tests. for state in state_overrides.into_iter() { for &exact_errors in [false, true].iter() { let mut newdesc = desc.clone(); match state { Some(s) => newdesc = format!("{} (in state {:?})", newdesc, s), None => (), }; if exact_errors { newdesc = format!("{} (exact errors)", newdesc); } tests.push(mk_test( newdesc, input.clone(), expect.clone(), TokenizerOpts { exact_errors: exact_errors, initial_state: state, last_start_tag_name: start_tag.clone(), // Not discarding a BOM is what the test suite expects; see // https://github.com/html5lib/html5lib-tests/issues/2 discard_bom: false, ..Default::default() }, )); } } } fn tests(src_dir: &Path) -> Vec { let mut tests = vec![]; foreach_html5lib_test( src_dir, "tokenizer", OsStr::new("test"), |path, mut file| { let mut s = String::new(); file.read_to_string(&mut s) .ok() .expect("file reading error"); let js: Value = serde_json::from_str(&s).ok().expect("json parse error"); match js.get_obj().get(&"tests".to_string()) { Some(&Value::Array(ref lst)) => { for test in lst.iter() { mk_tests( &mut tests, path.file_name().unwrap().to_str().unwrap(), test, ); } }, // xmlViolation.test doesn't follow this format. _ => (), } }, ); tests } fn main() { let args: Vec<_> = env::args().collect(); rustc_test::test_main(&args, tests(Path::new(env!("CARGO_MANIFEST_DIR")))); } markup5ever_rcdom-0.1.0/tests/html-tree-builder.rs010064400007650000120000000224651357673342700204450ustar0000000000000000// 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. extern crate markup5ever_rcdom as rcdom; extern crate rustc_test as test; #[macro_use] extern crate html5ever; mod foreach_html5lib_test; use foreach_html5lib_test::foreach_html5lib_test; use std::collections::{HashMap, HashSet}; use std::default::Default; use std::ffi::OsStr; use std::io::BufRead; use std::iter::repeat; use std::mem::replace; use std::path::Path; use std::{env, fs, io}; use test::{DynTestName, TestDesc, TestDescAndFn, TestFn}; use html5ever::tendril::{StrTendril, TendrilSink}; use html5ever::{parse_document, parse_fragment, ParseOpts}; use html5ever::{LocalName, QualName}; use rcdom::{Handle, NodeData, RcDom}; fn parse_tests>(mut lines: It) -> Vec> { let mut tests = vec![]; let mut test = HashMap::new(); let mut key: Option = None; let mut val = String::new(); macro_rules! finish_val ( () => ( match key.take() { None => (), Some(key) => { assert!(test.insert(key, replace(&mut val, String::new())).is_none()); } } )); macro_rules! finish_test ( () => ( if !test.is_empty() { tests.push(replace(&mut test, HashMap::new())); } )); loop { match lines.next() { None => break, Some(line) => { if line.starts_with("#") { finish_val!(); if line == "#data" { finish_test!(); } key = Some(line[1..].to_string()); } else { val.push_str(&line); val.push('\n'); } }, } } finish_val!(); finish_test!(); tests } fn serialize(buf: &mut String, indent: usize, handle: Handle) { buf.push_str("|"); buf.push_str(&repeat(" ").take(indent).collect::()); let node = handle; match node.data { NodeData::Document => panic!("should not reach Document"), NodeData::Doctype { ref name, ref public_id, ref system_id, } => { buf.push_str("\n"); }, NodeData::Text { ref contents } => { buf.push_str("\""); buf.push_str(&contents.borrow()); buf.push_str("\"\n"); }, NodeData::Comment { ref contents } => { buf.push_str("\n"); }, NodeData::Element { ref name, ref attrs, .. } => { buf.push_str("<"); match name.ns { ns!(svg) => buf.push_str("svg "), ns!(mathml) => buf.push_str("math "), _ => (), } buf.push_str(&*name.local); buf.push_str(">\n"); let mut attrs = attrs.borrow().clone(); attrs.sort_by(|x, y| x.name.local.cmp(&y.name.local)); // FIXME: sort by UTF-16 code unit for attr in attrs.into_iter() { buf.push_str("|"); buf.push_str(&repeat(" ").take(indent + 2).collect::()); match attr.name.ns { ns!(xlink) => buf.push_str("xlink "), ns!(xml) => buf.push_str("xml "), ns!(xmlns) => buf.push_str("xmlns "), _ => (), } buf.push_str(&format!("{}=\"{}\"\n", attr.name.local, attr.value)); } }, NodeData::ProcessingInstruction { .. } => unreachable!(), } for child in node.children.borrow().iter() { serialize(buf, indent + 2, child.clone()); } if let NodeData::Element { template_contents: Some(ref content), .. } = node.data { buf.push_str("|"); buf.push_str(&repeat(" ").take(indent + 2).collect::()); buf.push_str("content\n"); for child in content.children.borrow().iter() { serialize(buf, indent + 4, child.clone()); } } } fn make_test( tests: &mut Vec, ignores: &HashSet, filename: &str, idx: usize, fields: HashMap, ) { let scripting_flags = &[false, true]; let scripting_flags = if fields.contains_key("script-off") { &scripting_flags[0..1] } else if fields.contains_key("script-on") { &scripting_flags[1..2] } else { &scripting_flags[0..2] }; let name = format!("tb: {}-{}", filename, idx); for scripting_enabled in scripting_flags { let test = make_test_desc_with_scripting_flag(ignores, &name, &fields, *scripting_enabled); tests.push(test); } } fn make_test_desc_with_scripting_flag( ignores: &HashSet, name: &str, fields: &HashMap, scripting_enabled: bool, ) -> TestDescAndFn { let get_field = |key| { let field = fields.get(key).expect("missing field"); field.trim_end_matches('\n').to_string() }; let mut data = fields.get("data").expect("missing data").to_string(); data.pop(); let expected = get_field("document"); let context = fields .get("document-fragment") .map(|field| context_name(field.trim_end_matches('\n'))); let ignore = ignores.contains(name); let mut name = name.to_owned(); if scripting_enabled { name.push_str(" (scripting enabled)"); } else { name.push_str(" (scripting disabled)"); }; let mut opts: ParseOpts = Default::default(); opts.tree_builder.scripting_enabled = scripting_enabled; TestDescAndFn { desc: TestDesc { ignore: ignore, ..TestDesc::new(DynTestName(name)) }, testfn: TestFn::dyn_test_fn(move || { // Do this here because Tendril isn't Send. let data = StrTendril::from_slice(&data); let mut result = String::new(); match context { None => { let dom = parse_document(RcDom::default(), opts).one(data.clone()); for child in dom.document.children.borrow().iter() { serialize(&mut result, 1, child.clone()); } }, Some(ref context) => { let dom = parse_fragment(RcDom::default(), opts, context.clone(), vec![]) .one(data.clone()); // fragment case: serialize children of the html element // rather than children of the document let doc = &dom.document; let root = &doc.children.borrow()[0]; for child in root.children.borrow().iter() { serialize(&mut result, 1, child.clone()); } }, }; let len = result.len(); result.truncate(len - 1); // drop the trailing newline if result != expected { panic!( "\ninput: {}\ngot:\n{}\nexpected:\n{}\n", data, result, expected ); } }), } } fn context_name(context: &str) -> QualName { if context.starts_with("svg ") { QualName::new(None, ns!(svg), LocalName::from(&context[4..])) } else if context.starts_with("math ") { QualName::new(None, ns!(mathml), LocalName::from(&context[5..])) } else { QualName::new(None, ns!(html), LocalName::from(context)) } } fn tests(src_dir: &Path, ignores: &HashSet) -> Vec { let mut tests = vec![]; foreach_html5lib_test( src_dir, "tree-construction", OsStr::new("dat"), |path, file| { let buf = io::BufReader::new(file); let lines = buf.lines().map(|res| res.ok().expect("couldn't read")); let data = parse_tests(lines); for (i, test) in data.into_iter().enumerate() { make_test( &mut tests, ignores, path.file_name().unwrap().to_str().unwrap(), i, test, ); } }, ); tests } fn main() { let args: Vec<_> = env::args().collect(); let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let mut ignores = HashSet::new(); { let f = fs::File::open(&src_dir.join("data/test/ignore")).unwrap(); let r = io::BufReader::new(f); for ln in r.lines() { ignores.insert(ln.unwrap().trim_end().to_string()); } } test::test_main(&args, tests(src_dir, &ignores)); } markup5ever_rcdom-0.1.0/tests/html-tree-sink.rs010064400007650000120000000100021357673342700177430ustar0000000000000000use html5ever::driver; use html5ever::tendril::stream::TendrilSink; use html5ever::tendril::StrTendril; use html5ever::ExpandedName; use html5ever::QualName; use markup5ever::interface::{ElementFlags, NodeOrText, QuirksMode, TreeSink}; use markup5ever::{local_name, namespace_url, ns, Attribute}; use markup5ever_rcdom::{Handle, RcDom}; use std::borrow::Cow; pub struct LineCountingDOM { pub line_vec: Vec<(QualName, u64)>, pub current_line: u64, pub rcdom: RcDom, } impl TreeSink for LineCountingDOM { type Output = Self; fn finish(self) -> Self { self } type Handle = Handle; fn parse_error(&mut self, msg: Cow<'static, str>) { self.rcdom.parse_error(msg); } fn get_document(&mut self) -> Handle { self.rcdom.get_document() } fn get_template_contents(&mut self, target: &Handle) -> Handle { self.rcdom.get_template_contents(target) } fn set_quirks_mode(&mut self, mode: QuirksMode) { self.rcdom.set_quirks_mode(mode) } fn same_node(&self, x: &Handle, y: &Handle) -> bool { self.rcdom.same_node(x, y) } fn elem_name<'a>(&'a self, target: &'a Handle) -> ExpandedName<'a> { self.rcdom.elem_name(target) } fn create_element( &mut self, name: QualName, attrs: Vec, flags: ElementFlags, ) -> Handle { self.line_vec.push((name.clone(), self.current_line)); self.rcdom.create_element(name, attrs, flags) } fn create_comment(&mut self, text: StrTendril) -> Handle { self.rcdom.create_comment(text) } fn create_pi(&mut self, target: StrTendril, content: StrTendril) -> Handle { self.rcdom.create_pi(target, content) } fn append(&mut self, parent: &Handle, child: NodeOrText) { self.rcdom.append(parent, child) } fn append_before_sibling(&mut self, sibling: &Handle, child: NodeOrText) { self.rcdom.append_before_sibling(sibling, child) } fn append_based_on_parent_node( &mut self, element: &Handle, prev_element: &Handle, child: NodeOrText, ) { self.rcdom .append_based_on_parent_node(element, prev_element, child) } fn append_doctype_to_document( &mut self, name: StrTendril, public_id: StrTendril, system_id: StrTendril, ) { self.rcdom .append_doctype_to_document(name, public_id, system_id); } fn add_attrs_if_missing(&mut self, target: &Handle, attrs: Vec) { self.rcdom.add_attrs_if_missing(target, attrs); } fn remove_from_parent(&mut self, target: &Handle) { self.rcdom.remove_from_parent(target); } fn reparent_children(&mut self, node: &Handle, new_parent: &Handle) { self.rcdom.reparent_children(node, new_parent); } fn mark_script_already_started(&mut self, target: &Handle) { self.rcdom.mark_script_already_started(target); } fn set_current_line(&mut self, line_number: u64) { self.current_line = line_number; } } #[test] fn check_four_lines() { // Input let sink = LineCountingDOM { line_vec: vec![], current_line: 1, rcdom: RcDom::default(), }; let mut result_tok = driver::parse_document(sink, Default::default()); result_tok.process(StrTendril::from("\n")); result_tok.process(StrTendril::from("\n")); result_tok.process(StrTendril::from("\n")); result_tok.process(StrTendril::from("")); // Actual Output let actual = result_tok.finish(); // Expected Output let expected = vec![ (QualName::new(None, ns!(html), local_name!("html")), 1), (QualName::new(None, ns!(html), local_name!("head")), 1), (QualName::new(None, ns!(html), local_name!("body")), 1), (QualName::new(None, ns!(html), local_name!("a")), 1), (QualName::new(None, ns!(html), local_name!("b")), 3), ]; // Assertion assert_eq!(actual.line_vec, expected); } markup5ever_rcdom-0.1.0/tests/util/find_tests.rs010064400007650000120000000020401357673342700202220ustar0000000000000000// 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. use std::ffi::OsStr; use std::fs; use std::path::Path; pub fn foreach_xml5lib_test( src_dir: &Path, subdir: &'static str, ext: &'static OsStr, mut mk: Mk, ) where Mk: FnMut(&Path, fs::File), { let mut test_dir_path = src_dir.to_path_buf(); test_dir_path.push("xml5lib-tests"); test_dir_path.push(subdir); let test_files = fs::read_dir(&test_dir_path).unwrap(); for entry in test_files { let path = entry.unwrap().path(); if path.extension() == Some(ext) { let file = fs::File::open(&path).unwrap(); mk(&path, file); } } } markup5ever_rcdom-0.1.0/tests/xml-driver.rs010064400007650000120000000065421357673342700172070ustar0000000000000000use markup5ever_rcdom::{RcDom, SerializableHandle}; use xml5ever::driver; use xml5ever::serialize; use xml5ever::tendril::TendrilSink; #[test] fn el_ns_serialize() { assert_eq_serialization( "Test", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("Test".as_bytes()), ); } #[test] fn nested_ns_serialize() { assert_eq_serialization("", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("".as_bytes())); } #[test] fn def_ns_serialize() { assert_eq_serialization( "
", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("
".as_bytes()), ); } #[test] fn undefine_ns_serialize() { assert_eq_serialization( "", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one( "" .as_bytes(), ), ); } #[test] fn redefine_default_ns_serialize() { assert_eq_serialization( "", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("".as_bytes()), ); } #[test] fn attr_serialize() { assert_serialization( "Test", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("Test".as_bytes()), ); } #[test] fn from_utf8() { assert_serialization( "<title>Test", driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one("Test".as_bytes()), ); } fn assert_eq_serialization(text: &'static str, dom: RcDom) { let mut serialized = Vec::new(); let document: SerializableHandle = dom.document.clone().into(); serialize::serialize(&mut serialized, &document, Default::default()).unwrap(); let dom_from_text = driver::parse_document(RcDom::default(), Default::default()) .from_utf8() .one(text.as_bytes()); let mut reserialized = Vec::new(); let document: SerializableHandle = dom_from_text.document.clone().into(); serialize::serialize(&mut reserialized, &document, Default::default()).unwrap(); assert_eq!( String::from_utf8(serialized).unwrap(), String::from_utf8(reserialized).unwrap() ); } fn assert_serialization(text: &'static str, dom: RcDom) { let mut serialized = Vec::new(); let document: SerializableHandle = dom.document.clone().into(); serialize::serialize(&mut serialized, &document, Default::default()).unwrap(); assert_eq!(String::from_utf8(serialized).unwrap(), text); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������markup5ever_rcdom-0.1.0/tests/xml-tokenizer.rs������������������������������������������������������0100644�0000765�0000120�00000027323�13576733427�0017726�0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. use serde_json::{Map, Value}; use std::borrow::Cow::Borrowed; use std::env; use std::ffi::OsStr; use std::io::Read; use std::mem::replace; use std::path::Path; use rustc_test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn}; use util::find_tests::foreach_xml5lib_test; use markup5ever::buffer_queue::BufferQueue; use xml5ever::tendril::{SliceExt, StrTendril}; use xml5ever::tokenizer::{CharacterTokens, Token, TokenSink}; use xml5ever::tokenizer::{CommentToken, EmptyTag, EndTag, ShortTag, StartTag, Tag}; use xml5ever::tokenizer::{Doctype, DoctypeToken, PIToken, Pi}; use xml5ever::tokenizer::{EOFToken, XmlTokenizer, XmlTokenizerOpts}; use xml5ever::tokenizer::{NullCharacterToken, ParseError, TagToken}; use xml5ever::{namespace_url, ns, Attribute, LocalName, QualName}; mod util { pub mod find_tests; } // Return all ways of splitting the string into at most n // possibly-empty pieces. fn splits(s: &str, n: usize) -> Vec<Vec<StrTendril>> { if n == 1 { return vec![vec![s.to_tendril()]]; } let mut points: Vec<usize> = s.char_indices().map(|(n, _)| n).collect(); points.push(s.len()); // do this with iterators? let mut out = vec![]; for p in points.into_iter() { let y = &s[p..]; for mut x in splits(&s[..p], n - 1).into_iter() { x.push(y.to_tendril()); out.push(x); } } out.extend(splits(s, n - 1).into_iter()); out } struct TokenLogger { tokens: Vec<Token>, current_str: StrTendril, exact_errors: bool, } impl TokenLogger { fn new(exact_errors: bool) -> TokenLogger { TokenLogger { tokens: vec![], current_str: StrTendril::new(), exact_errors: exact_errors, } } // Push anything other than character tokens fn push(&mut self, token: Token) { self.finish_str(); self.tokens.push(token); } fn finish_str(&mut self) { if self.current_str.len() > 0 { let s = replace(&mut self.current_str, StrTendril::new()); self.tokens.push(CharacterTokens(s)); } } fn get_tokens(mut self) -> Vec<Token> { self.finish_str(); self.tokens } } impl TokenSink for TokenLogger { fn process_token(&mut self, token: Token) { match token { CharacterTokens(b) => { self.current_str.push_slice(&b); }, NullCharacterToken => { self.current_str.push_char('\0'); }, ParseError(_) => { if self.exact_errors { self.push(ParseError(Borrowed(""))); } }, TagToken(mut t) => { // The spec seems to indicate that one can emit // erroneous end tags with attrs, but the test // cases don't contain them. match t.kind { EndTag => { t.attrs = vec![]; }, _ => t.attrs.sort_by(|a1, a2| a1.name.cmp(&a2.name)), } self.push(TagToken(t)); }, EOFToken => (), _ => self.push(token), } } } fn tokenize_xml(input: Vec<StrTendril>, opts: XmlTokenizerOpts) -> Vec<Token> { let sink = TokenLogger::new(opts.exact_errors); let mut tok = XmlTokenizer::new(sink, opts); let mut buf = BufferQueue::new(); for chunk in input.into_iter() { buf.push_back(chunk); let _ = tok.feed(&mut buf); } let _ = tok.feed(&mut buf); tok.end(); tok.sink.get_tokens() } trait JsonExt: Sized { fn get_str(&self) -> String; fn get_tendril(&self) -> StrTendril; fn get_nullable_tendril(&self) -> Option<StrTendril>; fn get_bool(&self) -> bool; fn get_obj<'t>(&'t self) -> &'t Map<String, Self>; fn get_list<'t>(&'t self) -> &'t Vec<Self>; fn find<'t>(&'t self, key: &str) -> &'t Self; } impl JsonExt for Value { fn get_str(&self) -> String { match *self { Value::String(ref s) => s.to_string(), _ => panic!("Value::get_str: not a String"), } } fn get_tendril(&self) -> StrTendril { match *self { Value::String(ref s) => s.to_tendril(), _ => panic!("Value::get_tendril: not a String"), } } fn get_nullable_tendril(&self) -> Option<StrTendril> { match *self { Value::Null => None, Value::String(ref s) => Some(s.to_tendril()), _ => panic!("Value::get_nullable_tendril: not a String"), } } fn get_bool(&self) -> bool { match *self { Value::Bool(b) => b, _ => panic!("Value::get_bool: not a Boolean"), } } fn get_obj<'t>(&'t self) -> &'t Map<String, Value> { match *self { Value::Object(ref m) => &*m, _ => panic!("Value::get_obj: not an Object"), } } fn get_list<'t>(&'t self) -> &'t Vec<Value> { match *self { Value::Array(ref m) => m, _ => panic!("Value::get_list: not an Array"), } } fn find<'t>(&'t self, key: &str) -> &'t Value { self.get_obj().get(&key.to_string()).unwrap() } } // Parse a JSON object (other than "ParseError") to a token. fn json_to_token(js: &Value) -> Token { let parts = js.as_array().unwrap(); // Collect refs here so we don't have to use "ref" in all the patterns below. let args: Vec<&Value> = parts[1..].iter().collect(); match &*parts[0].get_str() { "StartTag" => TagToken(Tag { kind: StartTag, name: QualName::new(None, ns!(), LocalName::from(args[0].get_str())), attrs: args[1] .get_obj() .iter() .map(|(k, v)| Attribute { name: QualName::new(None, ns!(), LocalName::from(&**k)), value: v.get_tendril(), }) .collect(), }), "EndTag" => TagToken(Tag { kind: EndTag, name: QualName::new(None, ns!(), LocalName::from(args[0].get_str())), attrs: vec![], }), "ShortTag" => TagToken(Tag { kind: ShortTag, name: QualName::new(None, ns!(), LocalName::from(args[0].get_str())), attrs: vec![], }), "EmptyTag" => TagToken(Tag { kind: EmptyTag, name: QualName::new(None, ns!(), LocalName::from(args[0].get_str())), attrs: args[1] .get_obj() .iter() .map(|(k, v)| Attribute { name: QualName::new(None, ns!(), LocalName::from(&**k)), value: v.get_tendril(), }) .collect(), }), "Comment" => CommentToken(args[0].get_tendril()), "Character" => CharacterTokens(args[0].get_tendril()), "PI" => PIToken(Pi { target: args[0].get_tendril(), data: args[1].get_tendril(), }), "DOCTYPE" => DoctypeToken(Doctype { name: args[0].get_nullable_tendril(), public_id: args[1].get_nullable_tendril(), system_id: args[2].get_nullable_tendril(), }), // We don't need to produce NullCharacterToken because // the TokenLogger will convert them to CharacterTokens. _ => panic!("don't understand token {:?}", parts), } } // Parse the "output" field of the test case into a vector of tokens. fn json_to_tokens(js: &Value, exact_errors: bool) -> Vec<Token> { // Use a TokenLogger so that we combine character tokens separated // by an ignored error. let mut sink = TokenLogger::new(exact_errors); for tok in js.as_array().unwrap().iter() { match *tok { Value::String(ref s) if &s[..] == "ParseError" => { sink.process_token(ParseError(Borrowed(""))) }, _ => sink.process_token(json_to_token(tok)), } } sink.get_tokens() } fn mk_xml_test( desc: String, input: String, expect: Value, opts: XmlTokenizerOpts, ) -> TestDescAndFn { TestDescAndFn { desc: TestDesc::new(DynTestName(desc)), testfn: DynTestFn(Box::new(move || { // Split up the input at different points to test incremental tokenization. let insplits = splits(&input, 3); for input in insplits.into_iter() { // Clone 'input' so we have it for the failure message. // Also clone opts. If we don't, we get the wrong // result but the compiler doesn't catch it! // Possibly mozilla/rust#12223. let output = tokenize_xml(input.clone(), opts.clone()); let expect = json_to_tokens(&expect, opts.exact_errors); if output != expect { panic!( "\ninput: {:?}\ngot: {:?}\nexpected: {:?}", input, output, expect ); } } })), } } fn mk_xml_tests(tests: &mut Vec<TestDescAndFn>, filename: &str, js: &Value) { let input: &str = &js.find("input").get_str(); let expect = js.find("output"); let desc = format!("tok: {}: {}", filename, js.find("description").get_str()); // Some tests want to start in a state other than Data. let state_overrides = vec![None]; // Build the tests. for state in state_overrides.into_iter() { for &exact_errors in [false, true].iter() { let mut newdesc = desc.clone(); match state { Some(s) => newdesc = format!("{} (in state {:?})", newdesc, s), None => (), }; if exact_errors { newdesc = format!("{} (exact errors)", newdesc); } tests.push(mk_xml_test( newdesc, String::from(input), expect.clone(), XmlTokenizerOpts { exact_errors: exact_errors, initial_state: state, // Not discarding a BOM is what the test suite expects; see // https://github.com/html5lib/html5lib-tests/issues/2 discard_bom: false, ..Default::default() }, )); } } } fn tests(src_dir: &Path) -> Vec<TestDescAndFn> { let mut tests = vec![]; foreach_xml5lib_test( src_dir, "tokenizer", OsStr::new("test"), |path, mut file| { let mut s = String::new(); file.read_to_string(&mut s) .ok() .expect("file reading error"); let js: Value = serde_json::from_str(&s).ok().expect("json parse error"); match js["tests"] { Value::Array(ref lst) => { for test in lst.iter() { mk_xml_tests( &mut tests, path.file_name().unwrap().to_str().unwrap(), test, ); } }, _ => (), } }, ); tests } fn main() { let args: Vec<_> = env::args().collect(); rustc_test::test_main(&args, tests(Path::new(env!("CARGO_MANIFEST_DIR")))); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������markup5ever_rcdom-0.1.0/tests/xml-tree-builder.rs���������������������������������������������������0100644�0000765�0000120�00000016341�13576733427�0020275�0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. use markup5ever::{namespace_url, ns}; use markup5ever_rcdom::*; use rustc_test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::io::BufRead; use std::iter::repeat; use std::mem::replace; use std::path::Path; use std::{env, fs, io}; use util::find_tests::foreach_xml5lib_test; use xml5ever::driver::parse_document; use xml5ever::tendril::TendrilSink; mod util { pub mod find_tests; } fn parse_tests<It: Iterator<Item = String>>(mut lines: It) -> Vec<HashMap<String, String>> { let mut tests = vec![]; let mut test = HashMap::new(); let mut key: Option<String> = None; let mut val = String::new(); macro_rules! finish_val ( () => ( match key.take() { None => (), Some(key) => { assert!(test.insert(key, replace(&mut val, String::new())).is_none()); } } )); macro_rules! finish_test ( () => ( if !test.is_empty() { tests.push(replace(&mut test, HashMap::new())); } )); loop { match lines.next() { None => break, Some(line) => { if line.starts_with("#") { finish_val!(); if line == "#data" { finish_test!(); } key = Some(line[1..].to_string()); } else { val.push_str(&line); val.push('\n'); } }, } } finish_val!(); finish_test!(); tests } fn serialize(buf: &mut String, indent: usize, handle: Handle) { buf.push_str("|"); buf.push_str(&repeat(" ").take(indent).collect::<String>()); let node = handle; match node.data { NodeData::Document => panic!("should not reach Document"), NodeData::Doctype { ref name, ref public_id, ref system_id, } => { buf.push_str("<!DOCTYPE "); buf.push_str(&name); if !public_id.is_empty() || !system_id.is_empty() { buf.push_str(&format!(" \"{}\" \"{}\"", public_id, system_id)); } buf.push_str(">\n"); }, NodeData::Text { ref contents } => { buf.push_str("\""); buf.push_str(&contents.borrow()); buf.push_str("\"\n"); }, NodeData::ProcessingInstruction { ref target, ref contents, } => { buf.push_str("<?"); buf.push_str(&target); buf.push_str(" "); buf.push_str(&contents); buf.push_str("?>\n"); }, NodeData::Comment { ref contents } => { buf.push_str("<!-- "); buf.push_str(&contents); buf.push_str(" -->\n"); }, NodeData::Element { ref name, ref attrs, .. } => { buf.push_str("<"); if name.ns != ns!() { buf.push_str("{"); buf.push_str(&*name.ns); buf.push_str("}"); }; if let Some(ref prefix) = name.prefix { buf.push_str(&*prefix); buf.push_str(":"); } buf.push_str(&*name.local); buf.push_str(">\n"); let mut attrs = attrs.borrow().clone(); attrs.sort_by(|x, y| x.name.local.cmp(&y.name.local)); // FIXME: sort by UTF-16 code unit for attr in attrs.into_iter() { buf.push_str("|"); buf.push_str(&repeat(" ").take(indent + 2).collect::<String>()); if &*attr.name.ns != "" { buf.push_str("{"); buf.push_str(&*attr.name.ns); buf.push_str("}"); } if let Some(attr_prefix) = attr.name.prefix { buf.push_str(&*attr_prefix); buf.push_str(":"); } buf.push_str(&format!("{}=\"{}\"\n", attr.name.local, attr.value)); } }, } for child in node.children.borrow().iter() { serialize(buf, indent + 2, child.clone()); } } // Ignore tests containing these strings; we don't support these features yet. static IGNORE_SUBSTRS: &'static [&'static str] = &["<template"]; fn make_xml_test( tests: &mut Vec<TestDescAndFn>, ignores: &HashSet<String>, filename: &str, idx: usize, fields: HashMap<String, String>, ) { let get_field = |key| { let field = fields.get(key).expect("missing field"); field.trim_end_matches('\n').to_string() }; let data = get_field("data"); let expected = get_field("document"); let name = format!("tb: {}-{}", filename, idx); let ignore = ignores.contains(&name) || IGNORE_SUBSTRS.iter().any(|&ig| data.contains(ig)); tests.push(TestDescAndFn { desc: TestDesc { ignore: ignore, ..TestDesc::new(DynTestName(name)) }, testfn: DynTestFn(Box::new(move || { let mut result = String::new(); let dom = parse_document(RcDom::default(), Default::default()).one(data.clone()); for child in dom.document.children.borrow().iter() { serialize(&mut result, 1, child.clone()); } let len = result.len(); result.truncate(len - 1); // drop the trailing newline if result != expected { panic!( "\ninput: {}\ngot:\n{}\nexpected:\n{}\n", data, result, expected ); } })), }); } fn tests(src_dir: &Path, ignores: &HashSet<String>) -> Vec<TestDescAndFn> { let mut tests = vec![]; foreach_xml5lib_test( src_dir, "tree-construction", OsStr::new("dat"), |path, file| { let buf = io::BufReader::new(file); let lines = buf.lines().map(|res| res.ok().expect("couldn't read")); let data = parse_tests(lines); for (i, test) in data.into_iter().enumerate() { make_xml_test( &mut tests, ignores, path.file_name().unwrap().to_str().unwrap(), i, test, ); } }, ); tests } fn main() { let args: Vec<_> = env::args().collect(); let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let mut ignores = HashSet::new(); if let Ok(f) = fs::File::open(&src_dir.join("data/test/ignore")) { let r = io::BufReader::new(f); for ln in r.lines() { ignores.insert(ln.unwrap().trim_end().to_string()); } } rustc_test::test_main(&args, tests(src_dir, &ignores)); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������markup5ever_rcdom-0.1.0/Cargo.lock������������������������������������������������������������������0000644�����������������00000030310�13576733505�0012550�0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "c2-chacha" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" dependencies = [ "ppv-lite86", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "futf" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" dependencies = [ "mac", "new_debug_unreachable", ] [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ "unicode-width", ] [[package]] name = "getrandom" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "html5ever" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", "syn", ] [[package]] name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ "cfg-if", ] [[package]] name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab" dependencies = [ "log", "phf", "phf_codegen", "serde", "serde_derive", "serde_json", "string_cache", "string_cache_codegen", "tendril", ] [[package]] name = "markup5ever_rcdom" version = "0.1.0" dependencies = [ "html5ever", "markup5ever", "rustc-test", "serde_json", "tendril", "xml5ever", ] [[package]] name = "new_debug_unreachable" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" [[package]] name = "phf" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" dependencies = [ "phf_shared", "rand", ] [[package]] name = "phf_shared" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" dependencies = [ "siphasher", ] [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" dependencies = [ "getrandom", "libc", "rand_chacha", "rand_core", "rand_hc", "rand_pcg", ] [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ "c2-chacha", "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ "rand_core", ] [[package]] name = "rand_pcg" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" [[package]] name = "rustc-test" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc74abd96aeb3d5f68702101c903c661869fba4867f2ddf4836032f018ad4728" dependencies = [ "getopts", "libc", "rustc-serialize", "rustc_version", "term", "time", ] [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ "semver", ] [[package]] name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" [[package]] name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "siphasher" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83da420ee8d1a89e640d0948c646c1c088758d3a3c538f943bfa97bdac17929d" [[package]] name = "string_cache" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" dependencies = [ "lazy_static", "new_debug_unreachable", "phf_shared", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", ] [[package]] name = "syn" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "tendril" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" dependencies = [ "futf", "mac", "utf-8", ] [[package]] name = "term" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" dependencies = [ "kernel32-sys", "winapi 0.2.8", ] [[package]] name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" dependencies = [ "libc", "redox_syscall", "winapi 0.3.8", ] [[package]] name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "utf-8" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "wasi" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xml5ever" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59" dependencies = [ "log", "mac", "markup5ever", "time", ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������