diff --git a/.travis.yml b/.travis.yml index e053cd63..1b27aa62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,10 @@ branches: - servo language: rust -rust: nightly +rust: + - nightly + - beta + - stable script: scripts/travis-build.sh after_success: curl https://raw.githubusercontent.com/kmcallister/travis-doc-upload/master/travis-doc-upload.sh | sh diff --git a/Cargo.toml b/Cargo.toml index 776f570c..78686d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT / Apache-2.0" repository = "https://github.com/servo/html5ever" description = "High-performance browser-grade HTML5 parser" documentation = "https://kmcallister.github.io/docs/html5ever/html5ever/index.html" +build = "build.rs" [lib] name = "html5ever" @@ -14,26 +15,26 @@ name = "html5ever" # https://github.com/rust-lang/cargo/issues/1512 doctest = false +[features] +unstable = ["html5ever_macros", "tendril/unstable", "string_cache/unstable", "string_cache_plugin", "rc/unstable"] + [dependencies] time = "0" log = "0" phf = "0.7" -phf_macros = "0.7" -string_cache = "0" -string_cache_plugin = "0" +string_cache = "0.1.8" +string_cache_plugin = { version = "0.1.5", optional = true } mac = "0" -tendril = "0.1" - -[dependencies.html5ever_macros] -version = "0.1.0" -path = "macros" +tendril = "0.1.3" +rc = "0.1.1" [dev-dependencies] -rustc-serialize = "0" +rustc-serialize = "0.3.15" -[dev-dependencies.html5ever_test_util] -version = "0.1.0" -path = "test_util" +[build-dependencies] +phf_codegen = "0.7.3" +rustc-serialize = "0.3.15" +html5ever_macros = { version = "0.1.0", path = "macros", optional = true } [profile.dev] debug = false diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..70194dfe --- /dev/null +++ b/build.rs @@ -0,0 +1,129 @@ +// Copyright 2014-2015 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 phf_codegen; +extern crate rustc_serialize; + +use rustc_serialize::json::{Json, Decoder}; +use rustc_serialize::Decodable; +use std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +fn main() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let rules_rs = Path::new(&manifest_dir).join("src/tree_builder/rules.rs"); + expand_match_tokens( + &rules_rs, + // Keep the expanded file in the source directory, so that `cargo publish` ships it. + &rules_rs.with_extension("expanded.rs")); + + named_entities_to_phf( + &Path::new(&manifest_dir).join("data/entities.json"), + &Path::new(&env::var("OUT_DIR").unwrap()).join("named_entities.rs")) +} + +#[cfg(feature = "unstable")] +fn expand_match_tokens(from: &Path, to: &Path) { + extern crate html5ever_macros; + + html5ever_macros::pre_expand(from, to); +} + +#[cfg(not(feature = "unstable"))] +fn expand_match_tokens(from: &Path, to: &Path) { + use std::io::stderr; + use std::process::exit; + + if let Err(error) = check_hash(from, to) { + writeln!( + stderr(), + r" +{} is missing or not up to date with {}: +{} + +Run `cargo build --features unstable` to update it. + +If you’re using html5ever as a dependency, this is a bad release. +Please file an issue at https://github.com/servo/html5ever/issues/new +with the output of `cargo pkgid html5ever`. +", + to.file_name().unwrap().to_string_lossy(), + from.file_name().unwrap().to_string_lossy(), + error + ).unwrap(); + exit(1); + } +} + +#[cfg(not(feature = "unstable"))] +fn check_hash(from: &Path, to: &Path) -> Result<(), String> { + use std::hash::{Hash, Hasher, SipHasher}; + use std::io::Read; + + // Unwrap here as the source file is expected to exist. + let mut file_from = File::open(from).unwrap(); + let mut source = String::new(); + let mut hasher = SipHasher::new(); + file_from.read_to_string(&mut source).unwrap(); + source.hash(&mut hasher); + let source_hash = hasher.finish(); + + // IO errors from here indicate we need to regenerate the expanded file. + let mut file_to = try!(File::open(to).map_err(|e| e.to_string())); + let mut expanded = String::new(); + try!(file_to.read_to_string(&mut expanded).map_err(|e| e.to_string())); + let prefix = "// source SipHash: "; + let line = try!(expanded.lines().find(|line| line.starts_with(prefix)) + .ok_or("source hash not found".to_string())); + let expected_hash = try!(line[prefix.len()..].parse::().map_err(|e| e.to_string())); + if source_hash == expected_hash { + Ok(()) + } else { + Err("different hash".to_string()) + } +} + +fn named_entities_to_phf(from: &Path, to: &Path) { + // A struct matching the entries in entities.json. + #[derive(RustcDecodable)] + struct CharRef { + codepoints: Vec, + //characters: String, // Present in the file but we don't need it + } + + let json = Json::from_reader(&mut File::open(from).unwrap()).unwrap(); + let entities: HashMap = Decodable::decode(&mut Decoder::new(json)).unwrap(); + let mut entities: HashMap<&str, (u32, u32)> = entities.iter().map(|(name, char_ref)| { + assert!(name.starts_with("&")); + assert!(char_ref.codepoints.len() <= 2); + (&name[1..], (char_ref.codepoints[0], *char_ref.codepoints.get(1).unwrap_or(&0))) + }).collect(); + + // Add every missing prefix of those keys, mapping to NULL characters. + for key in entities.keys().cloned().collect::>() { + for n in 1 .. key.len() { + entities.entry(&key[..n]).or_insert((0, 0)); + } + } + entities.insert("", (0, 0)); + + let mut phf_map = phf_codegen::Map::new(); + for (key, value) in entities { + phf_map.entry(key, &format!("{:?}", value)); + } + + let mut file = File::create(to).unwrap(); + write!(&mut file, "pub static NAMED_ENTITIES: Map<&'static str, (u32, u32)> = ").unwrap(); + phf_map.build(&mut file).unwrap(); + write!(&mut file, ";\n").unwrap(); +} diff --git a/capi/Cargo.toml b/capi/Cargo.toml index 456a27c4..0b868c67 100644 --- a/capi/Cargo.toml +++ b/capi/Cargo.toml @@ -16,3 +16,4 @@ tendril = "0.1" [dependencies.html5ever] path = "../" +features = ["unstable"] diff --git a/dom_sink/Cargo.toml b/dom_sink/Cargo.toml deleted file mode 100644 index 255ac48e..00000000 --- a/dom_sink/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "html5ever_dom_sink" -version = "0.2.0" -authors = [ "The html5ever Project Developers" ] -description = "Basic DOM implementation for html5ever" -license = "MIT / Apache-2.0" -repository = "https://github.com/servo/html5ever" - -[lib] -name = "html5ever_dom_sink" - -[dependencies] -mac = "0" -string_cache = "0" -tendril = "0.1" - -[dev-dependencies] -string_cache_plugin = "0" - -[dependencies.html5ever] -version = "0.2" -path = "../" - -[dev-dependencies.html5ever_test_util] -version = "0.1.0" -path = "../test_util" diff --git a/dom_sink/src/common.rs b/dom_sink/src/common.rs deleted file mode 100644 index 9f021b0c..00000000 --- a/dom_sink/src/common.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 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::tokenizer::Attribute; - -use string_cache::QualName; -use tendril::StrTendril; - -pub use self::NodeEnum::{Document, Doctype, Text, Comment, Element}; - -/// The different kinds of nodes in the DOM. -#[derive(Debug)] -pub enum NodeEnum { - /// The `Document` itself. - Document, - - /// A `DOCTYPE` with name, public id, and system id. - Doctype(StrTendril, StrTendril, StrTendril), - - /// A text node. - Text(StrTendril), - - /// A comment. - Comment(StrTendril), - - /// An element with attributes. - Element(QualName, Vec), -} diff --git a/dom_sink/src/lib.rs b/dom_sink/src/lib.rs deleted file mode 100644 index 2bee53e8..00000000 --- a/dom_sink/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 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. - -#![crate_name="html5ever_dom_sink"] -#![crate_type="dylib"] - -#![feature(box_syntax, append, rc_weak)] - -extern crate html5ever; - -#[macro_use] -extern crate string_cache; - -extern crate tendril; - -#[macro_use] -extern crate mac; - -pub mod common; -pub mod rcdom; -pub mod owned_dom; diff --git a/dom_sink/src/owned_dom.rs b/dom_sink/src/owned_dom.rs deleted file mode 100644 index a2c21969..00000000 --- a/dom_sink/src/owned_dom.rs +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2014 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 DOM where every node is owned by its parent. -//! -//! Since ownership is more complicated during parsing, we actually -//! build a different type and then transmute to the public `Node`. -//! This is believed to be memory safe, but if you want to be extra -//! careful you can use `RcDom` instead. -//! -//! **Warning: Unstable.** This module uses unsafe code, has not -//! been thoroughly audited, and the performance gains vs. RcDom -//! have not been demonstrated. - -use common::{NodeEnum, Document, Doctype, Text, Comment, Element}; - -use html5ever::tokenizer::Attribute; -use html5ever::tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText}; -use html5ever::tree_builder; -use html5ever::serialize::{Serializable, Serializer}; -use html5ever::serialize::TraversalScope; -use html5ever::serialize::TraversalScope::{IncludeNode, ChildrenOnly}; -use html5ever::driver::ParseResult; - -use std::{mem, ptr}; -use std::cell::UnsafeCell; -use std::default::Default; -use std::mem::transmute; -use std::borrow::Cow; -use std::io::{self, Write}; -use std::collections::HashSet; -use std::ops::{Deref, DerefMut}; - -use string_cache::QualName; -use tendril::StrTendril; - -/// The internal type we use for nodes during parsing. -pub struct SquishyNode { - node: NodeEnum, - parent: Handle, - children: Vec, -} - -impl SquishyNode { - fn new(node: NodeEnum) -> SquishyNode { - SquishyNode { - node: node, - parent: Handle::null(), - children: vec!(), - } - } -} - -pub struct Handle { - ptr: *const UnsafeCell, -} - -impl Handle { - fn new(ptr: *const UnsafeCell) -> Handle { - Handle { - ptr: ptr, - } - } - - fn null() -> Handle { - Handle::new(ptr::null()) - } - - fn is_null(&self) -> bool { - self.ptr.is_null() - } -} - -impl PartialEq for Handle { - fn eq(&self, other: &Handle) -> bool { - self.ptr == other.ptr - } -} - -impl Eq for Handle { } - -impl Clone for Handle { - fn clone(&self) -> Handle { - Handle::new(self.ptr) - } -} - -impl Copy for Handle { } - -// The safety of `Deref` and `DerefMut` depends on the invariant that `Handle`s -// can't escape the `Sink`, because nodes are deallocated by consuming the -// `Sink`. - -impl DerefMut for Handle { - fn deref_mut<'a>(&'a mut self) -> &'a mut SquishyNode { - unsafe { - transmute::<_, &'a mut SquishyNode>((*self.ptr).get()) - } - } -} - -impl Deref for Handle { - type Target = SquishyNode; - fn deref<'a>(&'a self) -> &'a SquishyNode { - unsafe { - transmute::<_, &'a SquishyNode>((*self.ptr).get()) - } - } -} - -fn append(mut new_parent: Handle, mut child: Handle) { - new_parent.children.push(child); - let parent = &mut child.parent; - assert!(parent.is_null()); - *parent = new_parent -} - -fn get_parent_and_index(child: Handle) -> Option<(Handle, usize)> { - if child.parent.is_null() { - return None; - } - - let to_find = child; - match child.parent.children.iter().enumerate().find(|&(_, n)| *n == to_find) { - Some((i, _)) => Some((child.parent, i)), - None => panic!("have parent but couldn't find in parent's children!"), - } -} - -fn append_to_existing_text(mut prev: Handle, text: &str) -> bool { - match prev.deref_mut().node { - Text(ref mut existing) => { - existing.push_slice(text); - true - } - _ => false, - } -} - -pub struct Sink { - nodes: Vec>>, - document: Handle, - errors: Vec>, - quirks_mode: QuirksMode, -} - -impl Default for Sink { - fn default() -> Sink { - let mut sink = Sink { - nodes: vec!(), - document: Handle::null(), - errors: vec!(), - quirks_mode: tree_builder::NoQuirks, - }; - sink.document = sink.new_node(Document); - sink - } -} - -impl Sink { - fn new_node(&mut self, node: NodeEnum) -> Handle { - self.nodes.push(box UnsafeCell::new(SquishyNode::new(node))); - let ptr: *const UnsafeCell = &**self.nodes.last().unwrap(); - Handle::new(ptr) - } - - // FIXME(rust-lang/rust#18296): This is separate from remove_from_parent so - // we can call it. - fn unparent(&mut self, mut target: Handle) { - let (mut parent, i) = unwrap_or_return!(get_parent_and_index(target), ()); - parent.children.remove(i); - target.parent = Handle::null(); - } -} - -impl TreeSink for Sink { - type Handle = Handle; - - fn parse_error(&mut self, msg: Cow<'static, str>) { - self.errors.push(msg); - } - - fn get_document(&mut self) -> Handle { - self.document - } - - fn set_quirks_mode(&mut self, mode: QuirksMode) { - self.quirks_mode = mode; - } - - fn same_node(&self, x: Handle, y: Handle) -> bool { - x == y - } - - fn elem_name(&self, target: Handle) -> QualName { - match target.node { - Element(ref name, _) => name.clone(), - _ => panic!("not an element!"), - } - } - - fn create_element(&mut self, name: QualName, attrs: Vec) -> Handle { - self.new_node(Element(name, attrs)) - } - - fn create_comment(&mut self, text: StrTendril) -> Handle { - self.new_node(Comment(text)) - } - - fn append(&mut self, parent: Handle, child: NodeOrText) { - // Append to an existing Text node if we have one. - match child { - AppendText(ref text) => match parent.children.last() { - Some(h) => if append_to_existing_text(*h, &text) { return; }, - _ => (), - }, - _ => (), - } - - append(parent, match child { - AppendText(text) => self.new_node(Text(text)), - AppendNode(node) => node - }); - } - - fn append_before_sibling(&mut self, - sibling: Handle, - child: NodeOrText) -> Result<(), NodeOrText> { - let (mut parent, i) = unwrap_or_return!(get_parent_and_index(sibling), Err(child)); - - let mut child = match (child, i) { - // No previous node. - (AppendText(text), 0) => self.new_node(Text(text)), - - // Look for a text node before the insertion point. - (AppendText(text), i) => { - let prev = parent.children[i-1]; - if append_to_existing_text(prev, &text) { - return Ok(()); - } - self.new_node(Text(text)) - } - - // The tree builder promises we won't have a text node after - // the insertion point. - - // Any other kind of node. - (AppendNode(node), _) => node, - }; - - if !child.parent.is_null() { - self.unparent(child); - } - - child.parent = parent; - parent.children.insert(i, child); - Ok(()) - } - - fn append_doctype_to_document(&mut self, - name: StrTendril, - public_id: StrTendril, - system_id: StrTendril) { - append(self.document, self.new_node(Doctype(name, public_id, system_id))); - } - - fn add_attrs_if_missing(&mut self, mut target: Handle, mut attrs: Vec) { - let existing = match target.deref_mut().node { - Element(_, ref mut attrs) => attrs, - _ => return, - }; - - // FIXME: quadratic time - attrs.retain(|attr| - !existing.iter().any(|e| e.name == attr.name)); - existing.extend(attrs.into_iter()); - } - - fn remove_from_parent(&mut self, target: Handle) { - self.unparent(target); - } - - fn reparent_children(&mut self, mut node: Handle, mut new_parent: Handle) { - new_parent.children.append(&mut node.children); - } - - fn mark_script_already_started(&mut self, _node: Handle) { } -} - -pub struct Node { - pub node: NodeEnum, - _parent_not_accessible: usize, - pub children: Vec>, -} - -pub struct OwnedDom { - pub document: Box, - pub errors: Vec>, - pub quirks_mode: QuirksMode, -} - -impl ParseResult for OwnedDom { - type Sink = Sink; - - fn get_result(sink: Sink) -> OwnedDom { - fn walk(live: &mut HashSet, node: Handle) { - live.insert(node.ptr as usize); - for &child in node.deref().children.iter() { - walk(live, child); - } - } - - // Collect addresses of all the nodes that made it into the final tree. - let mut live = HashSet::new(); - walk(&mut live, sink.document); - - // Forget about the nodes in the final tree; they will be owned by - // their parent. In the process of iterating we drop all nodes that - // aren't in the tree. - for node in sink.nodes.into_iter() { - let ptr: *const UnsafeCell = &*node; - if live.contains(&(ptr as usize)) { - mem::forget(node); - } - } - - let old_addrs = addrs_of!(sink.document => node, parent, children); - - // Transmute the root to a Node, finalizing the transfer of ownership. - let document = unsafe { - mem::transmute::<*const UnsafeCell, Box>(sink.document.ptr) - }; - - // FIXME: do this assertion statically - let new_addrs = addrs_of!(document => node, _parent_not_accessible, children); - assert_eq!(old_addrs, new_addrs); - - OwnedDom { - document: document, - errors: sink.errors, - quirks_mode: sink.quirks_mode, - } - } -} - -impl Serializable for Node { - fn serialize<'wr, Wr: Write>(&self, - serializer: &mut Serializer<'wr, Wr>, - traversal_scope: TraversalScope) -> io::Result<()> { - - match (traversal_scope, &self.node) { - (_, &Element(ref name, ref attrs)) => { - if traversal_scope == IncludeNode { - try!(serializer.start_elem(name.clone(), - attrs.iter().map(|at| (&at.name, &at.value[..])))); - } - - for child in self.children.iter() { - try!(child.serialize(serializer, IncludeNode)); - } - - if traversal_scope == IncludeNode { - try!(serializer.end_elem(name.clone())); - } - Ok(()) - } - - (ChildrenOnly, &Document) => { - for child in self.children.iter() { - try!(child.serialize(serializer, IncludeNode)); - } - Ok(()) - } - - (ChildrenOnly, _) => Ok(()), - - (IncludeNode, &Doctype(ref name, _, _)) => serializer.write_doctype(&name), - (IncludeNode, &Text(ref text)) => serializer.write_text(&text), - (IncludeNode, &Comment(ref text)) => serializer.write_comment(&text), - - (IncludeNode, &Document) => panic!("Can't serialize Document node itself"), - } - } -} diff --git a/dom_sink/examples/html2html.rs b/examples/html2html.rs similarity index 95% rename from dom_sink/examples/html2html.rs rename to examples/html2html.rs index 51f0dda3..26cbeefe 100644 --- a/dom_sink/examples/html2html.rs +++ b/examples/html2html.rs @@ -17,7 +17,6 @@ extern crate tendril; extern crate html5ever; -extern crate html5ever_dom_sink; use std::io::{self, Write}; use std::default::Default; @@ -27,7 +26,7 @@ use tendril::{ByteTendril, ReadExt}; use html5ever::driver::ParseOpts; use html5ever::tree_builder::TreeBuilderOpts; use html5ever::{parse, one_input, serialize}; -use html5ever_dom_sink::rcdom::RcDom; +use html5ever::rcdom::RcDom; fn main() { let mut input = ByteTendril::new(); diff --git a/examples/noop-tokenize.rs b/examples/noop-tokenize.rs index b6de3753..4a0599d2 100644 --- a/examples/noop-tokenize.rs +++ b/examples/noop-tokenize.rs @@ -9,29 +9,24 @@ // Run a single benchmark once. For use with profiling tools. -#![feature(test)] - -extern crate test; extern crate html5ever; extern crate tendril; use std::io; use std::default::Default; -use test::black_box; - use tendril::{ByteTendril, ReadExt}; use html5ever::tokenizer::{TokenSink, Token}; use html5ever::driver::{tokenize_to, one_input}; -struct Sink; +struct Sink(Vec); impl TokenSink for Sink { fn process_token(&mut self, token: Token) { // Don't use the token, but make sure we don't get // optimized out entirely. - black_box(token); + self.0.push(token); } } @@ -40,5 +35,5 @@ fn main() { io::stdin().read_to_tendril(&mut input).unwrap(); let input = input.try_reinterpret().unwrap(); - tokenize_to(Sink, one_input(input), Default::default()); + tokenize_to(Sink(Vec::new()), one_input(input), Default::default()); } diff --git a/dom_sink/examples/print-rcdom.rs b/examples/print-rcdom.rs similarity index 80% rename from dom_sink/examples/print-rcdom.rs rename to examples/print-rcdom.rs index d5ff6043..115794af 100644 --- a/dom_sink/examples/print-rcdom.rs +++ b/examples/print-rcdom.rs @@ -7,11 +7,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(plugin, str_escape)] -#![plugin(string_cache_plugin)] +#![cfg_attr(feature = "unstable", feature(plugin))] +#![cfg_attr(feature = "unstable", plugin(string_cache_plugin))] extern crate html5ever; -extern crate html5ever_dom_sink; #[macro_use] extern crate string_cache; @@ -24,8 +23,7 @@ use std::string::String; use tendril::{ByteTendril, ReadExt}; use html5ever::{parse, one_input}; -use html5ever_dom_sink::common::{Document, Doctype, Text, Comment, Element}; -use html5ever_dom_sink::rcdom::{RcDom, Handle}; +use html5ever::rcdom::{Document, Doctype, Text, Comment, Element, RcDom, Handle}; // This is not proper HTML serialization, of course. @@ -41,10 +39,10 @@ fn walk(indent: usize, handle: Handle) { => println!("", *name, *public, *system), Text(ref text) - => println!("#text: {}", text.escape_default()), + => println!("#text: {}", escape_default(text)), Comment(ref text) - => println!("", text.escape_default()), + => println!("", escape_default(text)), Element(ref name, ref attrs) => { assert!(name.ns == ns!(html)); @@ -62,6 +60,11 @@ fn walk(indent: usize, handle: Handle) { } } +// 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 mut input = ByteTendril::new(); io::stdin().read_to_tendril(&mut input).unwrap(); diff --git a/examples/print-tree-actions.rs b/examples/print-tree-actions.rs index 3e175e3d..08324fdb 100644 --- a/examples/print-tree-actions.rs +++ b/examples/print-tree-actions.rs @@ -7,8 +7,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(str_escape)] - extern crate string_cache; extern crate tendril; extern crate html5ever; @@ -70,7 +68,7 @@ impl TreeSink for Sink { fn create_comment(&mut self, text: StrTendril) -> usize { let id = self.get_id(); - println!("Created comment \"{}\" as {}", text.escape_default(), id); + println!("Created comment \"{}\" as {}", escape_default(&text), id); id } @@ -79,7 +77,7 @@ impl TreeSink for Sink { AppendNode(n) => println!("Append node {} to {}", n, parent), AppendText(t) - => println!("Append text to {}: \"{}\"", parent, t.escape_default()), + => println!("Append text to {}: \"{}\"", parent, escape_default(&t)), } } @@ -90,7 +88,7 @@ impl TreeSink for Sink { AppendNode(n) => println!("Append node {} before {}", n, sibling), AppendText(t) - => println!("Append text before {}: \"{}\"", sibling, t.escape_default()), + => println!("Append text before {}: \"{}\"", sibling, escape_default(&t)), } // `sibling` will have a parent unless a script moved it, and we're @@ -125,6 +123,11 @@ impl TreeSink for Sink { } } +// 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 sink = Sink { next_id: 1, diff --git a/examples/tokenize.rs b/examples/tokenize.rs index 12a91893..42f7c399 100644 --- a/examples/tokenize.rs +++ b/examples/tokenize.rs @@ -7,8 +7,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(str_escape)] - extern crate tendril; extern crate html5ever; @@ -38,7 +36,7 @@ impl TokenPrinter { fn do_char(&mut self, c: char) { self.is_char(true); - print!("{}", c.to_string().escape_default()); + print!("{}", c.escape_default().collect::()); } } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index bf997cde..1a70fac2 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -8,10 +8,8 @@ repository = "https://github.com/servo/html5ever" description = "High-performance browser-grade HTML5 parser − compiler plugins" [lib] - name = "html5ever_macros" plugin = true [dependencies] -rustc-serialize = "0" mac = "0" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 5aa3be34..ad0595ef 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -7,21 +7,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![crate_name="html5ever_macros"] -#![crate_type="dylib"] - -#![feature(plugin_registrar, quote, rustc_private, slice_patterns)] +#![feature(quote, rustc_private, rc_unique)] #![deny(warnings)] extern crate syntax; -extern crate rustc; -extern crate rustc_serialize; #[macro_use] extern crate mac; -use rustc::plugin::Registry; - // See https://github.com/rust-lang/rust/pull/23857 macro_rules! panictry { ($e:expr) => ({ @@ -34,12 +27,7 @@ macro_rules! panictry { } // Make these public so that rustdoc will generate documentation for them. -pub mod named_entities; pub mod match_token; +pub mod pre_expand; -// NB: This needs to be public or we get a linker error. -#[plugin_registrar] -pub fn plugin_registrar(reg: &mut Registry) { - reg.register_macro("named_entities", named_entities::expand); - reg.register_macro("match_token", match_token::expand); -} +pub use pre_expand::pre_expand; diff --git a/macros/src/match_token.rs b/macros/src/match_token.rs index b5131cc3..65b6938f 100644 --- a/macros/src/match_token.rs +++ b/macros/src/match_token.rs @@ -292,14 +292,24 @@ fn make_tag_pattern(cx: &mut ExtCtxt, binding: Tokens, tag: Tag) -> Tokens { ) } +macro_rules! ext_err { + ($span: expr, $message: expr) => { return Err(($span, $message)) } +} +macro_rules! ext_err_if { + ($condition: expr, $span: expr, $message: expr) => { + if $condition { return Err(($span, $message)) } + } +} + /// Expand the `match_token!` macro. -pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box { +pub fn expand_to_tokens(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) + -> Result, (Span, &'static str)> { let Match { discriminant, mut arms } = panictry!(parse(cx, toks)); // Handle the last arm specially at the end. let last_arm = match arms.pop() { Some(x) => x, - None => ext_bail!(cx, span, "need at least one match arm"), + None => ext_err!(span, "need at least one match arm"), }; // Code for the arms other than the last one. @@ -324,11 +334,11 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box ext_bail!(cx, rhs.span, "'else' may not appear with an ordinary pattern"), + => ext_err!(rhs.span, "'else' may not appear with an ordinary pattern"), // ordinary pattern => expression (Pat(pat), Expr(expr)) => { - ext_bail_if!(!wildcards.is_empty(), cx, lhs.span, + ext_err_if!(!wildcards.is_empty(), lhs.span, "ordinary patterns may not appear after wildcard tags"); push_all(&mut arm_code, quote_tokens!(&mut *cx, $binding $pat => $expr,)); } @@ -336,8 +346,8 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box ... => else (Tags(tags), Else) => { for Spanned { span, node: tag } in tags.into_iter() { - ext_bail_if!(!seen_tags.insert(tag.clone()), cx, span, "duplicate tag"); - ext_bail_if!(tag.name.is_none(), cx, rhs.span, + ext_err_if!(!seen_tags.insert(tag.clone()), span, "duplicate tag"); + ext_err_if!(tag.name.is_none(), rhs.span, "'else' may not appear with a wildcard tag"); match wild_excluded.entry(tag.kind) { Occupied(e) => { e.into_mut().push(tag.clone()); } @@ -353,15 +363,15 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box Some(_) => { - ext_bail_if!(!wildcards.is_empty(), cx, lhs.span, + ext_err_if!(!wildcards.is_empty(), lhs.span, "specific tags may not appear after wildcard tags"); - ext_bail_if!(wildcard == Some(true), cx, span, + ext_err_if!(wildcard == Some(true), span, "wildcard tags must appear alone"); if wildcard.is_some() { @@ -375,7 +385,7 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box None => { - ext_bail_if!(wildcard.is_some(), cx, span, + ext_err_if!(wildcard.is_some(), span, "wildcard tags must appear alone"); wildcard = Some(true); wildcards.push(WildcardArm { @@ -388,7 +398,7 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box ext_bail!(cx, lhs.span, "[internal macro error] tag arm with no tags"), + None => ext_err!(lhs.span, "[internal macro error] tag arm with no tags"), Some(false) => { push_all(&mut arm_code, quote_tokens!(&mut *cx, => $expr,)); } @@ -424,12 +434,12 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box ext_bail!(cx, id.span, "the last arm cannot have an @-binding"), - (None, Tags(_), _) => ext_bail!(cx, lhs.span, "the last arm cannot have tag patterns"), - (None, _, Else) => ext_bail!(cx, rhs.span, "the last arm cannot use 'else'"), + (Some(id), _, _) => ext_err!(id.span, "the last arm cannot have an @-binding"), + (None, Tags(_), _) => ext_err!(lhs.span, "the last arm cannot have tag patterns"), + (None, _, Else) => ext_err!(rhs.span, "the last arm cannot use 'else'"), (None, Pat(p), Expr(e)) => match p.node { ast::PatWild(ast::PatWildSingle) | ast::PatIdent(..) => (p, e), - _ => ext_bail!(cx, lhs.span, "the last arm must have a wildcard or ident pattern"), + _ => ext_err!(lhs.span, "the last arm must have a wildcard or ident pattern"), }, }; @@ -456,7 +466,7 @@ pub fn expand(cx: &mut ExtCtxt, span: Span, toks: &[ast::TokenTree]) -> Box or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unused_imports)] // for quotes - -use std::path::PathBuf; -use std::fs; -use std::str::FromStr; -use std::collections::HashMap; -use std::convert::From; - -use rustc_serialize::json; -use rustc_serialize::json::Json; -use rustc_serialize::Decodable; -use syntax::codemap::Span; -use syntax::ast::{Path, ExprLit, Lit_, TokenTree, TtToken}; -use syntax::parse::token; -use syntax::ext::base::{ExtCtxt, MacResult, MacEager}; -use syntax::ext::source_util::expand_file; - -// A struct matching the entries in entities.json. -// Simplifies JSON parsing because we can use Decodable. -#[derive(RustcDecodable)] -struct CharRef { - codepoints: Vec, - //characters: String, // Present in the file but we don't need it -} - -// Build the map from entity names (and their prefixes) to characters. -fn build_map(js: Json) -> Option> { - let mut map = HashMap::new(); - let json_map = match js { - Json::Object(m) => m, - _ => return None, - }; - - // Add every named entity to the map. - for (k,v) in json_map.into_iter() { - let mut decoder = json::Decoder::new(v); - let CharRef { codepoints }: CharRef - = Decodable::decode(&mut decoder).ok().expect("bad CharRef"); - - assert!((codepoints.len() >= 1) && (codepoints.len() <= 2)); - let mut codepoint_pair = [0, 0]; - for (i,n) in codepoints.into_iter().enumerate() { - codepoint_pair[i] = n; - } - - // Slice off the initial '&' - assert!(k.chars().next() == Some('&')); - map.insert(k[1..].to_string(), codepoint_pair); - } - - // Add every missing prefix of those keys, mapping to NULL characters. - map.insert("".to_string(), [0, 0]); - let keys: Vec = map.keys().map(|k| k.to_string()).collect(); - for k in keys.into_iter() { - for n in 1 .. k.len() { - let pfx = k[..n].to_string(); - if !map.contains_key(&pfx) { - map.insert(pfx, [0, 0]); - } - } - } - - Some(map) -} - -// Expand named_entities!("path/to/entities.json") into an invocation of phf_map!(). -pub fn expand(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box { - let usage = "Usage: named_entities!(\"path/to/entities.json\")"; - - // Argument to the macro should be a single literal string: a path to - // entities.json, relative to the file containing the macro invocation. - let json_filename = match tt { - [TtToken(_, token::Literal(token::Lit::Str_(s), _))] => s.as_str().to_string(), - _ => ext_bail!(cx, sp, usage), - }; - - // Get the result of calling file!() in the same place as our macro. - let mod_filename = ext_expect!(cx, sp, match expand_file(cx, sp, &[]).make_expr() { - Some(e) => match e.node { - ExprLit(ref s) => match s.node { - Lit_::LitStr(ref s, _) => Some(s.to_string()), - _ => None, - }, - _ => None, - }, - _ => None, - }, "unexpected result from file!()"); - - // Combine those to get an absolute path to entities.json. - let mut path: PathBuf = From::from(&mod_filename); - path.pop(); - path.push(&json_filename); - - // Open the JSON file, parse it, and build the map from names to characters. - let mut json_file = ext_expect!(cx, sp, fs::File::open(&path).ok(), - "can't open JSON file"); - let js = ext_expect!(cx, sp, Json::from_reader(&mut json_file).ok(), - "can't parse JSON file"); - let map = ext_expect!(cx, sp, build_map(js), - "JSON file does not match entities.json format"); - - // Emit a macro invocation of the form - // - // phf_map!(k => v, k => v, ...) - let toks: Vec<_> = map.into_iter().flat_map(|(k, [c0, c1])| { - let k = &k[..]; - (quote_tokens!(&mut *cx, $k => [$c0, $c1],)).into_iter() - }).collect(); - MacEager::expr(quote_expr!(&mut *cx, phf_map!($toks))) -} diff --git a/macros/src/pre_expand.rs b/macros/src/pre_expand.rs new file mode 100644 index 00000000..75abb47a --- /dev/null +++ b/macros/src/pre_expand.rs @@ -0,0 +1,125 @@ +// Copyright 2015 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 match_token; +use std::fs::File; +use std::hash::{Hash, Hasher, SipHasher}; +use std::io::{Read, Write}; +use std::path::Path; +use std::rc::Rc; +use syntax::{ast, codemap, ext, parse, print}; +use syntax::parse::token; +use syntax::parse::attr::ParserAttr; + +pub fn pre_expand(from: &Path, to: &Path) { + let mut source = String::new(); + let mut file_from = File::open(from).unwrap(); + file_from.read_to_string(&mut source).unwrap(); + + let mut file_to = File::create(to).unwrap(); + write_header(&from, &source, &mut file_to); + + let sess = parse::ParseSess::new(); + let mut cx = ext::base::ExtCtxt::new(&sess, vec![], + ext::expand::ExpansionConfig::default("".to_owned())); + + let from = from.to_string_lossy().into_owned(); + let tts = parse::parse_tts_from_source_str(from, source, vec![], &sess); + let tts = find_and_expand_match_token(&mut cx, tts); + let tts = pretty(&mut cx, tts); + + let expanded = print::pprust::tts_to_string(&tts); + file_to.write_all(expanded.as_bytes()).unwrap(); +} + +fn find_and_expand_match_token(cx: &mut ext::base::ExtCtxt, tts: Vec) + -> Vec { + let mut expanded = Vec::new(); + let mut tts = tts.into_iter().peekable(); + while let Some(tt) = tts.next() { + match tt { + ast::TokenTree::TtToken(span, token::Token::Ident(ident, token::IdentStyle::Plain)) + if ident.name == "match_token" + => { + // `!` + if !matches!(tts.next(), Some(ast::TokenTree::TtToken(_, token::Token::Not))) { + expanded.push(tt); + continue + } + match tts.next() { + Some(ast::TokenTree::TtDelimited(_, block)) => { + cx.bt_push(expn_info(span)); + expanded.extend( + match match_token::expand_to_tokens(cx, span, &block.tts) { + Ok(tts) => tts, + Err((span, message)) => { + cx.parse_sess.span_diagnostic.span_err(span, message); + panic!("Error in match_token! expansion."); + } + }); + cx.bt_pop(); + } + _ => panic!("expected a block after {:?}", span) + } + } + ast::TokenTree::TtDelimited(span, mut block) => { + block.make_unique(); + let block = Rc::try_unwrap(block).unwrap(); + expanded.push(ast::TokenTree::TtDelimited(span, Rc::new(ast::Delimited { + delim: block.delim, + open_span: block.open_span, + tts: find_and_expand_match_token(cx, block.tts), + close_span: block.close_span, + }))) + } + _ => expanded.push(tt) + } + } + expanded +} + +fn expn_info(span: codemap::Span) -> codemap::ExpnInfo { + codemap::ExpnInfo { + call_site: span, + callee: codemap::NameAndSpan { + name: "match_token".to_string(), + format: codemap::ExpnFormat::MacroBang, + allow_internal_unstable: false, + span: None, + } + } +} + +/// Somehow, going through a parser and back to tokens gives nicer whitespace. +fn pretty(cx: &mut ext::base::ExtCtxt, tts: Vec) -> Vec { + let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); + let start_span = parser.span; + let attrs = parser.parse_inner_attributes(); + let mut items = Vec::new(); + while let Some(item) = parser.parse_item() { + items.push(item) + } + cx.bt_push(expn_info(start_span)); + quote_tokens!(&mut *cx, $attrs $items) +} + +fn write_header(source_file_name: &Path, source: &str, file: &mut File) { + let mut hasher = SipHasher::new(); + source.hash(&mut hasher); + let source_hash = hasher.finish(); + + for header_line in source.lines().take_while(|line| line.starts_with("//")) { + writeln!(file, "{}", header_line).unwrap(); + } + writeln!(file, r" +// This file is generated from {} +// source SipHash: {} +", + source_file_name.file_name().unwrap().to_string_lossy(), source_hash).unwrap(); +} diff --git a/scripts/travis-build.sh b/scripts/travis-build.sh index 4330758d..f3169ff0 100755 --- a/scripts/travis-build.sh +++ b/scripts/travis-build.sh @@ -10,17 +10,20 @@ set -ex +# Test without unstable first, to make sure src/tree_builder/rules.expanded.rs is up-to-date. cargo test --no-run cargo test | ./scripts/shrink-test-output.py r=${PIPESTATUS[0]} if [ $r -ne 0 ]; then exit $r; fi -cargo test --manifest-path dom_sink/Cargo.toml --no-run -cargo test --manifest-path dom_sink/Cargo.toml | ./scripts/shrink-test-output.py -r=${PIPESTATUS[0]} -if [ $r -ne 0 ]; then exit $r; fi +if [ $TRAVIS_RUST_VERSION = nightly ] +then + cargo test --no-run --features unstable + cargo test --features unstable | ./scripts/shrink-test-output.py + r=${PIPESTATUS[0]} + if [ $r -ne 0 ]; then exit $r; fi -cargo test --manifest-path capi/Cargo.toml + cargo test --manifest-path capi/Cargo.toml +fi -cargo doc --manifest-path dom_sink/Cargo.toml -mv dom_sink/target/doc target/doc +cargo doc diff --git a/src/lib.rs b/src/lib.rs index 924b9c68..5259aef0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,13 @@ #![crate_name="html5ever"] #![crate_type="dylib"] -#![feature(plugin, box_syntax, str_char, slice_patterns, iter_arith, str_escape)] +#![cfg_attr(feature = "unstable", feature(plugin, rc_weak))] #![cfg_attr(test, deny(warnings))] #![allow(unused_parens)] -#![plugin(phf_macros)] -#![plugin(string_cache_plugin)] -#![plugin(html5ever_macros)] +#![cfg_attr(feature = "unstable", plugin(string_cache_plugin))] + +extern crate rc; #[macro_use] extern crate log; @@ -49,7 +49,6 @@ mod util { pub mod tokenizer; pub mod tree_builder; - pub mod serialize; - pub mod driver; +pub mod rcdom; diff --git a/dom_sink/src/rcdom.rs b/src/rcdom.rs similarity index 91% rename from dom_sink/src/rcdom.rs rename to src/rcdom.rs index f5500d60..062b8ef0 100644 --- a/dom_sink/src/rcdom.rs +++ b/src/rcdom.rs @@ -12,26 +12,46 @@ //! This is sufficient as a static parse tree, but don't build a //! web browser using it. :) -use common::{NodeEnum, Document, Doctype, Text, Comment, Element}; - -use html5ever::tokenizer::Attribute; -use html5ever::tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText}; -use html5ever::tree_builder; -use html5ever::serialize::{Serializable, Serializer}; -use html5ever::serialize::TraversalScope; -use html5ever::serialize::TraversalScope::{IncludeNode, ChildrenOnly}; -use html5ever::driver::ParseResult; - use std::cell::RefCell; use std::default::Default; -use std::rc::{Rc, Weak}; use std::borrow::Cow; use std::io::{self, Write}; +use std::mem; use std::ops::{Deref, DerefMut}; +use rc::{Rc, Weak}; use string_cache::QualName; use tendril::StrTendril; +use tokenizer::Attribute; +use tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText}; +use tree_builder; +use serialize::{Serializable, Serializer}; +use serialize::TraversalScope; +use serialize::TraversalScope::{IncludeNode, ChildrenOnly}; +use driver::ParseResult; + +pub use self::NodeEnum::{Document, Doctype, Text, Comment, Element}; + +/// The different kinds of nodes in the DOM. +#[derive(Debug)] +pub enum NodeEnum { + /// The `Document` itself. + Document, + + /// A `DOCTYPE` with name, public id, and system id. + Doctype(StrTendril, StrTendril, StrTendril), + + /// A text node. + Text(StrTendril), + + /// A comment. + Comment(StrTendril), + + /// An element with attributes. + Element(QualName, Vec), +} + /// A DOM node. pub struct Node { pub node: NodeEnum, @@ -249,7 +269,7 @@ impl TreeSink for RcDom { let parent = &mut child.borrow_mut().parent; *parent = Some(new_parent.downgrade()); } - new_children.append(children); + new_children.extend(mem::replace(children, Vec::new()).into_iter()); } fn mark_script_already_started(&mut self, node: Handle) { diff --git a/src/tokenizer/buffer_queue.rs b/src/tokenizer/buffer_queue.rs index d5a1dcb5..19a63bad 100644 --- a/src/tokenizer/buffer_queue.rs +++ b/src/tokenizer/buffer_queue.rs @@ -57,7 +57,7 @@ impl BufferQueue { /// Look at the next available character, if any. pub fn peek(&mut self) -> Option { // Invariant: all buffers in the queue are non-empty. - self.buffers.front().map(|b| b.char_at(0)) + self.buffers.front().map(|b| b.chars().next().unwrap()) } /// Get the next character, if one is available. diff --git a/src/tokenizer/char_ref/data.rs b/src/tokenizer/char_ref/data.rs index aa2a3624..da0f435b 100644 --- a/src/tokenizer/char_ref/data.rs +++ b/src/tokenizer/char_ref/data.rs @@ -23,6 +23,4 @@ pub static C1_REPLACEMENTS: [Option; 32] = [ Some('\u{0153}'), None, Some('\u{017e}'), Some('\u{0178}'), ]; -// The named_entities! macro is defined in html5/macros/named_entities.rs. -pub static NAMED_ENTITIES: Map<&'static str, [u32; 2]> - = named_entities!("../../../data/entities.json"); +include!(concat!(env!("OUT_DIR"), "/named_entities.rs")); diff --git a/src/tokenizer/char_ref/mod.rs b/src/tokenizer/char_ref/mod.rs index db1fc5f3..d1302065 100644 --- a/src/tokenizer/char_ref/mod.rs +++ b/src/tokenizer/char_ref/mod.rs @@ -57,7 +57,7 @@ pub struct CharRefTokenizer { hex_marker: Option, name_buf_opt: Option, - name_match: Option<&'static [u32; 2]>, + name_match: Option<(u32, u32)>, name_len: usize, } @@ -250,8 +250,8 @@ impl CharRefTokenizer { self.name_buf_mut().push_char(c); match data::NAMED_ENTITIES.get(&self.name_buf()[..]) { // We have either a full match or a prefix of one. - Some(m) => { - if m[0] != 0 { + Some(&m) => { + if m.0 != 0 { // We have a full match, but there might be a longer one to come. self.name_match = Some(m); self.name_len = self.name_buf().len(); @@ -299,7 +299,7 @@ impl CharRefTokenizer { self.finish_none() } - Some(&[c1, c2]) => { + Some((c1, c2)) => { // We have a complete match, but we may have consumed // additional characters into self.name_buf. Usually // at least one, but several in cases like @@ -310,14 +310,14 @@ impl CharRefTokenizer { let name_len = self.name_len; assert!(name_len > 0); - let last_matched = self.name_buf().char_at(name_len-1); + let last_matched = self.name_buf()[name_len-1..].chars().next().unwrap(); // There might not be a next character after the match, if // we had a full match and then hit EOF. let next_after = if name_len == self.name_buf().len() { None } else { - Some(self.name_buf().char_at(name_len)) + Some(self.name_buf()[name_len..].chars().next().unwrap()) }; // "If the character reference is being consumed as part of an diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index ca34049b..1ac6fc13 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -213,7 +213,7 @@ impl Tokenizer { return; } - if self.discard_bom && input.char_at(0) == '\u{feff}' { + if self.discard_bom && input.starts_with("\u{feff}") { input.pop_front(3); }; @@ -488,7 +488,7 @@ impl Tokenizer { fn consume_char_ref(&mut self, addnl_allowed: Option) { // NB: The char ref tokenizer assumes we have an additional allowed // character iff we're tokenizing in an attribute value. - self.char_ref_tokenizer = Some(box CharRefTokenizer::new(addnl_allowed)); + self.char_ref_tokenizer = Some(Box::new(CharRefTokenizer::new(addnl_allowed))); } fn emit_eof(&mut self) { @@ -1235,7 +1235,7 @@ impl Tokenizer { = self.state_profile.iter().map(|(s, t)| (*s, *t)).collect(); results.sort_by(|&(_, x), &(_, y)| y.cmp(&x)); - let total: u64 = results.iter().map(|&(_, t)| t).sum(); + let total: u64 = results.iter().map(|&(_, t)| t).fold(0, ::std::ops::Add::add); println!("\nTokenizer profile, in nanoseconds"); println!("\n{:12} total in token sink", self.time_in_sink); println!("\n{:12} total in tokenizer", total); diff --git a/src/tree_builder/mod.rs b/src/tree_builder/mod.rs index f0b75f29..060774a0 100644 --- a/src/tree_builder/mod.rs +++ b/src/tree_builder/mod.rs @@ -40,7 +40,7 @@ pub mod interface; mod data; mod types; mod actions; -mod rules; +#[path = "rules.expanded.rs"] mod rules; /// Tree builder options, with an impl for Default. #[derive(Copy, Clone)] @@ -415,7 +415,7 @@ impl TokenSink tokenizer::EOFToken => EOFToken, tokenizer::CharacterTokens(mut x) => { - if !x.is_empty() && ignore_lf && x.char_at(0) == '\n' { + if ignore_lf && x.starts_with("\n") { x.pop_front(1); } if x.is_empty() { diff --git a/src/tree_builder/rules.expanded.rs b/src/tree_builder/rules.expanded.rs new file mode 100644 index 00000000..0d891794 --- /dev/null +++ b/src/tree_builder/rules.expanded.rs @@ -0,0 +1,2497 @@ +// Copyright 2014 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. + +// This file is generated from rules.rs +// source SipHash: 11824921987729494326 + +# ! [ +doc = + "//! The tree builder rules, as a single, enormous nested match expression." +] use tree_builder::types::*; use tree_builder::tag_sets::*; +use tree_builder::actions::TreeBuilderActions; +use tree_builder::interface::{TreeSink, Quirks, AppendNode, NextParserState}; +use tokenizer::{Tag, StartTag, EndTag}; +use tokenizer::states::{Rcdata, Rawtext, ScriptData, Plaintext, Quiescent}; +use util::str::is_ascii_whitespace; use std::ascii::AsciiExt; +use std::mem::replace; use std::borrow::Cow::Borrowed; +use std::borrow::ToOwned; use tendril::{StrTendril, SliceExt}; +fn any_not_whitespace(x: &StrTendril) -> bool { + x.chars().any(|c| !is_ascii_whitespace(c)) +} +pub trait TreeBuilderStep { + fn step(&mut self, mode: InsertionMode, token: Token) + -> ProcessResult; + fn step_foreign(&mut self, token: Token) + -> ProcessResult; +} +#[doc(hidden)] +impl TreeBuilderStep for super::TreeBuilder where + Handle: Clone, Sink: TreeSink { + fn step(&mut self, mode: InsertionMode, token: Token) -> ProcessResult { + self.debug_step(mode, &token); + match mode { + Initial => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, _) => Done, + CommentToken(text) => self.append_comment_to_doc(text), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => { + if !self.opts.iframe_srcdoc { + self.unexpected(&token); + self.set_quirks_mode(Quirks); + } + Reprocess(BeforeHtml, token) + } + } + } + }, + BeforeHtml => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, _) => Done, + CommentToken(text) => self.append_comment_to_doc(text), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => { + self.create_root(tag.attrs); + self.mode = BeforeHead; + Done + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(head), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), + .. }) => false, + _ => true, + }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => + self.unexpected(&tag), + (_, token) => { + self.create_root(vec!()); + Reprocess(BeforeHead, token) + } + } + } + }, + BeforeHead => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, _) => Done, + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(head), .. }) => { + self.head_elem = Some(self.insert_element_for(tag)); + self.mode = InHead; + Done + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(head), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), + .. }) => false, + _ => true, + }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => + self.unexpected(&tag), + (_, token) => { + self.head_elem = + Some(self.insert_phantom(atom!(head))); + Reprocess(InHead, token) + } + } + } + }, + InHead => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, text) => self.append_text(text), + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(base), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(basefont), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(bgsound), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(link), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(meta), .. }) => { + self.insert_and_pop_element_for(tag); + DoneAckSelfClosing + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(title), .. }) => { + self.parse_raw_data(tag, Rcdata); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noframes), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(style), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noscript), .. }) + => { + if (!self.opts.scripting_enabled) && + (tag.name == atom!(noscript)) { + self.insert_element_for(tag); + self.mode = InHeadNoscript; + } else { self.parse_raw_data(tag, Rawtext); } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(script), .. }) => + { + let elem = + self.sink.create_element(qualname!(HTML , script), + tag.attrs); + if self.is_fragment() { + self.sink.mark_script_already_started(elem.clone()); + } + self.insert_appropriately(AppendNode(elem.clone()), None); + self.open_elems.push(elem); + self.to_raw_text_mode(ScriptData); + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(head), .. }) => { + self.pop(); + self.mode = AfterHead; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) + => { + self.insert_element_for(tag); + self.active_formatting.push(Marker); + self.frameset_ok = false; + self.mode = InTemplate; + self.template_modes.push(InTemplate); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => { + if !self.in_scope_named(default_scope, atom!(template)) { + self.unexpected(&tag); + } else { + self.generate_implied_end(thorough_implied_end); + self.expect_to_close(atom!(template)); + self.clear_active_formatting_to_marker(); + self.template_modes.pop(); + self.mode = self.reset_insertion_mode(); + } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(head), .. }) => + self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), + .. }) => false, + _ => true, + }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => + self.unexpected(&tag), + (_, token) => { + self.pop(); + Reprocess(AfterHead, token) + } + } + } + }, + InHeadNoscript => + match token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(noscript), .. }) + => { + self.pop(); + self.mode = InHead; + Done + } + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, _) => self.step(InHead, token), + CommentToken(_) => self.step(InHead, token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(basefont), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(bgsound), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(link), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(meta), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noframes), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(style), .. }) => + self.step(InHead, token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(head), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noscript), .. }) + => self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), + .. }) => false, + _ => true, + }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => + self.unexpected(&tag), + (_, token) => { + self.unexpected(&token); + self.pop(); + Reprocess(InHead, token) + } + } + } + }, + AfterHead => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, text) => self.append_text(text), + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(body), .. }) => { + self.insert_element_for(tag); + self.frameset_ok = false; + self.mode = InBody; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(frameset), .. }) + => { + self.insert_element_for(tag); + self.mode = InFrameset; + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(base), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(basefont), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(bgsound), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(link), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(meta), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noframes), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(script), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(style), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(title), .. }) => { + self.unexpected(&token); + let head = + self.head_elem.as_ref().expect("no head element").clone(); + self.push(&head); + let result = self.step(InHead, token); + self.remove_from_stack(&head); + result + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => self.step(InHead, token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(head), .. }) => + self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), + .. }) => false, + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), + .. }) => false, + _ => true, + }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => + self.unexpected(&tag), + (_, token) => { + self.insert_phantom(atom!(body)); + Reprocess(InBody, token) + } + } + } + }, + InBody => + match token { + NullCharacterToken => self.unexpected(&token), + CharacterTokens(_, text) => { + self.reconstruct_formatting(); + if any_not_whitespace(&text) { self.frameset_ok = false; } + self.append_text(text) + } + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => { + self.unexpected(&tag); + let top = self.html_elem(); + self.sink.add_attrs_if_missing(top, tag.attrs); + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(base), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(basefont), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(bgsound), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(link), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(meta), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noframes), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(script), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(style), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(title), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => { + self.step(InHead, token) + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(body), .. }) => { + self.unexpected(&tag); + match self.body_elem() { + None => (), + Some(node) => { + self.frameset_ok = false; + self.sink.add_attrs_if_missing(node, tag.attrs) + } + } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(frameset), .. }) + => { + self.unexpected(&tag); + if !self.frameset_ok { return Done; } + let body = + match self.body_elem() { + None => return Done, + Some(x) => x, + }; + self.sink.remove_from_parent(body); + self.open_elems.truncate(1); + self.insert_element_for(tag); + self.mode = InFrameset; + Done + } + EOFToken => { self.check_body_end(); self.stop_parsing() } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) => { + if self.in_scope_named(default_scope, atom!(body)) { + self.check_body_end(); + self.mode = AfterBody; + } else { + self.sink.parse_error(Borrowed(" with no in scope")); + } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) => { + if self.in_scope_named(default_scope, atom!(body)) { + self.check_body_end(); + Reprocess(AfterBody, token) + } else { + self.sink.parse_error(Borrowed(" with no in scope")); + Done + } + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(address), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(article), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(aside), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(blockquote), .. }) + | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(center), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(details), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(dialog), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(dir), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(div), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(dl), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(fieldset), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(figcaption), .. }) + | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(figure), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(footer), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(header), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(hgroup), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(main), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(menu), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(nav), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(ol), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(p), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(section), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(summary), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(ul), .. }) => { + self.close_p_element_in_button_scope(); + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h1), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h2), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h3), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h4), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h5), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(h6), .. }) => { + self.close_p_element_in_button_scope(); + if self.current_node_in(heading_tag) { + self.sink.parse_error(Borrowed("nested heading tags")); + self.pop(); + } + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(pre), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(listing), .. }) => + { + self.close_p_element_in_button_scope(); + self.insert_element_for(tag); + self.ignore_lf = true; + self.frameset_ok = false; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(form), .. }) => { + if self.form_elem.is_some() { + self.sink.parse_error(Borrowed("nested forms")); + } else { + self.close_p_element_in_button_scope(); + let elem = self.insert_element_for(tag); + self.form_elem = Some(elem); + } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(li), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(dd), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(dt), .. }) => { + declare_tag_set!(close_list = li); + declare_tag_set!(close_defn = dd dt); + declare_tag_set!(extra_special = special_tag - address div + p); + let can_close: fn(::string_cache::QualName) -> bool = + match tag.name { + atom!(li) => close_list, + atom!(dd) | atom!(dt) => close_defn, + _ => unreachable!(), + }; + self.frameset_ok = false; + let mut to_close = None; + for node in self.open_elems.iter().rev() { + let name = self.sink.elem_name(node.clone()); + if can_close(name.clone()) { + to_close = Some(name.local); + break ; + } + if extra_special(name.clone()) { break ; } + } + match to_close { + Some(name) => { + self.generate_implied_end_except(name.clone()); + self.expect_to_close(name); + } + None => (), + } + self.close_p_element_in_button_scope(); + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(plaintext), .. }) + => { + self.close_p_element_in_button_scope(); + self.insert_element_for(tag); + self.next_tokenizer_state = Some(Plaintext); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(button), .. }) => + { + if self.in_scope_named(default_scope, atom!(button)) { + self.sink.parse_error(Borrowed("nested buttons")); + self.generate_implied_end(cursory_implied_end); + self.pop_until_named(atom!(button)); + } + self.reconstruct_formatting(); + self.insert_element_for(tag); + self.frameset_ok = false; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(address), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(article), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(aside), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(blockquote), .. }) + | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(button), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(center), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(details), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(dialog), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(dir), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(div), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(dl), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(fieldset), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(figcaption), .. }) + | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(figure), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(footer), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(header), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(hgroup), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(listing), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(main), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(menu), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(nav), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(ol), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(pre), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(section), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(summary), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(ul), .. }) => { + if !self.in_scope_named(default_scope, tag.name.clone()) { + self.unexpected(&tag); + } else { + self.generate_implied_end(cursory_implied_end); + self.expect_to_close(tag.name); + } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(form), .. }) => { + let node = + match self.form_elem.take() { + None => { + self.sink.parse_error(Borrowed("Null form element pointer on ")); + return Done; + } + Some(x) => x, + }; + if !self.in_scope(default_scope, + |n| + self.sink.same_node(node.clone(), + n)) { + self.sink.parse_error(Borrowed("Form element not in scope on ")); + return Done; + } + self.generate_implied_end(cursory_implied_end); + let current = self.current_node(); + self.remove_from_stack(&node); + if !self.sink.same_node(current, node) { + self.sink.parse_error(Borrowed("Bad open element on ")); + } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(p), .. }) => { + if !self.in_scope_named(button_scope, atom!(p)) { + self.sink.parse_error(Borrowed("No

tag to close")); + self.insert_phantom(atom!(p)); + } + self.close_p_element(); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(li), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(dd), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(dt), .. }) => { + let scope: fn(::string_cache::QualName) -> bool = + match tag.name { + atom!(li) => list_item_scope, + _ => default_scope, + }; + if self.in_scope_named(|x| scope(x), tag.name.clone()) { + self.generate_implied_end_except(tag.name.clone()); + self.expect_to_close(tag.name); + } else { + self.sink.parse_error(Borrowed("No matching tag to close")); + } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h1), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h2), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h3), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h4), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h5), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(h6), .. }) => { + if self.in_scope(default_scope, + |n| self.elem_in(n.clone(), heading_tag)) + { + self.generate_implied_end(cursory_implied_end); + if !self.current_node_named(tag.name) { + self.sink.parse_error(Borrowed("Closing wrong heading tag")); + } + self.pop_until(heading_tag); + } else { + self.sink.parse_error(Borrowed("No heading tag to close")); + } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(a), .. }) => { + self.handle_misnested_a_tags(&tag); + self.reconstruct_formatting(); + self.create_formatting_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(b), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(big), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(code), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(em), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(font), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(i), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(s), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(small), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(strike), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(strong), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tt), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(u), .. }) => { + self.reconstruct_formatting(); + self.create_formatting_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(nobr), .. }) => { + self.reconstruct_formatting(); + if self.in_scope_named(default_scope, atom!(nobr)) { + self.sink.parse_error(Borrowed("Nested ")); + self.adoption_agency(atom!(nobr)); + self.reconstruct_formatting(); + } + self.create_formatting_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(a), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(b), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(big), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(code), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(em), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(font), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(i), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(nobr), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(s), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(small), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(strike), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(strong), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tt), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(u), .. }) => { + self.adoption_agency(tag.name); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(applet), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(marquee), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(object), .. }) => + { + self.reconstruct_formatting(); + self.insert_element_for(tag); + self.active_formatting.push(Marker); + self.frameset_ok = false; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(applet), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(marquee), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(object), .. }) => + { + if !self.in_scope_named(default_scope, tag.name.clone()) { + self.unexpected(&tag); + } else { + self.generate_implied_end(cursory_implied_end); + self.expect_to_close(tag.name); + self.clear_active_formatting_to_marker(); + } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(table), .. }) => { + if self.quirks_mode != Quirks { + self.close_p_element_in_button_scope(); + } + self.insert_element_for(tag); + self.frameset_ok = false; + self.mode = InTable; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(br), .. }) => { + self.unexpected(&tag); + self.step(InBody, + TagToken(Tag{kind: StartTag, + attrs: vec!(), ..tag})) + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(area), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(br), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(embed), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(img), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(keygen), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(wbr), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(input), .. }) => { + let keep_frameset_ok = + match tag.name { + atom!(input) => self.is_type_hidden(&tag), + _ => false, + }; + self.reconstruct_formatting(); + self.insert_and_pop_element_for(tag); + if !keep_frameset_ok { self.frameset_ok = false; } + DoneAckSelfClosing + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(menuitem), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(param), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(source), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(track), .. }) => { + self.insert_and_pop_element_for(tag); + DoneAckSelfClosing + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(hr), .. }) => { + self.close_p_element_in_button_scope(); + self.insert_and_pop_element_for(tag); + self.frameset_ok = false; + DoneAckSelfClosing + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(image), .. }) => { + self.unexpected(&tag); + self.step(InBody, TagToken(Tag{name: atom!(img), ..tag})) + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(isindex), .. }) => + panic!("FIXME: not implemented"), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(textarea), .. }) + => { + self.ignore_lf = true; + self.frameset_ok = false; + self.parse_raw_data(tag, Rcdata); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(xmp), .. }) => { + self.close_p_element_in_button_scope(); + self.reconstruct_formatting(); + self.frameset_ok = false; + self.parse_raw_data(tag, Rawtext); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(iframe), .. }) => + { + self.frameset_ok = false; + self.parse_raw_data(tag, Rawtext); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(noembed), .. }) => + { + self.parse_raw_data(tag, Rawtext); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(select), .. }) => + { + self.reconstruct_formatting(); + self.insert_element_for(tag); + self.frameset_ok = false; + self.mode = + match self.mode { + InTable | InCaption | InTableBody | InRow | InCell + => InSelectInTable, + _ => InSelect, + }; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(optgroup), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(option), .. }) => + { + if self.current_node_named(atom!(option)) { self.pop(); } + self.reconstruct_formatting(); + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(rp), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(rt), .. }) => { + if self.in_scope_named(default_scope, atom!(ruby)) { + self.generate_implied_end(cursory_implied_end); + } + if !self.current_node_named(atom!(ruby)) { + self.unexpected(&tag); + } + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(math), .. }) => + self.enter_foreign(tag, ns!(MathML)), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(svg), .. }) => + self.enter_foreign(tag, ns!(SVG)), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(frame), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(head), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) => { + self.unexpected(&token); + Done + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + .. })) => { + if self.opts.scripting_enabled && + tag.name == atom!(noscript) { + self.parse_raw_data(tag, Rawtext); + } else { + self.reconstruct_formatting(); + self.insert_element_for(tag); + } + Done + } + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => { + self.process_end_tag_in_body(tag); + Done + } + (_, _) => panic!("impossible case in InBody mode"), + } + } + }, + Text => + match token { + CharacterTokens(_, text) => self.append_text(text), + EOFToken => { + self.unexpected(&token); + if self.current_node_named(atom!(script)) { + let current = self.current_node(); + self.sink.mark_script_already_started(current); + } + self.pop(); + Reprocess(self.orig_mode.take().unwrap(), token) + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (true, + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + .. })) => { + let node = self.pop(); + if tag.name == atom!(script) { + warn!("FIXME: not fully implemented"); + if self.sink.complete_script(node) == + NextParserState::Suspend { + self.next_tokenizer_state = + Some(Quiescent); + } + } + self.mode = self.orig_mode.take().unwrap(); + Done + } + (_, _) => panic!("impossible case in Text mode"), + } + } + }, + InTable => + match token { + NullCharacterToken => self.process_chars_in_table(token), + CharacterTokens(..) => self.process_chars_in_table(token), + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) => + { + self.pop_until_current(table_scope); + self.active_formatting.push(Marker); + self.insert_element_for(tag); + self.mode = InCaption; + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) + => { + self.pop_until_current(table_scope); + self.insert_element_for(tag); + self.mode = InColumnGroup; + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) => { + self.pop_until_current(table_scope); + self.insert_phantom(atom!(colgroup)); + Reprocess(InColumnGroup, token) + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) => { + self.pop_until_current(table_scope); + self.insert_element_for(tag); + self.mode = InTableBody; + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) => { + self.pop_until_current(table_scope); + self.insert_phantom(atom!(tbody)); + Reprocess(InTableBody, token) + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(table), .. }) => { + self.unexpected(&token); + if self.in_scope_named(table_scope, atom!(table)) { + self.pop_until_named(atom!(table)); + Reprocess(self.reset_insertion_mode(), token) + } else { Done } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) => { + if self.in_scope_named(table_scope, atom!(table)) { + self.pop_until_named(atom!(table)); + self.mode = self.reset_insertion_mode(); + } else { self.unexpected(&token); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) => + self.unexpected(&token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(style), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(script), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => self.step(InHead, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(input), .. }) => { + self.unexpected(&tag); + if self.is_type_hidden(&tag) { + self.insert_and_pop_element_for(tag); + DoneAckSelfClosing + } else { self.foster_parent_in_body(TagToken(tag)) } + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(form), .. }) => { + self.unexpected(&tag); + if self.form_elem.is_none() { + self.form_elem = + Some(self.insert_and_pop_element_for(tag)); + } + Done + } + EOFToken => self.step(InBody, token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => { + self.unexpected(&token); + self.foster_parent_in_body(token) + } + } + } + }, + InTableText => + match token { + NullCharacterToken => self.unexpected(&token), + CharacterTokens(split, text) => { + self.pending_table_text.push((split, text)); + Done + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => { + let pending = + replace(&mut self.pending_table_text, vec!()); + let contains_nonspace = + pending.iter().any(|&(split, ref text)| { + match split { + Whitespace => false, + NotWhitespace => true, + NotSplit => + any_not_whitespace(text), + } }); + if contains_nonspace { + self.sink.parse_error(Borrowed("Non-space table text")); + for (split, text) in pending.into_iter() { + match self.foster_parent_in_body(CharacterTokens(split, + text)) + { + Done => (), + _ => + panic!("not prepared to handle this!"), + } + } + } else { + for (_, text) in pending.into_iter() { + self.append_text(text); + } + } + Reprocess(self.orig_mode.take().unwrap(), token) + } + } + } + }, + InCaption => + match token { + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) => + { + if self.in_scope_named(table_scope, atom!(caption)) { + self.generate_implied_end(cursory_implied_end); + self.expect_to_close(atom!(caption)); + self.clear_active_formatting_to_marker(); + match tag { + Tag { kind: EndTag, name: atom!(caption), .. } => + { + self.mode = InTable; + Done + } + _ => Reprocess(InTable, TagToken(tag)), + } + } else { self.unexpected(&tag); Done } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) => + self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.step(InBody, token), + } + } + }, + InColumnGroup => + match token { + CharacterTokens(NotSplit, text) => SplitWhitespace(text), + CharacterTokens(Whitespace, text) => self.append_text(text), + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) => { + self.insert_and_pop_element_for(tag); + DoneAckSelfClosing + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) + => { + if self.current_node_named(atom!(colgroup)) { + self.pop(); + self.mode = InTable; + } else { self.unexpected(&token); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) => + self.unexpected(&token), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => self.step(InHead, token), + EOFToken => self.step(InBody, token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => { + if self.current_node_named(atom!(colgroup)) { + self.pop(); + Reprocess(InTable, token) + } else { self.unexpected(&token) } + } + } + } + }, + InTableBody => + match token { + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) => { + self.pop_until_current(table_body_context); + self.insert_element_for(tag); + self.mode = InRow; + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) => { + self.unexpected(&token); + self.pop_until_current(table_body_context); + self.insert_phantom(atom!(tr)); + Reprocess(InRow, token) + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) => { + if self.in_scope_named(table_scope, tag.name.clone()) { + self.pop_until_current(table_body_context); + self.pop(); + self.mode = InTable; + } else { self.unexpected(&tag); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) => { + declare_tag_set!(table_outer = table tbody tfoot); + if self.in_scope(table_scope, + |e| self.elem_in(e, table_outer)) { + self.pop_until_current(table_body_context); + self.pop(); + Reprocess(InTable, token) + } else { self.unexpected(&token) } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) => + self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.step(InTable, token), + } + } + }, + InRow => + match token { + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) => { + self.pop_until_current(table_row_context); + self.insert_element_for(tag); + self.mode = InCell; + self.active_formatting.push(Marker); + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) => { + if self.in_scope_named(table_scope, atom!(tr)) { + self.pop_until_current(table_row_context); + let node = self.pop(); + self.assert_named(node, atom!(tr)); + self.mode = InTableBody; + } else { self.unexpected(&token); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) => { + if self.in_scope_named(table_scope, atom!(tr)) { + self.pop_until_current(table_row_context); + let node = self.pop(); + self.assert_named(node, atom!(tr)); + Reprocess(InTableBody, token) + } else { self.unexpected(&token) } + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) => { + if self.in_scope_named(table_scope, tag.name.clone()) { + if self.in_scope_named(table_scope, atom!(tr)) { + self.pop_until_current(table_row_context); + let node = self.pop(); + self.assert_named(node, atom!(tr)); + Reprocess(InTableBody, TagToken(tag)) + } else { Done } + } else { self.unexpected(&tag) } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) => + self.unexpected(&token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.step(InTable, token), + } + } + }, + InCell => + match token { + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) => { + if self.in_scope_named(table_scope, tag.name.clone()) { + self.generate_implied_end(cursory_implied_end); + self.expect_to_close(tag.name); + self.clear_active_formatting_to_marker(); + self.mode = InRow; + } else { self.unexpected(&tag); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) => { + if self.in_scope(table_scope, + |n| self.elem_in(n.clone(), td_th)) { + self.close_the_cell(); + Reprocess(InRow, token) + } else { self.unexpected(&token) } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(body), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(col), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(colgroup), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(html), .. }) => + self.unexpected(&token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) => { + if self.in_scope_named(table_scope, tag.name.clone()) { + self.close_the_cell(); + Reprocess(InRow, TagToken(tag)) + } else { self.unexpected(&tag) } + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.step(InBody, token), + } + } + }, + InSelect => + match token { + NullCharacterToken => self.unexpected(&token), + CharacterTokens(_, text) => self.append_text(text), + CommentToken(text) => self.append_comment(text), + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(html), .. }) => + self.step(InBody, token), + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(option), .. }) => + { + if self.current_node_named(atom!(option)) { self.pop(); } + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(optgroup), .. }) + => { + if self.current_node_named(atom!(option)) { self.pop(); } + if self.current_node_named(atom!(optgroup)) { + self.pop(); + } + self.insert_element_for(tag); + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(optgroup), .. }) + => { + if self.open_elems.len() >= 2 && + self.current_node_named(atom!(option)) && + self.html_elem_named(self.open_elems[self.open_elems.len() + - + 2].clone(), + atom!(optgroup)) { + self.pop(); + } + if self.current_node_named(atom!(optgroup)) { + self.pop(); + } else { self.unexpected(&token); } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(option), .. }) => + { + if self.current_node_named(atom!(option)) { + self.pop(); + } else { self.unexpected(&token); } + Done + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(select), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(select), .. }) => + { + let in_scope = + self.in_scope_named(select_scope, atom!(select)); + if !in_scope || tag.kind == StartTag { + self.unexpected(&tag); + } + if in_scope { + self.pop_until_named(atom!(select)); + self.mode = self.reset_insertion_mode(); + } + Done + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(input), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(keygen), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(textarea), .. }) + => { + self.unexpected(&token); + if self.in_scope_named(select_scope, atom!(select)) { + self.pop_until_named(atom!(select)); + Reprocess(self.reset_insertion_mode(), token) + } else { Done } + } + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(script), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(template), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(template), .. }) + => self.step(InHead, token), + EOFToken => self.step(InBody, token), + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.unexpected(&token), + } + } + }, + InSelectInTable => + match token { + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(table), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(tr), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(::tokenizer::Tag { + kind: ::tokenizer::StartTag, + name: atom!(th), .. }) => { + self.unexpected(&token); + self.pop_until_named(atom!(select)); + Reprocess(self.reset_insertion_mode(), token) + } + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(caption), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(table), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tbody), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tfoot), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(thead), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(tr), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(td), .. }) | + ::tree_builder::types::TagToken(tag@::tokenizer::Tag { + kind: ::tokenizer::EndTag, + name: atom!(th), .. }) => { + self.unexpected(&tag); + if self.in_scope_named(table_scope, tag.name.clone()) { + self.pop_until_named(atom!(select)); + Reprocess(self.reset_insertion_mode(), TagToken(tag)) + } else { Done } + } + last_arm_token => { + let enable_wildcards = + match last_arm_token { _ => true, }; + match (enable_wildcards, last_arm_token) { + (_, token) => self.step(InSelect, token), + } + } + }, + InTemplate => { + if self.opts.ignore_missing_rules { + self.step(InBody, token) + } else { panic!("FIXME: