1 // Copyright 2014 The html5ever Project Developers. See the 2 // COPYRIGHT file at the top-level directory of this distribution. 3 // 4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 7 // option. This file may not be copied, modified, or distributed 8 // except according to those terms. 9 10 //! Helpers for implementing the tree builder rules. 11 //! 12 //! Many of these are named within the spec, e.g. "reset the insertion 13 //! mode appropriately". 14 15 use tree_builder::types::*; 16 use tree_builder::tag_sets::*; 17 use tree_builder::interface::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText}; 18 use tree_builder::rules::TreeBuilderStep; 19 20 use tokenizer::{Attribute, Tag, StartTag, EndTag}; 21 use tokenizer::states::{RawData, RawKind}; 22 23 use util::str::to_escaped_string; 24 25 use std::ascii::AsciiExt; 26 use std::{slice, fmt}; 27 use std::mem::replace; 28 use std::iter::{Rev, Enumerate}; 29 use std::borrow::Cow::Borrowed; 30 31 use {LocalName, Namespace, QualName}; 32 use tendril::StrTendril; 33 34 pub use self::PushFlag::*; 35 36 pub struct ActiveFormattingIter<'a, Handle: 'a> { 37 iter: Rev<Enumerate<slice::Iter<'a, FormatEntry<Handle>>>>, 38 } 39 40 impl<'a, Handle> Iterator for ActiveFormattingIter<'a, Handle> { 41 type Item = (usize, &'a Handle, &'a Tag); next(&mut self) -> Option<(usize, &'a Handle, &'a Tag)>42 fn next(&mut self) -> Option<(usize, &'a Handle, &'a Tag)> { 43 match self.iter.next() { 44 None | Some((_, &Marker)) => None, 45 Some((i, &Element(ref h, ref t))) => Some((i, h, t)), 46 } 47 } 48 } 49 50 pub enum PushFlag { 51 Push, 52 NoPush, 53 } 54 55 enum Bookmark<Handle> { 56 Replace(Handle), 57 InsertAfter(Handle), 58 } 59 60 // These go in a trait so that we can control visibility. 61 pub trait TreeBuilderActions<Handle> { unexpected<T: fmt::Debug>(&mut self, thing: &T) -> ProcessResult<Handle>62 fn unexpected<T: fmt::Debug>(&mut self, thing: &T) -> ProcessResult<Handle>; assert_named(&mut self, node: Handle, name: LocalName)63 fn assert_named(&mut self, node: Handle, name: LocalName); clear_active_formatting_to_marker(&mut self)64 fn clear_active_formatting_to_marker(&mut self); create_formatting_element_for(&mut self, tag: Tag) -> Handle65 fn create_formatting_element_for(&mut self, tag: Tag) -> Handle; append_text(&mut self, text: StrTendril) -> ProcessResult<Handle>66 fn append_text(&mut self, text: StrTendril) -> ProcessResult<Handle>; append_comment(&mut self, text: StrTendril) -> ProcessResult<Handle>67 fn append_comment(&mut self, text: StrTendril) -> ProcessResult<Handle>; append_comment_to_doc(&mut self, text: StrTendril) -> ProcessResult<Handle>68 fn append_comment_to_doc(&mut self, text: StrTendril) -> ProcessResult<Handle>; append_comment_to_html(&mut self, text: StrTendril) -> ProcessResult<Handle>69 fn append_comment_to_html(&mut self, text: StrTendril) -> ProcessResult<Handle>; insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>)70 fn insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>); insert_phantom(&mut self, name: LocalName) -> Handle71 fn insert_phantom(&mut self, name: LocalName) -> Handle; insert_and_pop_element_for(&mut self, tag: Tag) -> Handle72 fn insert_and_pop_element_for(&mut self, tag: Tag) -> Handle; insert_element_for(&mut self, tag: Tag) -> Handle73 fn insert_element_for(&mut self, tag: Tag) -> Handle; insert_element(&mut self, push: PushFlag, ns: Namespace, name: LocalName, attrs: Vec<Attribute>) -> Handle74 fn insert_element(&mut self, push: PushFlag, ns: Namespace, name: LocalName, attrs: Vec<Attribute>) -> Handle; create_root(&mut self, attrs: Vec<Attribute>)75 fn create_root(&mut self, attrs: Vec<Attribute>); close_the_cell(&mut self)76 fn close_the_cell(&mut self); reset_insertion_mode(&mut self) -> InsertionMode77 fn reset_insertion_mode(&mut self) -> InsertionMode; process_chars_in_table(&mut self, token: Token) -> ProcessResult<Handle>78 fn process_chars_in_table(&mut self, token: Token) -> ProcessResult<Handle>; foster_parent_in_body(&mut self, token: Token) -> ProcessResult<Handle>79 fn foster_parent_in_body(&mut self, token: Token) -> ProcessResult<Handle>; is_type_hidden(&self, tag: &Tag) -> bool80 fn is_type_hidden(&self, tag: &Tag) -> bool; close_p_element_in_button_scope(&mut self)81 fn close_p_element_in_button_scope(&mut self); close_p_element(&mut self)82 fn close_p_element(&mut self); expect_to_close(&mut self, name: LocalName)83 fn expect_to_close(&mut self, name: LocalName); pop_until_named(&mut self, name: LocalName) -> usize84 fn pop_until_named(&mut self, name: LocalName) -> usize; pop_until<TagSet>(&mut self, pred: TagSet) -> usize where TagSet: Fn(QualName) -> bool85 fn pop_until<TagSet>(&mut self, pred: TagSet) -> usize where TagSet: Fn(QualName) -> bool; pop_until_current<TagSet>(&mut self, pred: TagSet) where TagSet: Fn(QualName) -> bool86 fn pop_until_current<TagSet>(&mut self, pred: TagSet) where TagSet: Fn(QualName) -> bool; generate_implied_end_except(&mut self, except: LocalName)87 fn generate_implied_end_except(&mut self, except: LocalName); generate_implied_end<TagSet>(&mut self, set: TagSet) where TagSet: Fn(QualName) -> bool88 fn generate_implied_end<TagSet>(&mut self, set: TagSet) where TagSet: Fn(QualName) -> bool; in_scope_named<TagSet>(&self, scope: TagSet, name: LocalName) -> bool where TagSet: Fn(QualName) -> bool89 fn in_scope_named<TagSet>(&self, scope: TagSet, name: LocalName) -> bool where TagSet: Fn(QualName) -> bool; current_node_named(&self, name: LocalName) -> bool90 fn current_node_named(&self, name: LocalName) -> bool; html_elem_named(&self, elem: Handle, name: LocalName) -> bool91 fn html_elem_named(&self, elem: Handle, name: LocalName) -> bool; in_html_elem_named(&self, name: LocalName) -> bool92 fn in_html_elem_named(&self, name: LocalName) -> bool; elem_in<TagSet>(&self, elem: Handle, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool93 fn elem_in<TagSet>(&self, elem: Handle, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool; in_scope<TagSet,Pred>(&self, scope: TagSet, pred: Pred) -> bool where TagSet: Fn(QualName) -> bool, Pred: Fn(Handle) -> bool94 fn in_scope<TagSet,Pred>(&self, scope: TagSet, pred: Pred) -> bool where TagSet: Fn(QualName) -> bool, Pred: Fn(Handle) -> bool; check_body_end(&mut self)95 fn check_body_end(&mut self); body_elem(&mut self) -> Option<Handle>96 fn body_elem(&mut self) -> Option<Handle>; html_elem(&self) -> Handle97 fn html_elem(&self) -> Handle; reconstruct_formatting(&mut self)98 fn reconstruct_formatting(&mut self); remove_from_stack(&mut self, elem: &Handle)99 fn remove_from_stack(&mut self, elem: &Handle); pop(&mut self) -> Handle100 fn pop(&mut self) -> Handle; push(&mut self, elem: &Handle)101 fn push(&mut self, elem: &Handle); adoption_agency(&mut self, subject: LocalName)102 fn adoption_agency(&mut self, subject: LocalName); current_node_in<TagSet>(&self, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool103 fn current_node_in<TagSet>(&self, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool; current_node(&self) -> Handle104 fn current_node(&self) -> Handle; adjusted_current_node(&self) -> Handle105 fn adjusted_current_node(&self) -> Handle; parse_raw_data(&mut self, tag: Tag, k: RawKind) -> ProcessResult<Handle>106 fn parse_raw_data(&mut self, tag: Tag, k: RawKind) -> ProcessResult<Handle>; to_raw_text_mode(&mut self, k: RawKind) -> ProcessResult<Handle>107 fn to_raw_text_mode(&mut self, k: RawKind) -> ProcessResult<Handle>; stop_parsing(&mut self) -> ProcessResult<Handle>108 fn stop_parsing(&mut self) -> ProcessResult<Handle>; set_quirks_mode(&mut self, mode: QuirksMode)109 fn set_quirks_mode(&mut self, mode: QuirksMode); active_formatting_end_to_marker<'a>(&'a self) -> ActiveFormattingIter<'a, Handle>110 fn active_formatting_end_to_marker<'a>(&'a self) -> ActiveFormattingIter<'a, Handle>; is_marker_or_open(&self, entry: &FormatEntry<Handle>) -> bool111 fn is_marker_or_open(&self, entry: &FormatEntry<Handle>) -> bool; position_in_active_formatting(&self, element: &Handle) -> Option<usize>112 fn position_in_active_formatting(&self, element: &Handle) -> Option<usize>; process_end_tag_in_body(&mut self, tag: Tag)113 fn process_end_tag_in_body(&mut self, tag: Tag); handle_misnested_a_tags(&mut self, tag: &Tag)114 fn handle_misnested_a_tags(&mut self, tag: &Tag); is_foreign(&mut self, token: &Token) -> bool115 fn is_foreign(&mut self, token: &Token) -> bool; enter_foreign(&mut self, tag: Tag, ns: Namespace) -> ProcessResult<Handle>116 fn enter_foreign(&mut self, tag: Tag, ns: Namespace) -> ProcessResult<Handle>; adjust_attributes<F>(&mut self, tag: &mut Tag, mut map: F) where F: FnMut(LocalName) -> Option<QualName>117 fn adjust_attributes<F>(&mut self, tag: &mut Tag, mut map: F) 118 where F: FnMut(LocalName) -> Option<QualName>; adjust_svg_tag_name(&mut self, tag: &mut Tag)119 fn adjust_svg_tag_name(&mut self, tag: &mut Tag); adjust_svg_attributes(&mut self, tag: &mut Tag)120 fn adjust_svg_attributes(&mut self, tag: &mut Tag); adjust_mathml_attributes(&mut self, tag: &mut Tag)121 fn adjust_mathml_attributes(&mut self, tag: &mut Tag); adjust_foreign_attributes(&mut self, tag: &mut Tag)122 fn adjust_foreign_attributes(&mut self, tag: &mut Tag); foreign_start_tag(&mut self, tag: Tag) -> ProcessResult<Handle>123 fn foreign_start_tag(&mut self, tag: Tag) -> ProcessResult<Handle>; unexpected_start_tag_in_foreign_content(&mut self, tag: Tag) -> ProcessResult<Handle>124 fn unexpected_start_tag_in_foreign_content(&mut self, tag: Tag) -> ProcessResult<Handle>; 125 } 126 127 #[doc(hidden)] 128 impl<Handle, Sink> TreeBuilderActions<Handle> 129 for super::TreeBuilder<Handle, Sink> 130 where Handle: Clone, 131 Sink: TreeSink<Handle=Handle>, 132 { unexpected<T: fmt::Debug>(&mut self, _thing: &T) -> ProcessResult<Handle>133 fn unexpected<T: fmt::Debug>(&mut self, _thing: &T) -> ProcessResult<Handle> { 134 self.sink.parse_error(format_if!( 135 self.opts.exact_errors, 136 "Unexpected token", 137 "Unexpected token {} in insertion mode {:?}", to_escaped_string(_thing), self.mode)); 138 Done 139 } 140 assert_named(&mut self, node: Handle, name: LocalName)141 fn assert_named(&mut self, node: Handle, name: LocalName) { 142 assert!(self.html_elem_named(node, name)); 143 } 144 145 /// Iterate over the active formatting elements (with index in the list) from the end 146 /// to the last marker, or the beginning if there are no markers. active_formatting_end_to_marker<'a>(&'a self) -> ActiveFormattingIter<'a, Handle>147 fn active_formatting_end_to_marker<'a>(&'a self) -> ActiveFormattingIter<'a, Handle> { 148 ActiveFormattingIter { 149 iter: self.active_formatting.iter().enumerate().rev(), 150 } 151 } 152 position_in_active_formatting(&self, element: &Handle) -> Option<usize>153 fn position_in_active_formatting(&self, element: &Handle) -> Option<usize> { 154 self.active_formatting 155 .iter() 156 .position(|n| { 157 match n { 158 &Marker => false, 159 &Element(ref handle, _) => self.sink.same_node(handle.clone(), element.clone()) 160 } 161 }) 162 } 163 set_quirks_mode(&mut self, mode: QuirksMode)164 fn set_quirks_mode(&mut self, mode: QuirksMode) { 165 self.quirks_mode = mode; 166 self.sink.set_quirks_mode(mode); 167 } 168 stop_parsing(&mut self) -> ProcessResult<Handle>169 fn stop_parsing(&mut self) -> ProcessResult<Handle> { 170 warn!("stop_parsing not implemented, full speed ahead!"); 171 Done 172 } 173 174 //§ parsing-elements-that-contain-only-text 175 // Switch to `Text` insertion mode, save the old mode, and 176 // switch the tokenizer to a raw-data state. 177 // The latter only takes effect after the current / next 178 // `process_token` of a start tag returns! to_raw_text_mode(&mut self, k: RawKind) -> ProcessResult<Handle>179 fn to_raw_text_mode(&mut self, k: RawKind) -> ProcessResult<Handle> { 180 self.orig_mode = Some(self.mode); 181 self.mode = Text; 182 ToRawData(k) 183 } 184 185 // The generic raw text / RCDATA parsing algorithm. parse_raw_data(&mut self, tag: Tag, k: RawKind) -> ProcessResult<Handle>186 fn parse_raw_data(&mut self, tag: Tag, k: RawKind) -> ProcessResult<Handle> { 187 self.insert_element_for(tag); 188 self.to_raw_text_mode(k) 189 } 190 //§ END 191 current_node(&self) -> Handle192 fn current_node(&self) -> Handle { 193 self.open_elems.last().expect("no current element").clone() 194 } 195 adjusted_current_node(&self) -> Handle196 fn adjusted_current_node(&self) -> Handle { 197 if self.open_elems.len() == 1 { 198 if let Some(ctx) = self.context_elem.as_ref() { 199 return ctx.clone(); 200 } 201 } 202 self.current_node() 203 } 204 current_node_in<TagSet>(&self, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool205 fn current_node_in<TagSet>(&self, set: TagSet) -> bool 206 where TagSet: Fn(QualName) -> bool 207 { 208 set(self.sink.elem_name(self.current_node())) 209 } 210 211 // Insert at the "appropriate place for inserting a node". insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>)212 fn insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>) { 213 declare_tag_set!(foster_target = "table" "tbody" "tfoot" "thead" "tr"); 214 let target = override_target.unwrap_or_else(|| self.current_node()); 215 if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) { 216 if self.html_elem_named(target.clone(), local_name!("template")) { 217 // No foster parenting (inside template). 218 let contents = self.sink.get_template_contents(target); 219 self.sink.append(contents, child); 220 } else { 221 // No foster parenting (the common case). 222 self.sink.append(target, child); 223 } 224 return; 225 } 226 227 // Foster parenting 228 let mut iter = self.open_elems.iter().rev().peekable(); 229 while let Some(elem) = iter.next() { 230 if self.html_elem_named(elem.clone(), local_name!("template")) { 231 let contents = self.sink.get_template_contents(elem.clone()); 232 self.sink.append(contents, child); 233 return; 234 } else if self.html_elem_named(elem.clone(), local_name!("table")) { 235 // Try inserting "inside last table's parent node, immediately before last table" 236 if let Err(child) = self.sink.append_before_sibling(elem.clone(), child) { 237 // If last_table has no parent, we regain ownership of the child. 238 // Insert "inside previous element, after its last child (if any)" 239 let previous_element = (*iter.peek().unwrap()).clone(); 240 self.sink.append(previous_element, child); 241 } 242 return; 243 } 244 } 245 let html_elem = self.html_elem(); 246 self.sink.append(html_elem, child); 247 } 248 adoption_agency(&mut self, subject: LocalName)249 fn adoption_agency(&mut self, subject: LocalName) { 250 // 1. 251 if self.current_node_named(subject.clone()) { 252 if self.position_in_active_formatting(&self.current_node()).is_none() { 253 self.pop(); 254 return; 255 } 256 } 257 258 // 2. 3. 4. 259 for _ in 0..8 { 260 // 5. 261 let (fmt_elem_index, fmt_elem, fmt_elem_tag) = unwrap_or_return!( 262 // We clone the Handle and Tag so they don't cause an immutable borrow of self. 263 self.active_formatting_end_to_marker() 264 .filter(|&(_, _, tag)| tag.name == subject) 265 .next() 266 .map(|(i, h, t)| (i, h.clone(), t.clone())), 267 268 { 269 self.process_end_tag_in_body(Tag { 270 kind: EndTag, 271 name: subject, 272 self_closing: false, 273 attrs: vec!(), 274 }); 275 } 276 ); 277 278 let fmt_elem_stack_index = unwrap_or_return!( 279 self.open_elems.iter() 280 .rposition(|n| self.sink.same_node(n.clone(), fmt_elem.clone())), 281 282 { 283 self.sink.parse_error(Borrowed("Formatting element not open")); 284 self.active_formatting.remove(fmt_elem_index); 285 } 286 ); 287 288 // 7. 289 if !self.in_scope(default_scope, |n| self.sink.same_node(n.clone(), fmt_elem.clone())) { 290 self.sink.parse_error(Borrowed("Formatting element not in scope")); 291 return; 292 } 293 294 // 8. 295 if !self.sink.same_node(self.current_node(), fmt_elem.clone()) { 296 self.sink.parse_error(Borrowed("Formatting element not current node")); 297 } 298 299 // 9. 300 let (furthest_block_index, furthest_block) = unwrap_or_return!( 301 self.open_elems.iter() 302 .enumerate() 303 .skip(fmt_elem_stack_index) 304 .filter(|&(_, open_element)| self.elem_in(open_element.clone(), special_tag)) 305 .next() 306 .map(|(i, h)| (i, h.clone())), 307 308 // 10. 309 { 310 self.open_elems.truncate(fmt_elem_stack_index); 311 self.active_formatting.remove(fmt_elem_index); 312 } 313 ); 314 315 // 11. 316 let common_ancestor = self.open_elems[fmt_elem_stack_index - 1].clone(); 317 318 // 12. 319 let mut bookmark = Bookmark::Replace(fmt_elem.clone()); 320 321 // 13. 322 let mut node; 323 let mut node_index = furthest_block_index; 324 let mut last_node = furthest_block.clone(); 325 326 // 13.1. 327 let mut inner_counter = 0; 328 loop { 329 // 13.2. 330 inner_counter += 1; 331 332 // 13.3. 333 node_index -= 1; 334 node = self.open_elems[node_index].clone(); 335 336 // 13.4. 337 if self.sink.same_node(node.clone(), fmt_elem.clone()) { 338 break; 339 } 340 341 // 13.5. 342 if inner_counter > 3 { 343 self.position_in_active_formatting(&node) 344 .map(|position| self.active_formatting.remove(position)); 345 self.open_elems.remove(node_index); 346 continue; 347 } 348 349 let node_formatting_index = unwrap_or_else!( 350 self.position_in_active_formatting(&node), 351 352 // 13.6. 353 { 354 self.open_elems.remove(node_index); 355 continue; 356 } 357 ); 358 359 // 13.7. 360 let tag = match self.active_formatting[node_formatting_index] { 361 Element(ref h, ref t) => { 362 assert!(self.sink.same_node(h.clone(), node.clone())); 363 t.clone() 364 } 365 Marker => panic!("Found marker during adoption agency"), 366 }; 367 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their 368 // own, once as part of t.clone() above)? 369 let new_element = self.sink.create_element( 370 QualName::new(ns!(html), tag.name.clone()), tag.attrs.clone()); 371 self.open_elems[node_index] = new_element.clone(); 372 self.active_formatting[node_formatting_index] = Element(new_element.clone(), tag); 373 node = new_element; 374 375 // 13.8. 376 if self.sink.same_node(last_node.clone(), furthest_block.clone()) { 377 bookmark = Bookmark::InsertAfter(node.clone()); 378 } 379 380 // 13.9. 381 self.sink.remove_from_parent(last_node.clone()); 382 self.sink.append(node.clone(), AppendNode(last_node.clone())); 383 384 // 13.10. 385 last_node = node.clone(); 386 387 // 13.11. 388 } 389 390 // 14. 391 self.sink.remove_from_parent(last_node.clone()); 392 self.insert_appropriately(AppendNode(last_node.clone()), Some(common_ancestor)); 393 394 // 15. 395 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their own, 396 // once as part of t.clone() above)? 397 let new_element = self.sink.create_element( 398 QualName::new(ns!(html), fmt_elem_tag.name.clone()), fmt_elem_tag.attrs.clone()); 399 let new_entry = Element(new_element.clone(), fmt_elem_tag); 400 401 // 16. 402 self.sink.reparent_children(furthest_block.clone(), new_element.clone()); 403 404 // 17. 405 self.sink.append(furthest_block.clone(), AppendNode(new_element.clone())); 406 407 // 18. 408 // FIXME: We could probably get rid of the position_in_active_formatting() calls here 409 // if we had a more clever Bookmark representation. 410 match bookmark { 411 Bookmark::Replace(to_replace) => { 412 let index = self.position_in_active_formatting(&to_replace) 413 .expect("bookmark not found in active formatting elements"); 414 self.active_formatting[index] = new_entry; 415 } 416 Bookmark::InsertAfter(previous) => { 417 let index = self.position_in_active_formatting(&previous) 418 .expect("bookmark not found in active formatting elements") + 1; 419 self.active_formatting.insert(index, new_entry); 420 let old_index = self.position_in_active_formatting(&fmt_elem) 421 .expect("formatting element not found in active formatting elements"); 422 self.active_formatting.remove(old_index); 423 } 424 } 425 426 // 19. 427 self.remove_from_stack(&fmt_elem); 428 let new_furthest_block_index = self.open_elems.iter() 429 .position(|n| self.sink.same_node(n.clone(), furthest_block.clone())) 430 .expect("furthest block missing from open element stack"); 431 self.open_elems.insert(new_furthest_block_index + 1, new_element); 432 433 // 20. 434 } 435 } 436 push(&mut self, elem: &Handle)437 fn push(&mut self, elem: &Handle) { 438 self.open_elems.push(elem.clone()); 439 } 440 pop(&mut self) -> Handle441 fn pop(&mut self) -> Handle { 442 let elem = self.open_elems.pop().expect("no current element"); 443 self.sink.pop(elem.clone()); 444 elem 445 } 446 remove_from_stack(&mut self, elem: &Handle)447 fn remove_from_stack(&mut self, elem: &Handle) { 448 let sink = &mut self.sink; 449 let position = self.open_elems 450 .iter() 451 .rposition(|x| sink.same_node(elem.clone(), x.clone())); 452 if let Some(position) = position { 453 self.open_elems.remove(position); 454 sink.pop(elem.clone()); 455 } 456 } 457 is_marker_or_open(&self, entry: &FormatEntry<Handle>) -> bool458 fn is_marker_or_open(&self, entry: &FormatEntry<Handle>) -> bool { 459 match *entry { 460 Marker => true, 461 Element(ref node, _) => { 462 self.open_elems.iter() 463 .rev() 464 .any(|n| self.sink.same_node(n.clone(), node.clone())) 465 } 466 } 467 } 468 469 /// Reconstruct the active formatting elements. reconstruct_formatting(&mut self)470 fn reconstruct_formatting(&mut self) { 471 { 472 let last = unwrap_or_return!(self.active_formatting.last(), ()); 473 if self.is_marker_or_open(last) { 474 return 475 } 476 } 477 478 let mut entry_index = self.active_formatting.len() - 1; 479 loop { 480 if entry_index == 0 { 481 break 482 } 483 entry_index -= 1; 484 if self.is_marker_or_open(&self.active_formatting[entry_index]) { 485 entry_index += 1; 486 break 487 } 488 } 489 490 loop { 491 let tag = match self.active_formatting[entry_index] { 492 Element(_, ref t) => t.clone(), 493 Marker => panic!("Found marker during formatting element reconstruction"), 494 }; 495 496 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their own, 497 // once as part of t.clone() above)? 498 let new_element = self.insert_element(Push, ns!(html), tag.name.clone(), 499 tag.attrs.clone()); 500 self.active_formatting[entry_index] = Element(new_element, tag); 501 if entry_index == self.active_formatting.len() - 1 { 502 break 503 } 504 entry_index += 1; 505 } 506 } 507 508 /// Get the first element on the stack, which will be the <html> element. html_elem(&self) -> Handle509 fn html_elem(&self) -> Handle { 510 self.open_elems[0].clone() 511 } 512 513 /// Get the second element on the stack, if it's a HTML body element. body_elem(&mut self) -> Option<Handle>514 fn body_elem(&mut self) -> Option<Handle> { 515 if self.open_elems.len() <= 1 { 516 return None; 517 } 518 519 let node = self.open_elems[1].clone(); 520 if self.html_elem_named(node.clone(), local_name!("body")) { 521 Some(node) 522 } else { 523 None 524 } 525 } 526 527 /// Signal an error depending on the state of the stack of open elements at 528 /// the end of the body. check_body_end(&mut self)529 fn check_body_end(&mut self) { 530 declare_tag_set!(body_end_ok = 531 "dd" "dt" "li" "optgroup" "option" "p" "rp" "rt" "tbody" "td" "tfoot" "th" 532 "thead" "tr" "body" "html"); 533 534 for elem in self.open_elems.iter() { 535 let name = self.sink.elem_name(elem.clone()); 536 if !body_end_ok(name.clone()) { 537 self.sink.parse_error(format_if!(self.opts.exact_errors, 538 "Unexpected open tag at end of body", 539 "Unexpected open tag {:?} at end of body", name)); 540 // FIXME: Do we keep checking after finding one bad tag? 541 // The spec suggests not. 542 return; 543 } 544 } 545 } 546 in_scope<TagSet,Pred>(&self, scope: TagSet, pred: Pred) -> bool where TagSet: Fn(QualName) -> bool, Pred: Fn(Handle) -> bool547 fn in_scope<TagSet,Pred>(&self, scope: TagSet, pred: Pred) -> bool 548 where TagSet: Fn(QualName) -> bool, Pred: Fn(Handle) -> bool 549 { 550 for node in self.open_elems.iter().rev() { 551 if pred(node.clone()) { 552 return true; 553 } 554 if scope(self.sink.elem_name(node.clone())) { 555 return false; 556 } 557 } 558 559 // supposed to be impossible, because <html> is always in scope 560 561 false 562 } 563 elem_in<TagSet>(&self, elem: Handle, set: TagSet) -> bool where TagSet: Fn(QualName) -> bool564 fn elem_in<TagSet>(&self, elem: Handle, set: TagSet) -> bool 565 where TagSet: Fn(QualName) -> bool 566 { 567 set(self.sink.elem_name(elem)) 568 } 569 html_elem_named(&self, elem: Handle, name: LocalName) -> bool570 fn html_elem_named(&self, elem: Handle, name: LocalName) -> bool { 571 self.sink.elem_name(elem) == QualName::new(ns!(html), name) 572 } 573 in_html_elem_named(&self, name: LocalName) -> bool574 fn in_html_elem_named(&self, name: LocalName) -> bool { 575 self.open_elems.iter().any(|elem| self.html_elem_named(elem.clone(), name.clone())) 576 } 577 current_node_named(&self, name: LocalName) -> bool578 fn current_node_named(&self, name: LocalName) -> bool { 579 self.html_elem_named(self.current_node(), name) 580 } 581 in_scope_named<TagSet>(&self, scope: TagSet, name: LocalName) -> bool where TagSet: Fn(QualName) -> bool582 fn in_scope_named<TagSet>(&self, scope: TagSet, name: LocalName) -> bool 583 where TagSet: Fn(QualName) -> bool 584 { 585 self.in_scope(scope, |elem| 586 self.html_elem_named(elem, name.clone())) 587 } 588 589 //§ closing-elements-that-have-implied-end-tags generate_implied_end<TagSet>(&mut self, set: TagSet) where TagSet: Fn(QualName) -> bool590 fn generate_implied_end<TagSet>(&mut self, set: TagSet) 591 where TagSet: Fn(QualName) -> bool 592 { 593 loop { 594 let elem = unwrap_or_return!(self.open_elems.last(), ()).clone(); 595 let nsname = self.sink.elem_name(elem); 596 if !set(nsname) { return; } 597 self.pop(); 598 } 599 } 600 generate_implied_end_except(&mut self, except: LocalName)601 fn generate_implied_end_except(&mut self, except: LocalName) { 602 self.generate_implied_end(|p| match p { 603 QualName { ns: ns!(html), ref local } if *local == except => false, 604 _ => cursory_implied_end(p), 605 }); 606 } 607 //§ END 608 609 // Pop elements until the current element is in the set. pop_until_current<TagSet>(&mut self, pred: TagSet) where TagSet: Fn(QualName) -> bool610 fn pop_until_current<TagSet>(&mut self, pred: TagSet) 611 where TagSet: Fn(QualName) -> bool 612 { 613 loop { 614 if self.current_node_in(|x| pred(x)) { 615 break; 616 } 617 self.open_elems.pop(); 618 } 619 } 620 621 // Pop elements until an element from the set has been popped. Returns the 622 // number of elements popped. pop_until<P>(&mut self, pred: P) -> usize where P: Fn(QualName) -> bool623 fn pop_until<P>(&mut self, pred: P) -> usize 624 where P: Fn(QualName) -> bool 625 { 626 let mut n = 0; 627 loop { 628 n += 1; 629 match self.open_elems.pop() { 630 None => break, 631 Some(elem) => if pred(self.sink.elem_name(elem)) { break; }, 632 } 633 } 634 n 635 } 636 pop_until_named(&mut self, name: LocalName) -> usize637 fn pop_until_named(&mut self, name: LocalName) -> usize { 638 self.pop_until(|p| p == QualName::new(ns!(html), name.clone())) 639 } 640 641 // Pop elements until one with the specified name has been popped. 642 // Signal an error if it was not the first one. expect_to_close(&mut self, name: LocalName)643 fn expect_to_close(&mut self, name: LocalName) { 644 if self.pop_until_named(name.clone()) != 1 { 645 self.sink.parse_error(format_if!(self.opts.exact_errors, 646 "Unexpected open element", 647 "Unexpected open element while closing {:?}", name)); 648 } 649 } 650 close_p_element(&mut self)651 fn close_p_element(&mut self) { 652 declare_tag_set!(implied = [cursory_implied_end] - "p"); 653 self.generate_implied_end(implied); 654 self.expect_to_close(local_name!("p")); 655 } 656 close_p_element_in_button_scope(&mut self)657 fn close_p_element_in_button_scope(&mut self) { 658 if self.in_scope_named(button_scope, local_name!("p")) { 659 self.close_p_element(); 660 } 661 } 662 663 // Check <input> tags for type=hidden is_type_hidden(&self, tag: &Tag) -> bool664 fn is_type_hidden(&self, tag: &Tag) -> bool { 665 match tag.attrs.iter().find(|&at| at.name == qualname!("", "type")) { 666 None => false, 667 Some(at) => (&*at.value).eq_ignore_ascii_case("hidden"), 668 } 669 } 670 foster_parent_in_body(&mut self, token: Token) -> ProcessResult<Handle>671 fn foster_parent_in_body(&mut self, token: Token) -> ProcessResult<Handle> { 672 warn!("foster parenting not implemented"); 673 self.foster_parenting = true; 674 let res = self.step(InBody, token); 675 // FIXME: what if res is Reprocess? 676 self.foster_parenting = false; 677 res 678 } 679 process_chars_in_table(&mut self, token: Token) -> ProcessResult<Handle>680 fn process_chars_in_table(&mut self, token: Token) -> ProcessResult<Handle> { 681 declare_tag_set!(table_outer = "table" "tbody" "tfoot" "thead" "tr"); 682 if self.current_node_in(table_outer) { 683 assert!(self.pending_table_text.is_empty()); 684 self.orig_mode = Some(self.mode); 685 Reprocess(InTableText, token) 686 } else { 687 self.sink.parse_error(format_if!(self.opts.exact_errors, 688 "Unexpected characters in table", 689 "Unexpected characters {} in table", to_escaped_string(&token))); 690 self.foster_parent_in_body(token) 691 } 692 } 693 694 // https://html.spec.whatwg.org/multipage/syntax.html#reset-the-insertion-mode-appropriately reset_insertion_mode(&mut self) -> InsertionMode695 fn reset_insertion_mode(&mut self) -> InsertionMode { 696 for (i, mut node) in self.open_elems.iter().enumerate().rev() { 697 let last = i == 0usize; 698 if let (true, Some(ctx)) = (last, self.context_elem.as_ref()) { 699 node = ctx; 700 } 701 let name = match self.sink.elem_name(node.clone()) { 702 QualName { ns: ns!(html), local } => local, 703 _ => continue, 704 }; 705 match name { 706 local_name!("select") => { 707 for ancestor in self.open_elems[0..i].iter().rev() { 708 if self.html_elem_named(ancestor.clone(), local_name!("template")) { 709 return InSelect; 710 } else if self.html_elem_named(ancestor.clone(), local_name!("table")) { 711 return InSelectInTable; 712 } 713 } 714 return InSelect; 715 }, 716 local_name!("td") | local_name!("th") => if !last { return InCell; }, 717 local_name!("tr") => return InRow, 718 local_name!("tbody") | local_name!("thead") | local_name!("tfoot") => return InTableBody, 719 local_name!("caption") => return InCaption, 720 local_name!("colgroup") => return InColumnGroup, 721 local_name!("table") => return InTable, 722 local_name!("template") => return *self.template_modes.last().unwrap(), 723 local_name!("head") => if !last { return InHead }, 724 local_name!("body") => return InBody, 725 local_name!("frameset") => return InFrameset, 726 local_name!("html") => match self.head_elem { 727 None => return BeforeHead, 728 Some(_) => return AfterHead, 729 }, 730 731 _ => (), 732 } 733 } 734 InBody 735 } 736 close_the_cell(&mut self)737 fn close_the_cell(&mut self) { 738 self.generate_implied_end(cursory_implied_end); 739 if self.pop_until(td_th) != 1 { 740 self.sink.parse_error(Borrowed("expected to close <td> or <th> with cell")); 741 } 742 self.clear_active_formatting_to_marker(); 743 } 744 append_text(&mut self, text: StrTendril) -> ProcessResult<Handle>745 fn append_text(&mut self, text: StrTendril) -> ProcessResult<Handle> { 746 self.insert_appropriately(AppendText(text), None); 747 Done 748 } 749 append_comment(&mut self, text: StrTendril) -> ProcessResult<Handle>750 fn append_comment(&mut self, text: StrTendril) -> ProcessResult<Handle> { 751 let comment = self.sink.create_comment(text); 752 self.insert_appropriately(AppendNode(comment), None); 753 Done 754 } 755 append_comment_to_doc(&mut self, text: StrTendril) -> ProcessResult<Handle>756 fn append_comment_to_doc(&mut self, text: StrTendril) -> ProcessResult<Handle> { 757 let target = self.doc_handle.clone(); 758 let comment = self.sink.create_comment(text); 759 self.sink.append(target, AppendNode(comment)); 760 Done 761 } 762 append_comment_to_html(&mut self, text: StrTendril) -> ProcessResult<Handle>763 fn append_comment_to_html(&mut self, text: StrTendril) -> ProcessResult<Handle> { 764 let target = self.html_elem(); 765 let comment = self.sink.create_comment(text); 766 self.sink.append(target, AppendNode(comment)); 767 Done 768 } 769 770 //§ creating-and-inserting-nodes create_root(&mut self, attrs: Vec<Attribute>)771 fn create_root(&mut self, attrs: Vec<Attribute>) { 772 let elem = self.sink.create_element(qualname!(html, "html"), attrs); 773 self.push(&elem); 774 self.sink.append(self.doc_handle.clone(), AppendNode(elem)); 775 // FIXME: application cache selection algorithm 776 } 777 insert_element(&mut self, push: PushFlag, ns: Namespace, name: LocalName, attrs: Vec<Attribute>) -> Handle778 fn insert_element(&mut self, push: PushFlag, ns: Namespace, name: LocalName, attrs: Vec<Attribute>) 779 -> Handle { 780 let elem = self.sink.create_element(QualName::new(ns, name), attrs); 781 self.insert_appropriately(AppendNode(elem.clone()), None); 782 match push { 783 Push => self.push(&elem), 784 NoPush => (), 785 } 786 // FIXME: Remove from the stack if we can't append? 787 elem 788 } 789 insert_element_for(&mut self, tag: Tag) -> Handle790 fn insert_element_for(&mut self, tag: Tag) -> Handle { 791 self.insert_element(Push, ns!(html), tag.name, tag.attrs) 792 } 793 insert_and_pop_element_for(&mut self, tag: Tag) -> Handle794 fn insert_and_pop_element_for(&mut self, tag: Tag) -> Handle { 795 self.insert_element(NoPush, ns!(html), tag.name, tag.attrs) 796 } 797 insert_phantom(&mut self, name: LocalName) -> Handle798 fn insert_phantom(&mut self, name: LocalName) -> Handle { 799 self.insert_element(Push, ns!(html), name, vec!()) 800 } 801 //§ END 802 create_formatting_element_for(&mut self, tag: Tag) -> Handle803 fn create_formatting_element_for(&mut self, tag: Tag) -> Handle { 804 // FIXME: This really wants unit tests. 805 let mut first_match = None; 806 let mut matches = 0usize; 807 for (i, _, old_tag) in self.active_formatting_end_to_marker() { 808 if tag.equiv_modulo_attr_order(old_tag) { 809 first_match = Some(i); 810 matches += 1; 811 } 812 } 813 814 if matches >= 3 { 815 self.active_formatting.remove(first_match.expect("matches with no index")); 816 } 817 818 let elem = self.insert_element(Push, ns!(html), tag.name.clone(), tag.attrs.clone()); 819 self.active_formatting.push(Element(elem.clone(), tag)); 820 elem 821 } 822 clear_active_formatting_to_marker(&mut self)823 fn clear_active_formatting_to_marker(&mut self) { 824 loop { 825 match self.active_formatting.pop() { 826 None | Some(Marker) => break, 827 _ => (), 828 } 829 } 830 } 831 process_end_tag_in_body(&mut self, tag: Tag)832 fn process_end_tag_in_body(&mut self, tag: Tag) { 833 // Look back for a matching open element. 834 let mut match_idx = None; 835 for (i, elem) in self.open_elems.iter().enumerate().rev() { 836 if self.html_elem_named(elem.clone(), tag.name.clone()) { 837 match_idx = Some(i); 838 break; 839 } 840 841 if self.elem_in(elem.clone(), special_tag) { 842 self.sink.parse_error(Borrowed("Found special tag while closing generic tag")); 843 return; 844 } 845 } 846 847 // Can't use unwrap_or_return!() due to rust-lang/rust#16617. 848 let match_idx = match match_idx { 849 None => { 850 // I believe this is impossible, because the root 851 // <html> element is in special_tag. 852 self.unexpected(&tag); 853 return; 854 } 855 Some(x) => x, 856 }; 857 858 self.generate_implied_end_except(tag.name.clone()); 859 860 if match_idx != self.open_elems.len() - 1 { 861 // mis-nested tags 862 self.unexpected(&tag); 863 } 864 self.open_elems.truncate(match_idx); 865 } 866 handle_misnested_a_tags(&mut self, tag: &Tag)867 fn handle_misnested_a_tags(&mut self, tag: &Tag) { 868 let node = unwrap_or_return!( 869 self.active_formatting_end_to_marker() 870 .filter(|&(_, n, _)| self.html_elem_named(n.clone(), local_name!("a"))) 871 .next() 872 .map(|(_, n, _)| n.clone()), 873 874 () 875 ); 876 877 self.unexpected(tag); 878 self.adoption_agency(local_name!("a")); 879 self.position_in_active_formatting(&node) 880 .map(|index| self.active_formatting.remove(index)); 881 self.remove_from_stack(&node); 882 } 883 884 //§ tree-construction is_foreign(&mut self, token: &Token) -> bool885 fn is_foreign(&mut self, token: &Token) -> bool { 886 if let EOFToken = *token { 887 return false; 888 } 889 890 if self.open_elems.len() == 0 { 891 return false; 892 } 893 894 let name = self.sink.elem_name(self.adjusted_current_node()); 895 if let ns!(html) = name.ns { 896 return false; 897 } 898 899 if mathml_text_integration_point(name.clone()) { 900 match *token { 901 CharacterTokens(..) | NullCharacterToken => return false, 902 TagToken(Tag { kind: StartTag, ref name, .. }) 903 if !matches!(*name, local_name!("mglyph") | local_name!("malignmark")) => return false, 904 _ => (), 905 } 906 } 907 908 if svg_html_integration_point(name.clone()) { 909 match *token { 910 CharacterTokens(..) | NullCharacterToken => return false, 911 TagToken(Tag { kind: StartTag, .. }) => return false, 912 _ => (), 913 } 914 } 915 916 if let qualname!(mathml, "annotation-xml") = name { 917 match *token { 918 TagToken(Tag { kind: StartTag, name: local_name!("svg"), .. }) => return false, 919 CharacterTokens(..) | NullCharacterToken | 920 TagToken(Tag { kind: StartTag, .. }) => { 921 return !self.sink.is_mathml_annotation_xml_integration_point(self.adjusted_current_node()) 922 } 923 _ => {} 924 }; 925 } 926 927 true 928 } 929 //§ END 930 enter_foreign(&mut self, mut tag: Tag, ns: Namespace) -> ProcessResult<Handle>931 fn enter_foreign(&mut self, mut tag: Tag, ns: Namespace) -> ProcessResult<Handle> { 932 match ns { 933 ns!(mathml) => self.adjust_mathml_attributes(&mut tag), 934 ns!(svg) => self.adjust_svg_attributes(&mut tag), 935 _ => (), 936 } 937 self.adjust_foreign_attributes(&mut tag); 938 939 if tag.self_closing { 940 self.insert_element(NoPush, ns, tag.name, tag.attrs); 941 DoneAckSelfClosing 942 } else { 943 self.insert_element(Push, ns, tag.name, tag.attrs); 944 Done 945 } 946 } 947 adjust_svg_tag_name(&mut self, tag: &mut Tag)948 fn adjust_svg_tag_name(&mut self, tag: &mut Tag) { 949 let Tag { ref mut name, .. } = *tag; 950 match *name { 951 local_name!("altglyph") => *name = local_name!("altGlyph"), 952 local_name!("altglyphdef") => *name = local_name!("altGlyphDef"), 953 local_name!("altglyphitem") => *name = local_name!("altGlyphItem"), 954 local_name!("animatecolor") => *name = local_name!("animateColor"), 955 local_name!("animatemotion") => *name = local_name!("animateMotion"), 956 local_name!("animatetransform") => *name = local_name!("animateTransform"), 957 local_name!("clippath") => *name = local_name!("clipPath"), 958 local_name!("feblend") => *name = local_name!("feBlend"), 959 local_name!("fecolormatrix") => *name = local_name!("feColorMatrix"), 960 local_name!("fecomponenttransfer") => *name = local_name!("feComponentTransfer"), 961 local_name!("fecomposite") => *name = local_name!("feComposite"), 962 local_name!("feconvolvematrix") => *name = local_name!("feConvolveMatrix"), 963 local_name!("fediffuselighting") => *name = local_name!("feDiffuseLighting"), 964 local_name!("fedisplacementmap") => *name = local_name!("feDisplacementMap"), 965 local_name!("fedistantlight") => *name = local_name!("feDistantLight"), 966 local_name!("fedropshadow") => *name = local_name!("feDropShadow"), 967 local_name!("feflood") => *name = local_name!("feFlood"), 968 local_name!("fefunca") => *name = local_name!("feFuncA"), 969 local_name!("fefuncb") => *name = local_name!("feFuncB"), 970 local_name!("fefuncg") => *name = local_name!("feFuncG"), 971 local_name!("fefuncr") => *name = local_name!("feFuncR"), 972 local_name!("fegaussianblur") => *name = local_name!("feGaussianBlur"), 973 local_name!("feimage") => *name = local_name!("feImage"), 974 local_name!("femerge") => *name = local_name!("feMerge"), 975 local_name!("femergenode") => *name = local_name!("feMergeNode"), 976 local_name!("femorphology") => *name = local_name!("feMorphology"), 977 local_name!("feoffset") => *name = local_name!("feOffset"), 978 local_name!("fepointlight") => *name = local_name!("fePointLight"), 979 local_name!("fespecularlighting") => *name = local_name!("feSpecularLighting"), 980 local_name!("fespotlight") => *name = local_name!("feSpotLight"), 981 local_name!("fetile") => *name = local_name!("feTile"), 982 local_name!("feturbulence") => *name = local_name!("feTurbulence"), 983 local_name!("foreignobject") => *name = local_name!("foreignObject"), 984 local_name!("glyphref") => *name = local_name!("glyphRef"), 985 local_name!("lineargradient") => *name = local_name!("linearGradient"), 986 local_name!("radialgradient") => *name = local_name!("radialGradient"), 987 local_name!("textpath") => *name = local_name!("textPath"), 988 _ => (), 989 } 990 } 991 adjust_attributes<F>(&mut self, tag: &mut Tag, mut map: F) where F: FnMut(LocalName) -> Option<QualName>,992 fn adjust_attributes<F>(&mut self, tag: &mut Tag, mut map: F) 993 where F: FnMut(LocalName) -> Option<QualName>, 994 { 995 for &mut Attribute { ref mut name, .. } in &mut tag.attrs { 996 if let Some(replacement) = map(name.local.clone()) { 997 *name = replacement; 998 } 999 } 1000 } 1001 adjust_svg_attributes(&mut self, tag: &mut Tag)1002 fn adjust_svg_attributes(&mut self, tag: &mut Tag) { 1003 self.adjust_attributes(tag, |k| match k { 1004 local_name!("attributename") => Some(qualname!("", "attributeName")), 1005 local_name!("attributetype") => Some(qualname!("", "attributeType")), 1006 local_name!("basefrequency") => Some(qualname!("", "baseFrequency")), 1007 local_name!("baseprofile") => Some(qualname!("", "baseProfile")), 1008 local_name!("calcmode") => Some(qualname!("", "calcMode")), 1009 local_name!("clippathunits") => Some(qualname!("", "clipPathUnits")), 1010 local_name!("diffuseconstant") => Some(qualname!("", "diffuseConstant")), 1011 local_name!("edgemode") => Some(qualname!("", "edgeMode")), 1012 local_name!("filterunits") => Some(qualname!("", "filterUnits")), 1013 local_name!("glyphref") => Some(qualname!("", "glyphRef")), 1014 local_name!("gradienttransform") => Some(qualname!("", "gradientTransform")), 1015 local_name!("gradientunits") => Some(qualname!("", "gradientUnits")), 1016 local_name!("kernelmatrix") => Some(qualname!("", "kernelMatrix")), 1017 local_name!("kernelunitlength") => Some(qualname!("", "kernelUnitLength")), 1018 local_name!("keypoints") => Some(qualname!("", "keyPoints")), 1019 local_name!("keysplines") => Some(qualname!("", "keySplines")), 1020 local_name!("keytimes") => Some(qualname!("", "keyTimes")), 1021 local_name!("lengthadjust") => Some(qualname!("", "lengthAdjust")), 1022 local_name!("limitingconeangle") => Some(qualname!("", "limitingConeAngle")), 1023 local_name!("markerheight") => Some(qualname!("", "markerHeight")), 1024 local_name!("markerunits") => Some(qualname!("", "markerUnits")), 1025 local_name!("markerwidth") => Some(qualname!("", "markerWidth")), 1026 local_name!("maskcontentunits") => Some(qualname!("", "maskContentUnits")), 1027 local_name!("maskunits") => Some(qualname!("", "maskUnits")), 1028 local_name!("numoctaves") => Some(qualname!("", "numOctaves")), 1029 local_name!("pathlength") => Some(qualname!("", "pathLength")), 1030 local_name!("patterncontentunits") => Some(qualname!("", "patternContentUnits")), 1031 local_name!("patterntransform") => Some(qualname!("", "patternTransform")), 1032 local_name!("patternunits") => Some(qualname!("", "patternUnits")), 1033 local_name!("pointsatx") => Some(qualname!("", "pointsAtX")), 1034 local_name!("pointsaty") => Some(qualname!("", "pointsAtY")), 1035 local_name!("pointsatz") => Some(qualname!("", "pointsAtZ")), 1036 local_name!("preservealpha") => Some(qualname!("", "preserveAlpha")), 1037 local_name!("preserveaspectratio") => Some(qualname!("", "preserveAspectRatio")), 1038 local_name!("primitiveunits") => Some(qualname!("", "primitiveUnits")), 1039 local_name!("refx") => Some(qualname!("", "refX")), 1040 local_name!("refy") => Some(qualname!("", "refY")), 1041 local_name!("repeatcount") => Some(qualname!("", "repeatCount")), 1042 local_name!("repeatdur") => Some(qualname!("", "repeatDur")), 1043 local_name!("requiredextensions") => Some(qualname!("", "requiredExtensions")), 1044 local_name!("requiredfeatures") => Some(qualname!("", "requiredFeatures")), 1045 local_name!("specularconstant") => Some(qualname!("", "specularConstant")), 1046 local_name!("specularexponent") => Some(qualname!("", "specularExponent")), 1047 local_name!("spreadmethod") => Some(qualname!("", "spreadMethod")), 1048 local_name!("startoffset") => Some(qualname!("", "startOffset")), 1049 local_name!("stddeviation") => Some(qualname!("", "stdDeviation")), 1050 local_name!("stitchtiles") => Some(qualname!("", "stitchTiles")), 1051 local_name!("surfacescale") => Some(qualname!("", "surfaceScale")), 1052 local_name!("systemlanguage") => Some(qualname!("", "systemLanguage")), 1053 local_name!("tablevalues") => Some(qualname!("", "tableValues")), 1054 local_name!("targetx") => Some(qualname!("", "targetX")), 1055 local_name!("targety") => Some(qualname!("", "targetY")), 1056 local_name!("textlength") => Some(qualname!("", "textLength")), 1057 local_name!("viewbox") => Some(qualname!("", "viewBox")), 1058 local_name!("viewtarget") => Some(qualname!("", "viewTarget")), 1059 local_name!("xchannelselector") => Some(qualname!("", "xChannelSelector")), 1060 local_name!("ychannelselector") => Some(qualname!("", "yChannelSelector")), 1061 local_name!("zoomandpan") => Some(qualname!("", "zoomAndPan")), 1062 _ => None, 1063 }); 1064 } 1065 adjust_mathml_attributes(&mut self, tag: &mut Tag)1066 fn adjust_mathml_attributes(&mut self, tag: &mut Tag) { 1067 self.adjust_attributes(tag, |k| match k { 1068 local_name!("definitionurl") => Some(qualname!("", "definitionURL")), 1069 _ => None, 1070 }); 1071 } 1072 adjust_foreign_attributes(&mut self, tag: &mut Tag)1073 fn adjust_foreign_attributes(&mut self, tag: &mut Tag) { 1074 self.adjust_attributes(tag, |k| match k { 1075 local_name!("xlink:actuate") => Some(qualname!(xlink, "actuate")), 1076 local_name!("xlink:arcrole") => Some(qualname!(xlink, "arcrole")), 1077 local_name!("xlink:href") => Some(qualname!(xlink, "href")), 1078 local_name!("xlink:role") => Some(qualname!(xlink, "role")), 1079 local_name!("xlink:show") => Some(qualname!(xlink, "show")), 1080 local_name!("xlink:title") => Some(qualname!(xlink, "title")), 1081 local_name!("xlink:type") => Some(qualname!(xlink, "type")), 1082 local_name!("xml:base") => Some(qualname!(xml, "base")), 1083 local_name!("xml:lang") => Some(qualname!(xml, "lang")), 1084 local_name!("xml:space") => Some(qualname!(xml, "space")), 1085 local_name!("xmlns") => Some(qualname!(xmlns, "xmlns")), 1086 local_name!("xmlns:xlink") => Some(qualname!(xmlns, "xlink")), 1087 _ => None, 1088 }); 1089 } 1090 foreign_start_tag(&mut self, mut tag: Tag) -> ProcessResult<Handle>1091 fn foreign_start_tag(&mut self, mut tag: Tag) -> ProcessResult<Handle> { 1092 let cur = self.sink.elem_name(self.adjusted_current_node()); 1093 match cur.ns { 1094 ns!(mathml) => self.adjust_mathml_attributes(&mut tag), 1095 ns!(svg) => { 1096 self.adjust_svg_tag_name(&mut tag); 1097 self.adjust_svg_attributes(&mut tag); 1098 } 1099 _ => (), 1100 } 1101 self.adjust_foreign_attributes(&mut tag); 1102 if tag.self_closing { 1103 // FIXME(#118): <script /> in SVG 1104 self.insert_element(NoPush, cur.ns, tag.name, tag.attrs); 1105 DoneAckSelfClosing 1106 } else { 1107 self.insert_element(Push, cur.ns, tag.name, tag.attrs); 1108 Done 1109 } 1110 } 1111 unexpected_start_tag_in_foreign_content(&mut self, tag: Tag) -> ProcessResult<Handle>1112 fn unexpected_start_tag_in_foreign_content(&mut self, tag: Tag) -> ProcessResult<Handle> { 1113 self.unexpected(&tag); 1114 if self.is_fragment() { 1115 self.foreign_start_tag(tag) 1116 } else { 1117 self.pop(); 1118 while !self.current_node_in(|n| { 1119 n.ns == ns!(html) || mathml_text_integration_point(n.clone()) 1120 || svg_html_integration_point(n) 1121 }) { 1122 self.pop(); 1123 } 1124 ReprocessForeign(TagToken(tag)) 1125 } 1126 } 1127 } 1128