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