From 02312cba4ac032dd1c9b7c0cf4a35ae6f9dbf24d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 19 Jul 2015 11:38:16 +0200 Subject: [PATCH 1/8] Remove usage of some unstable features. --- macros/src/named_entities.rs | 17 ++++++++--------- src/lib.rs | 2 +- src/tokenizer/buffer_queue.rs | 2 +- src/tokenizer/char_ref/data.rs | 2 +- src/tokenizer/char_ref/mod.rs | 12 ++++++------ src/tokenizer/mod.rs | 6 +++--- src/tree_builder/mod.rs | 2 +- src/util/str.rs | 2 +- 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/macros/src/named_entities.rs b/macros/src/named_entities.rs index 3fd44b60..14b718a5 100644 --- a/macros/src/named_entities.rs +++ b/macros/src/named_entities.rs @@ -33,7 +33,7 @@ struct CharRef { } // Build the map from entity names (and their prefixes) to characters. -fn build_map(js: Json) -> Option> { +fn build_map(js: Json) -> Option> { let mut map = HashMap::new(); let json_map = match js { Json::Object(m) => m, @@ -47,10 +47,9 @@ fn build_map(js: Json) -> Option> { = 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; - } + let mut codepoints = codepoints.into_iter(); + let codepoint_pair = (codepoints.next().unwrap(), codepoints.next().unwrap_or(0)); + assert!(codepoints.next().is_none()); // Slice off the initial '&' assert!(k.chars().next() == Some('&')); @@ -58,13 +57,13 @@ fn build_map(js: Json) -> Option> { } // Add every missing prefix of those keys, mapping to NULL characters. - map.insert("".to_string(), [0, 0]); + 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]); + map.insert(pfx, (0, 0)); } } } @@ -111,9 +110,9 @@ pub fn expand(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box v, k => v, ...) - let toks: Vec<_> = map.into_iter().flat_map(|(k, [c0, c1])| { + let toks: Vec<_> = map.into_iter().flat_map(|(k, (c0, c1))| { let k = &k[..]; - (quote_tokens!(&mut *cx, $k => [$c0, $c1],)).into_iter() + (quote_tokens!(&mut *cx, $k => ($c0, $c1),)).into_iter() }).collect(); MacEager::expr(quote_expr!(&mut *cx, phf_map!($toks))) } diff --git a/src/lib.rs b/src/lib.rs index 924b9c68..85d29cfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ #![crate_name="html5ever"] #![crate_type="dylib"] -#![feature(plugin, box_syntax, str_char, slice_patterns, iter_arith, str_escape)] +#![feature(plugin)] #![cfg_attr(test, deny(warnings))] #![allow(unused_parens)] 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..fad5ae4e 100644 --- a/src/tokenizer/char_ref/data.rs +++ b/src/tokenizer/char_ref/data.rs @@ -24,5 +24,5 @@ pub static C1_REPLACEMENTS: [Option; 32] = [ ]; // The named_entities! macro is defined in html5/macros/named_entities.rs. -pub static NAMED_ENTITIES: Map<&'static str, [u32; 2]> +pub static NAMED_ENTITIES: Map<&'static str, (u32, u32)> = named_entities!("../../../data/entities.json"); 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..1807840a 100644 --- a/src/tree_builder/mod.rs +++ b/src/tree_builder/mod.rs @@ -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/util/str.rs b/src/util/str.rs index 3ce980b6..7b0adb5b 100644 --- a/src/util/str.rs +++ b/src/util/str.rs @@ -16,7 +16,7 @@ pub fn to_escaped_string(x: &T) -> String { let mut buf = String::new(); let _ = buf.write_fmt(format_args!("{:?}", x)); buf.shrink_to_fit(); - buf.escape_default() + buf.chars().flat_map(|c| c.escape_default()).collect() } /// If `c` is an ASCII letter, return the corresponding lowercase From dfc9d214d73ec688a81538640ddebfa58b5ec6bd Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 19 Jul 2015 12:10:25 +0200 Subject: [PATCH 2/8] Use phf_codegen instead of a plugin. --- Cargo.toml | 16 ++--- build.rs | 61 +++++++++++++++++ macros/Cargo.toml | 1 - macros/src/lib.rs | 3 - macros/src/named_entities.rs | 118 --------------------------------- src/lib.rs | 1 - src/tokenizer/char_ref/data.rs | 4 +- 7 files changed, 69 insertions(+), 135 deletions(-) create mode 100644 build.rs delete mode 100644 macros/src/named_entities.rs diff --git a/Cargo.toml b/Cargo.toml index 776f570c..89159dfa 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" @@ -18,22 +19,19 @@ doctest = false time = "0" log = "0" phf = "0.7" -phf_macros = "0.7" string_cache = "0" string_cache_plugin = "0" mac = "0" tendril = "0.1" - -[dependencies.html5ever_macros] -version = "0.1.0" -path = "macros" +html5ever_macros = { version = "0.1.0", path = "macros"} [dev-dependencies] -rustc-serialize = "0" +rustc-serialize = "0.3.15" +html5ever_test_util = { version = "0.1.0", path = "test_util" } -[dev-dependencies.html5ever_test_util] -version = "0.1.0" -path = "test_util" +[build-dependencies] +phf_codegen = "0.7.3" +rustc-serialize = "0.3.15" [profile.dev] debug = false diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..94c7146e --- /dev/null +++ b/build.rs @@ -0,0 +1,61 @@ +// 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() { + named_entities_to_phf( + &Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("data").join("entities.json"), + &Path::new(&env::var("OUT_DIR").unwrap()).join("named_entities.rs")) +} + + +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/macros/Cargo.toml b/macros/Cargo.toml index bf997cde..54c5676c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -13,5 +13,4 @@ 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..303ae7ae 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -15,7 +15,6 @@ extern crate syntax; extern crate rustc; -extern crate rustc_serialize; #[macro_use] extern crate mac; @@ -34,12 +33,10 @@ macro_rules! panictry { } // Make these public so that rustdoc will generate documentation for them. -pub mod named_entities; pub mod match_token; // 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); } diff --git a/macros/src/named_entities.rs b/macros/src/named_entities.rs deleted file mode 100644 index 14b718a5..00000000 --- a/macros/src/named_entities.rs +++ /dev/null @@ -1,118 +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. - -#![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 codepoints = codepoints.into_iter(); - let codepoint_pair = (codepoints.next().unwrap(), codepoints.next().unwrap_or(0)); - assert!(codepoints.next().is_none()); - - // 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/src/lib.rs b/src/lib.rs index 85d29cfa..b2375112 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,6 @@ #![cfg_attr(test, deny(warnings))] #![allow(unused_parens)] -#![plugin(phf_macros)] #![plugin(string_cache_plugin)] #![plugin(html5ever_macros)] diff --git a/src/tokenizer/char_ref/data.rs b/src/tokenizer/char_ref/data.rs index fad5ae4e..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, u32)> - = named_entities!("../../../data/entities.json"); +include!(concat!(env!("OUT_DIR"), "/named_entities.rs")); From beeec3ded02e70074fb60925743e11c54d7bd127 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 20 Jul 2015 18:54:40 +0200 Subject: [PATCH 3/8] Ship a copy of rules.rs with match_token! pre-expanded. --- macros/Cargo.toml | 5 +- macros/src/lib.rs | 11 +- macros/src/match_token.rs | 49 +- macros/src/pre_expand.rs | 107 ++ src/tree_builder/mod.rs | 2 +- src/tree_builder/rules.expanded.rs | 2485 ++++++++++++++++++++++++++++ 6 files changed, 2637 insertions(+), 22 deletions(-) create mode 100644 macros/src/pre_expand.rs create mode 100644 src/tree_builder/rules.expanded.rs diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 54c5676c..17931fdb 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -8,9 +8,12 @@ repository = "https://github.com/servo/html5ever" description = "High-performance browser-grade HTML5 parser − compiler plugins" [lib] - name = "html5ever_macros" plugin = true +[[bin]] +name = "html5ever_pre_expand" +path = "src/lib.rs" + [dependencies] mac = "0" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 303ae7ae..00119ea4 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -7,10 +7,7 @@ // 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(plugin_registrar, quote, rustc_private, rc_unique)] #![deny(warnings)] extern crate syntax; @@ -34,9 +31,15 @@ macro_rules! panictry { // Make these public so that rustdoc will generate documentation for them. 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("match_token", match_token::expand); } + +#[allow(dead_code)] // only used when building as a binary +fn main() { + pre_expand::pre_expand() +} diff --git a/macros/src/match_token.rs b/macros/src/match_token.rs index b5131cc3..bf7e4676 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 Box Box { + match expand_to_tokens(cx, span, toks) { + Ok(tokens) => MacEager::expr(quote_expr!(&mut *cx, $tokens)), + Err((span, message)) => ext_bail!(cx, span, message) + } +} diff --git a/macros/src/pre_expand.rs b/macros/src/pre_expand.rs new file mode 100644 index 00000000..7359ba14 --- /dev/null +++ b/macros/src/pre_expand.rs @@ -0,0 +1,107 @@ +// 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::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() { + let mut source = String::new(); + let path = Path::new(file!()).parent().unwrap().join("../../src/tree_builder/rules.rs"); + let mut file = File::open(&path).unwrap(); + file.read_to_string(&mut source).unwrap(); + + let sess = parse::ParseSess::new(); + let mut cx = ext::base::ExtCtxt::new(&sess, vec![], + ext::expand::ExpansionConfig::default("".to_owned())); + + let tts = parse::parse_tts_from_source_str("".to_owned(), 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); + let mut file = File::create(&path.with_extension("expanded.rs")).unwrap(); + file.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) +} diff --git a/src/tree_builder/mod.rs b/src/tree_builder/mod.rs index 1807840a..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)] diff --git a/src/tree_builder/rules.expanded.rs b/src/tree_builder/rules.expanded.rs new file mode 100644 index 00000000..98509979 --- /dev/null +++ b/src/tree_builder/rules.expanded.rs @@ -0,0 +1,2485 @@ +# ! [ +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: