1// Copyright 2014-2017 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//! The HTML5 tree builder.
11
12pub use crate::interface::{
13 create_element, ElemName, ElementFlags, NextParserState, Tracer, TreeSink,
14};
15pub use crate::interface::{AppendNode, AppendText, Attribute, NodeOrText};
16pub use crate::interface::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
17
18use self::types::*;
19
20use crate::tendril::StrTendril;
21use crate::{ExpandedName, LocalName, Namespace, QualName};
22
23use crate::tokenizer;
24use crate::tokenizer::states as tok_state;
25use crate::tokenizer::{Doctype, EndTag, StartTag, Tag, TokenSink, TokenSinkResult};
26
27use std::borrow::Cow::Borrowed;
28use std::cell::{Cell, Ref, RefCell};
29use std::collections::VecDeque;
30use std::iter::{Enumerate, Rev};
31use std::{fmt, slice};
32
33use crate::tokenizer::states::RawKind;
34use crate::tree_builder::tag_sets::*;
35use crate::util::str::to_escaped_string;
36use log::{debug, log_enabled, warn, Level};
37use mac::format_if;
38use markup5ever::{expanded_name, local_name, namespace_prefix, namespace_url, ns};
39
40pub use self::PushFlag::*;
41
42#[macro_use]
43mod tag_sets;
44
45mod data;
46mod rules;
47mod types;
48
49/// Tree builder options, with an impl for Default.
50#[derive(Copy, Clone)]
51pub struct TreeBuilderOpts {
52 /// Report all parse errors described in the spec, at some
53 /// performance penalty? Default: false
54 pub exact_errors: bool,
55
56 /// Is scripting enabled?
57 pub scripting_enabled: bool,
58
59 /// Is this an `iframe srcdoc` document?
60 pub iframe_srcdoc: bool,
61
62 /// Should we drop the DOCTYPE (if any) from the tree?
63 pub drop_doctype: bool,
64
65 /// Obsolete, ignored.
66 pub ignore_missing_rules: bool,
67
68 /// Initial TreeBuilder quirks mode. Default: NoQuirks
69 pub quirks_mode: QuirksMode,
70}
71
72impl Default for TreeBuilderOpts {
73 fn default() -> TreeBuilderOpts {
74 TreeBuilderOpts {
75 exact_errors: false,
76 scripting_enabled: true,
77 iframe_srcdoc: false,
78 drop_doctype: false,
79 ignore_missing_rules: false,
80 quirks_mode: NoQuirks,
81 }
82 }
83}
84
85/// The HTML tree builder.
86pub struct TreeBuilder<Handle, Sink> {
87 /// Options controlling the behavior of the tree builder.
88 opts: TreeBuilderOpts,
89
90 /// Consumer of tree modifications.
91 pub sink: Sink,
92
93 /// Insertion mode.
94 mode: Cell<InsertionMode>,
95
96 /// Original insertion mode, used by Text and InTableText modes.
97 orig_mode: Cell<Option<InsertionMode>>,
98
99 /// Stack of template insertion modes.
100 template_modes: RefCell<Vec<InsertionMode>>,
101
102 /// Pending table character tokens.
103 pending_table_text: RefCell<Vec<(SplitStatus, StrTendril)>>,
104
105 /// Quirks mode as set by the parser.
106 /// FIXME: can scripts etc. change this?
107 quirks_mode: Cell<QuirksMode>,
108
109 /// The document node, which is created by the sink.
110 doc_handle: Handle,
111
112 /// Stack of open elements, most recently added at end.
113 open_elems: RefCell<Vec<Handle>>,
114
115 /// List of active formatting elements.
116 active_formatting: RefCell<Vec<FormatEntry<Handle>>>,
117
118 //§ the-element-pointers
119 /// Head element pointer.
120 head_elem: RefCell<Option<Handle>>,
121
122 /// Form element pointer.
123 form_elem: RefCell<Option<Handle>>,
124 //§ END
125 /// Frameset-ok flag.
126 frameset_ok: Cell<bool>,
127
128 /// Ignore a following U+000A LINE FEED?
129 ignore_lf: Cell<bool>,
130
131 /// Is foster parenting enabled?
132 foster_parenting: Cell<bool>,
133
134 /// The context element for the fragment parsing algorithm.
135 context_elem: RefCell<Option<Handle>>,
136
137 /// Track current line
138 current_line: Cell<u64>,
139 // WARNING: If you add new fields that contain Handles, you
140 // must add them to trace_handles() below to preserve memory
141 // safety!
142 //
143 // FIXME: Auto-generate the trace hooks like Servo does.
144}
145
146impl<Handle, Sink> TreeBuilder<Handle, Sink>
147where
148 Handle: Clone,
149 Sink: TreeSink<Handle = Handle>,
150{
151 /// Create a new tree builder which sends tree modifications to a particular `TreeSink`.
152 ///
153 /// The tree builder is also a `TokenSink`.
154 pub fn new(sink: Sink, opts: TreeBuilderOpts) -> TreeBuilder<Handle, Sink> {
155 let doc_handle = sink.get_document();
156 TreeBuilder {
157 opts,
158 sink,
159 mode: Cell::new(Initial),
160 orig_mode: Cell::new(None),
161 template_modes: Default::default(),
162 pending_table_text: Default::default(),
163 quirks_mode: Cell::new(opts.quirks_mode),
164 doc_handle,
165 open_elems: Default::default(),
166 active_formatting: Default::default(),
167 head_elem: Default::default(),
168 form_elem: Default::default(),
169 frameset_ok: Cell::new(true),
170 ignore_lf: Default::default(),
171 foster_parenting: Default::default(),
172 context_elem: Default::default(),
173 current_line: Cell::new(1),
174 }
175 }
176
177 /// Create a new tree builder which sends tree modifications to a particular `TreeSink`.
178 /// This is for parsing fragments.
179 ///
180 /// The tree builder is also a `TokenSink`.
181 pub fn new_for_fragment(
182 sink: Sink,
183 context_elem: Handle,
184 form_elem: Option<Handle>,
185 opts: TreeBuilderOpts,
186 ) -> TreeBuilder<Handle, Sink> {
187 let doc_handle = sink.get_document();
188 let context_is_template =
189 sink.elem_name(&context_elem).expanded() == expanded_name!(html "template");
190 let template_modes = if context_is_template {
191 RefCell::new(vec![InTemplate])
192 } else {
193 RefCell::new(vec![])
194 };
195
196 let tb = TreeBuilder {
197 opts,
198 sink,
199 mode: Cell::new(Initial),
200 orig_mode: Cell::new(None),
201 template_modes,
202 pending_table_text: Default::default(),
203 quirks_mode: Cell::new(opts.quirks_mode),
204 doc_handle,
205 open_elems: Default::default(),
206 active_formatting: Default::default(),
207 head_elem: Default::default(),
208 form_elem: RefCell::new(form_elem),
209 frameset_ok: Cell::new(true),
210 ignore_lf: Default::default(),
211 foster_parenting: Default::default(),
212 context_elem: RefCell::new(Some(context_elem)),
213 current_line: Cell::new(1),
214 };
215
216 // https://html.spec.whatwg.org/multipage/#parsing-html-fragments
217 // 5. Let root be a new html element with no attributes.
218 // 6. Append the element root to the Document node created above.
219 // 7. Set up the parser's stack of open elements so that it contains just the single element root.
220 tb.create_root(vec![]);
221 // 10. Reset the parser's insertion mode appropriately.
222 let old_insertion_mode = tb.reset_insertion_mode();
223 tb.mode.set(old_insertion_mode);
224
225 tb
226 }
227
228 // https://html.spec.whatwg.org/multipage/#concept-frag-parse-context
229 // Step 4. Set the state of the HTML parser's tokenization stage as follows:
230 pub fn tokenizer_state_for_context_elem(&self) -> tok_state::State {
231 let context_elem = self.context_elem.borrow();
232 let elem = context_elem.as_ref().expect("no context element");
233 let elem_name = self.sink.elem_name(elem);
234 let name = match elem_name.expanded() {
235 ExpandedName {
236 ns: &ns!(html),
237 local,
238 } => local,
239 _ => return tok_state::Data,
240 };
241 match *name {
242 local_name!("title") | local_name!("textarea") => tok_state::RawData(tok_state::Rcdata),
243
244 local_name!("style")
245 | local_name!("xmp")
246 | local_name!("iframe")
247 | local_name!("noembed")
248 | local_name!("noframes") => tok_state::RawData(tok_state::Rawtext),
249
250 local_name!("script") => tok_state::RawData(tok_state::ScriptData),
251
252 local_name!("noscript") => {
253 if self.opts.scripting_enabled {
254 tok_state::RawData(tok_state::Rawtext)
255 } else {
256 tok_state::Data
257 }
258 },
259
260 local_name!("plaintext") => tok_state::Plaintext,
261
262 _ => tok_state::Data,
263 }
264 }
265
266 /// Call the `Tracer`'s `trace_handle` method on every `Handle` in the tree builder's
267 /// internal state. This is intended to support garbage-collected DOMs.
268 pub fn trace_handles(&self, tracer: &dyn Tracer<Handle = Handle>) {
269 tracer.trace_handle(&self.doc_handle);
270 for e in &*self.open_elems.borrow() {
271 tracer.trace_handle(e);
272 }
273
274 for e in &*self.active_formatting.borrow() {
275 if let FormatEntry::Element(handle, _) = e {
276 tracer.trace_handle(handle);
277 }
278 }
279
280 if let Some(head_elem) = self.head_elem.borrow().as_ref() {
281 tracer.trace_handle(head_elem);
282 }
283
284 if let Some(form_elem) = self.form_elem.borrow().as_ref() {
285 tracer.trace_handle(form_elem);
286 }
287
288 if let Some(context_elem) = self.context_elem.borrow().as_ref() {
289 tracer.trace_handle(context_elem);
290 }
291 }
292
293 #[allow(dead_code)]
294 fn dump_state(&self, label: String) {
295 println!("dump_state on {label}");
296 print!(" open_elems:");
297 for node in self.open_elems.borrow().iter() {
298 let name = self.sink.elem_name(node);
299 match *name.ns() {
300 ns!(html) => print!(" {}", name.local_name()),
301 _ => panic!(),
302 }
303 }
304 println!();
305 print!(" active_formatting:");
306 for entry in self.active_formatting.borrow().iter() {
307 match entry {
308 &Marker => print!(" Marker"),
309 Element(h, _) => {
310 let name = self.sink.elem_name(h);
311 match *name.ns() {
312 ns!(html) => print!(" {}", name.local_name()),
313 _ => panic!(),
314 }
315 },
316 }
317 }
318 println!();
319 }
320
321 fn debug_step(&self, mode: InsertionMode, token: &Token) {
322 if log_enabled!(Level::Debug) {
323 debug!(
324 "processing {} in insertion mode {:?}",
325 to_escaped_string(token),
326 mode
327 );
328 }
329 }
330
331 fn process_to_completion(&self, mut token: Token) -> TokenSinkResult<Handle> {
332 // Queue of additional tokens yet to be processed.
333 // This stays empty in the common case where we don't split whitespace.
334 let mut more_tokens = VecDeque::new();
335
336 loop {
337 let should_have_acknowledged_self_closing_flag = matches!(
338 token,
339 TagToken(Tag {
340 self_closing: true,
341 kind: StartTag,
342 ..
343 })
344 );
345 let result = if self.is_foreign(&token) {
346 self.step_foreign(token)
347 } else {
348 let mode = self.mode.get();
349 self.step(mode, token)
350 };
351 match result {
352 Done => {
353 if should_have_acknowledged_self_closing_flag {
354 self.sink
355 .parse_error(Borrowed("Unacknowledged self-closing tag"));
356 }
357 token = unwrap_or_return!(
358 more_tokens.pop_front(),
359 tokenizer::TokenSinkResult::Continue
360 );
361 },
362 DoneAckSelfClosing => {
363 token = unwrap_or_return!(
364 more_tokens.pop_front(),
365 tokenizer::TokenSinkResult::Continue
366 );
367 },
368 Reprocess(m, t) => {
369 self.mode.set(m);
370 token = t;
371 },
372 ReprocessForeign(t) => {
373 token = t;
374 },
375 SplitWhitespace(mut buf) => {
376 let p = buf.pop_front_char_run(|c| c.is_ascii_whitespace());
377 let (first, is_ws) = unwrap_or_return!(p, tokenizer::TokenSinkResult::Continue);
378 let status = if is_ws { Whitespace } else { NotWhitespace };
379 token = CharacterTokens(status, first);
380
381 if buf.len32() > 0 {
382 more_tokens.push_back(CharacterTokens(NotSplit, buf));
383 }
384 },
385 Script(node) => {
386 assert!(more_tokens.is_empty());
387 return tokenizer::TokenSinkResult::Script(node);
388 },
389 ToPlaintext => {
390 assert!(more_tokens.is_empty());
391 return tokenizer::TokenSinkResult::Plaintext;
392 },
393 ToRawData(k) => {
394 assert!(more_tokens.is_empty());
395 return tokenizer::TokenSinkResult::RawData(k);
396 },
397 }
398 }
399 }
400
401 /// Are we parsing a HTML fragment?
402 pub fn is_fragment(&self) -> bool {
403 self.context_elem.borrow().is_some()
404 }
405
406 /// https://html.spec.whatwg.org/multipage/#appropriate-place-for-inserting-a-node
407 fn appropriate_place_for_insertion(
408 &self,
409 override_target: Option<Handle>,
410 ) -> InsertionPoint<Handle> {
411 use self::tag_sets::*;
412
413 declare_tag_set!(foster_target = "table" "tbody" "tfoot" "thead" "tr");
414 let target = override_target.unwrap_or_else(|| self.current_node().clone());
415 if !(self.foster_parenting.get() && self.elem_in(&target, foster_target)) {
416 if self.html_elem_named(&target, local_name!("template")) {
417 // No foster parenting (inside template).
418 let contents = self.sink.get_template_contents(&target);
419 return LastChild(contents);
420 } else {
421 // No foster parenting (the common case).
422 return LastChild(target);
423 }
424 }
425
426 // Foster parenting
427 let open_elems = self.open_elems.borrow();
428 let mut iter = open_elems.iter().rev().peekable();
429 while let Some(elem) = iter.next() {
430 if self.html_elem_named(elem, local_name!("template")) {
431 let contents = self.sink.get_template_contents(elem);
432 return LastChild(contents);
433 } else if self.html_elem_named(elem, local_name!("table")) {
434 return TableFosterParenting {
435 element: elem.clone(),
436 prev_element: (*iter.peek().unwrap()).clone(),
437 };
438 }
439 }
440 let html_elem = self.html_elem();
441 LastChild(html_elem.clone())
442 }
443
444 fn insert_at(&self, insertion_point: InsertionPoint<Handle>, child: NodeOrText<Handle>) {
445 match insertion_point {
446 LastChild(parent) => self.sink.append(&parent, child),
447 BeforeSibling(sibling) => self.sink.append_before_sibling(&sibling, child),
448 TableFosterParenting {
449 element,
450 prev_element,
451 } => self
452 .sink
453 .append_based_on_parent_node(&element, &prev_element, child),
454 }
455 }
456}
457
458impl<Handle, Sink> TokenSink for TreeBuilder<Handle, Sink>
459where
460 Handle: Clone,
461 Sink: TreeSink<Handle = Handle>,
462{
463 type Handle = Handle;
464
465 fn process_token(&self, token: tokenizer::Token, line_number: u64) -> TokenSinkResult<Handle> {
466 if line_number != self.current_line.get() {
467 self.sink.set_current_line(line_number);
468 }
469 let ignore_lf = self.ignore_lf.take();
470
471 // Handle `ParseError` and `DoctypeToken`; convert everything else to the local `Token` type.
472 let token = match token {
473 tokenizer::ParseError(e) => {
474 self.sink.parse_error(e);
475 return tokenizer::TokenSinkResult::Continue;
476 },
477
478 tokenizer::DoctypeToken(dt) => {
479 if self.mode.get() == Initial {
480 let (err, quirk) = data::doctype_error_and_quirks(&dt, self.opts.iframe_srcdoc);
481 if err {
482 self.sink.parse_error(format_if!(
483 self.opts.exact_errors,
484 "Bad DOCTYPE",
485 "Bad DOCTYPE: {:?}",
486 dt
487 ));
488 }
489 let Doctype {
490 name,
491 public_id,
492 system_id,
493 force_quirks: _,
494 } = dt;
495 if !self.opts.drop_doctype {
496 self.sink.append_doctype_to_document(
497 name.unwrap_or(StrTendril::new()),
498 public_id.unwrap_or(StrTendril::new()),
499 system_id.unwrap_or(StrTendril::new()),
500 );
501 }
502 self.set_quirks_mode(quirk);
503
504 self.mode.set(BeforeHtml);
505 return tokenizer::TokenSinkResult::Continue;
506 } else {
507 self.sink.parse_error(format_if!(
508 self.opts.exact_errors,
509 "DOCTYPE in body",
510 "DOCTYPE in insertion mode {:?}",
511 self.mode.get()
512 ));
513 return tokenizer::TokenSinkResult::Continue;
514 }
515 },
516
517 tokenizer::TagToken(x) => TagToken(x),
518 tokenizer::CommentToken(x) => CommentToken(x),
519 tokenizer::NullCharacterToken => NullCharacterToken,
520 tokenizer::EOFToken => EOFToken,
521
522 tokenizer::CharacterTokens(mut x) => {
523 if ignore_lf && x.starts_with("\n") {
524 x.pop_front(1);
525 }
526 if x.is_empty() {
527 return tokenizer::TokenSinkResult::Continue;
528 }
529 CharacterTokens(NotSplit, x)
530 },
531 };
532
533 self.process_to_completion(token)
534 }
535
536 fn end(&self) {
537 for elem in self.open_elems.borrow_mut().drain(..).rev() {
538 self.sink.pop(&elem);
539 }
540 }
541
542 fn adjusted_current_node_present_but_not_in_html_namespace(&self) -> bool {
543 !self.open_elems.borrow().is_empty()
544 && *self.sink.elem_name(&self.adjusted_current_node()).ns() != ns!(html)
545 }
546}
547
548pub fn html_elem<Handle>(open_elems: &[Handle]) -> &Handle {
549 &open_elems[0]
550}
551
552struct ActiveFormattingView<'a, Handle: 'a> {
553 data: Ref<'a, Vec<FormatEntry<Handle>>>,
554}
555
556impl<'a, Handle: 'a> ActiveFormattingView<'a, Handle> {
557 fn iter(&'a self) -> impl Iterator<Item = (usize, &'a Handle, &'a Tag)> + 'a {
558 ActiveFormattingIter {
559 iter: self.data.iter().enumerate().rev(),
560 }
561 }
562}
563
564pub struct ActiveFormattingIter<'a, Handle: 'a> {
565 iter: Rev<Enumerate<slice::Iter<'a, FormatEntry<Handle>>>>,
566}
567
568impl<'a, Handle> Iterator for ActiveFormattingIter<'a, Handle> {
569 type Item = (usize, &'a Handle, &'a Tag);
570 fn next(&mut self) -> Option<(usize, &'a Handle, &'a Tag)> {
571 match self.iter.next() {
572 None | Some((_, &Marker)) => None,
573 Some((i: usize, Element(h: &Handle, t: &Tag))) => Some((i, h, t)),
574 }
575 }
576}
577
578pub enum PushFlag {
579 Push,
580 NoPush,
581}
582
583enum Bookmark<Handle> {
584 Replace(Handle),
585 InsertAfter(Handle),
586}
587
588macro_rules! qualname {
589 ("", $local:tt) => {
590 QualName {
591 prefix: None,
592 ns: ns!(),
593 local: local_name!($local),
594 }
595 };
596 ($prefix: tt $ns:tt $local:tt) => {
597 QualName {
598 prefix: Some(namespace_prefix!($prefix)),
599 ns: ns!($ns),
600 local: local_name!($local),
601 }
602 };
603}
604
605#[doc(hidden)]
606impl<Handle, Sink> TreeBuilder<Handle, Sink>
607where
608 Handle: Clone,
609 Sink: TreeSink<Handle = Handle>,
610{
611 fn unexpected<T: fmt::Debug>(&self, _thing: &T) -> ProcessResult<Handle> {
612 self.sink.parse_error(format_if!(
613 self.opts.exact_errors,
614 "Unexpected token",
615 "Unexpected token {} in insertion mode {:?}",
616 to_escaped_string(_thing),
617 self.mode.get()
618 ));
619 Done
620 }
621
622 fn assert_named(&self, node: &Handle, name: LocalName) {
623 assert!(self.html_elem_named(node, name));
624 }
625
626 /// Iterate over the active formatting elements (with index in the list) from the end
627 /// to the last marker, or the beginning if there are no markers.
628 fn active_formatting_end_to_marker(&self) -> ActiveFormattingView<'_, Handle> {
629 ActiveFormattingView {
630 data: self.active_formatting.borrow(),
631 }
632 }
633
634 fn position_in_active_formatting(&self, element: &Handle) -> Option<usize> {
635 self.active_formatting
636 .borrow()
637 .iter()
638 .position(|n| match n {
639 FormatEntry::Marker => false,
640 FormatEntry::Element(ref handle, _) => self.sink.same_node(handle, element),
641 })
642 }
643
644 fn set_quirks_mode(&self, mode: QuirksMode) {
645 self.quirks_mode.set(mode);
646 self.sink.set_quirks_mode(mode);
647 }
648
649 fn stop_parsing(&self) -> ProcessResult<Handle> {
650 Done
651 }
652
653 //§ parsing-elements-that-contain-only-text
654 // Switch to `Text` insertion mode, save the old mode, and
655 // switch the tokenizer to a raw-data state.
656 // The latter only takes effect after the current / next
657 // `process_token` of a start tag returns!
658 fn to_raw_text_mode(&self, k: RawKind) -> ProcessResult<Handle> {
659 self.orig_mode.set(Some(self.mode.get()));
660 self.mode.set(Text);
661 ToRawData(k)
662 }
663
664 // The generic raw text / RCDATA parsing algorithm.
665 fn parse_raw_data(&self, tag: Tag, k: RawKind) -> ProcessResult<Handle> {
666 self.insert_element_for(tag);
667 self.to_raw_text_mode(k)
668 }
669 //§ END
670
671 fn current_node(&self) -> Ref<Handle> {
672 Ref::map(self.open_elems.borrow(), |elems| {
673 elems.last().expect("no current element")
674 })
675 }
676
677 fn adjusted_current_node(&self) -> Ref<Handle> {
678 if self.open_elems.borrow().len() == 1 {
679 let context_elem = self.context_elem.borrow();
680 let ctx = Ref::filter_map(context_elem, |e| e.as_ref());
681 if let Ok(ctx) = ctx {
682 return ctx;
683 }
684 }
685 self.current_node()
686 }
687
688 fn current_node_in<TagSet>(&self, set: TagSet) -> bool
689 where
690 TagSet: Fn(ExpandedName) -> bool,
691 {
692 set(self.sink.elem_name(&self.current_node()).expanded())
693 }
694
695 // Insert at the "appropriate place for inserting a node".
696 fn insert_appropriately(&self, child: NodeOrText<Handle>, override_target: Option<Handle>) {
697 let insertion_point = self.appropriate_place_for_insertion(override_target);
698 self.insert_at(insertion_point, child);
699 }
700
701 fn adoption_agency(&self, subject: LocalName) {
702 // 1.
703 if self.current_node_named(subject.clone())
704 && self
705 .position_in_active_formatting(&self.current_node())
706 .is_none()
707 {
708 self.pop();
709 return;
710 }
711
712 // 2. 3. 4.
713 for _ in 0..8 {
714 // 5.
715 let (fmt_elem_index, fmt_elem, fmt_elem_tag) = unwrap_or_return!(
716 // We clone the Handle and Tag so they don't cause an immutable borrow of self.
717 self.active_formatting_end_to_marker()
718 .iter()
719 .find(|&(_, _, tag)| tag.name == subject)
720 .map(|(i, h, t)| (i, h.clone(), t.clone())),
721 {
722 self.process_end_tag_in_body(Tag {
723 kind: EndTag,
724 name: subject,
725 self_closing: false,
726 attrs: vec![],
727 });
728 }
729 );
730
731 let fmt_elem_stack_index = unwrap_or_return!(
732 self.open_elems
733 .borrow()
734 .iter()
735 .rposition(|n| self.sink.same_node(n, &fmt_elem)),
736 {
737 self.sink
738 .parse_error(Borrowed("Formatting element not open"));
739 self.active_formatting.borrow_mut().remove(fmt_elem_index);
740 }
741 );
742
743 // 7.
744 if !self.in_scope(default_scope, |n| self.sink.same_node(&n, &fmt_elem)) {
745 self.sink
746 .parse_error(Borrowed("Formatting element not in scope"));
747 return;
748 }
749
750 // 8.
751 if !self.sink.same_node(&self.current_node(), &fmt_elem) {
752 self.sink
753 .parse_error(Borrowed("Formatting element not current node"));
754 }
755
756 // 9.
757 let (furthest_block_index, furthest_block) = unwrap_or_return!(
758 self.open_elems
759 .borrow()
760 .iter()
761 .enumerate()
762 .skip(fmt_elem_stack_index)
763 .find(|&(_, open_element)| self.elem_in(open_element, special_tag))
764 .map(|(i, h)| (i, h.clone())),
765 // 10.
766 {
767 self.open_elems.borrow_mut().truncate(fmt_elem_stack_index);
768 self.active_formatting.borrow_mut().remove(fmt_elem_index);
769 }
770 );
771
772 // 11.
773 let common_ancestor = self.open_elems.borrow()[fmt_elem_stack_index - 1].clone();
774
775 // 12.
776 let mut bookmark = Bookmark::Replace(fmt_elem.clone());
777
778 // 13.
779 let mut node;
780 let mut node_index = furthest_block_index;
781 let mut last_node = furthest_block.clone();
782
783 // 13.1.
784 let mut inner_counter = 0;
785 loop {
786 // 13.2.
787 inner_counter += 1;
788
789 // 13.3.
790 node_index -= 1;
791 node = self.open_elems.borrow()[node_index].clone();
792
793 // 13.4.
794 if self.sink.same_node(&node, &fmt_elem) {
795 break;
796 }
797
798 // 13.5.
799 if inner_counter > 3 {
800 self.position_in_active_formatting(&node)
801 .map(|position| self.active_formatting.borrow_mut().remove(position));
802 self.open_elems.borrow_mut().remove(node_index);
803 continue;
804 }
805
806 let node_formatting_index = unwrap_or_else!(
807 self.position_in_active_formatting(&node),
808 // 13.6.
809 {
810 self.open_elems.borrow_mut().remove(node_index);
811 continue;
812 }
813 );
814
815 // 13.7.
816 let tag = match self.active_formatting.borrow()[node_formatting_index] {
817 Element(ref h, ref t) => {
818 assert!(self.sink.same_node(h, &node));
819 t.clone()
820 },
821 Marker => panic!("Found marker during adoption agency"),
822 };
823 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their
824 // own, once as part of t.clone() above)?
825 let new_element = create_element(
826 &self.sink,
827 QualName::new(None, ns!(html), tag.name.clone()),
828 tag.attrs.clone(),
829 );
830 self.open_elems.borrow_mut()[node_index] = new_element.clone();
831 self.active_formatting.borrow_mut()[node_formatting_index] =
832 Element(new_element.clone(), tag);
833 node = new_element;
834
835 // 13.8.
836 if self.sink.same_node(&last_node, &furthest_block) {
837 bookmark = Bookmark::InsertAfter(node.clone());
838 }
839
840 // 13.9.
841 self.sink.remove_from_parent(&last_node);
842 self.sink.append(&node, AppendNode(last_node.clone()));
843
844 // 13.10.
845 last_node = node.clone();
846
847 // 13.11.
848 }
849
850 // 14.
851 self.sink.remove_from_parent(&last_node);
852 self.insert_appropriately(AppendNode(last_node.clone()), Some(common_ancestor));
853
854 // 15.
855 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their own,
856 // once as part of t.clone() above)?
857 let new_element = create_element(
858 &self.sink,
859 QualName::new(None, ns!(html), fmt_elem_tag.name.clone()),
860 fmt_elem_tag.attrs.clone(),
861 );
862 let new_entry = Element(new_element.clone(), fmt_elem_tag);
863
864 // 16.
865 self.sink.reparent_children(&furthest_block, &new_element);
866
867 // 17.
868 self.sink
869 .append(&furthest_block, AppendNode(new_element.clone()));
870
871 // 18.
872 // FIXME: We could probably get rid of the position_in_active_formatting() calls here
873 // if we had a more clever Bookmark representation.
874 match bookmark {
875 Bookmark::Replace(to_replace) => {
876 let index = self
877 .position_in_active_formatting(&to_replace)
878 .expect("bookmark not found in active formatting elements");
879 self.active_formatting.borrow_mut()[index] = new_entry;
880 },
881 Bookmark::InsertAfter(previous) => {
882 let index = self
883 .position_in_active_formatting(&previous)
884 .expect("bookmark not found in active formatting elements")
885 + 1;
886 self.active_formatting.borrow_mut().insert(index, new_entry);
887 let old_index = self
888 .position_in_active_formatting(&fmt_elem)
889 .expect("formatting element not found in active formatting elements");
890 self.active_formatting.borrow_mut().remove(old_index);
891 },
892 }
893
894 // 19.
895 self.remove_from_stack(&fmt_elem);
896 let new_furthest_block_index = self
897 .open_elems
898 .borrow()
899 .iter()
900 .position(|n| self.sink.same_node(n, &furthest_block))
901 .expect("furthest block missing from open element stack");
902 self.open_elems
903 .borrow_mut()
904 .insert(new_furthest_block_index + 1, new_element);
905
906 // 20.
907 }
908 }
909
910 fn push(&self, elem: &Handle) {
911 self.open_elems.borrow_mut().push(elem.clone());
912 }
913
914 fn pop(&self) -> Handle {
915 let elem = self
916 .open_elems
917 .borrow_mut()
918 .pop()
919 .expect("no current element");
920 self.sink.pop(&elem);
921 elem
922 }
923
924 fn remove_from_stack(&self, elem: &Handle) {
925 let position = self
926 .open_elems
927 .borrow()
928 .iter()
929 .rposition(|x| self.sink.same_node(elem, x));
930 if let Some(position) = position {
931 self.open_elems.borrow_mut().remove(position);
932 self.sink.pop(elem);
933 }
934 }
935
936 fn is_marker_or_open(&self, entry: &FormatEntry<Handle>) -> bool {
937 match *entry {
938 Marker => true,
939 Element(ref node, _) => self
940 .open_elems
941 .borrow()
942 .iter()
943 .rev()
944 .any(|n| self.sink.same_node(n, node)),
945 }
946 }
947
948 /// Reconstruct the active formatting elements.
949 fn reconstruct_formatting(&self) {
950 {
951 let active_formatting = self.active_formatting.borrow();
952 let last = unwrap_or_return!(active_formatting.last());
953 if self.is_marker_or_open(last) {
954 return;
955 }
956 }
957
958 let mut entry_index = self.active_formatting.borrow().len() - 1;
959 loop {
960 if entry_index == 0 {
961 break;
962 }
963 entry_index -= 1;
964 if self.is_marker_or_open(&self.active_formatting.borrow()[entry_index]) {
965 entry_index += 1;
966 break;
967 }
968 }
969
970 loop {
971 let tag = match self.active_formatting.borrow()[entry_index] {
972 Element(_, ref t) => t.clone(),
973 Marker => panic!("Found marker during formatting element reconstruction"),
974 };
975
976 // FIXME: Is there a way to avoid cloning the attributes twice here (once on their own,
977 // once as part of t.clone() above)?
978 let new_element =
979 self.insert_element(Push, ns!(html), tag.name.clone(), tag.attrs.clone());
980 self.active_formatting.borrow_mut()[entry_index] = Element(new_element, tag);
981 if entry_index == self.active_formatting.borrow().len() - 1 {
982 break;
983 }
984 entry_index += 1;
985 }
986 }
987
988 /// Get the first element on the stack, which will be the <html> element.
989 fn html_elem(&self) -> Ref<Handle> {
990 Ref::map(self.open_elems.borrow(), |elems| &elems[0])
991 }
992
993 /// Get the second element on the stack, if it's a HTML body element.
994 fn body_elem(&self) -> Option<Ref<Handle>> {
995 if self.open_elems.borrow().len() <= 1 {
996 return None;
997 }
998
999 let node = Ref::map(self.open_elems.borrow(), |elems| &elems[1]);
1000 if self.html_elem_named(&node, local_name!("body")) {
1001 Some(node)
1002 } else {
1003 None
1004 }
1005 }
1006
1007 /// Signal an error depending on the state of the stack of open elements at
1008 /// the end of the body.
1009 fn check_body_end(&self) {
1010 declare_tag_set!(body_end_ok =
1011 "dd" "dt" "li" "optgroup" "option" "p" "rp" "rt" "tbody" "td" "tfoot" "th"
1012 "thead" "tr" "body" "html");
1013
1014 for elem in self.open_elems.borrow().iter() {
1015 let error;
1016 {
1017 let elem_name = self.sink.elem_name(elem);
1018 let name = elem_name.expanded();
1019 if body_end_ok(name) {
1020 continue;
1021 }
1022 error = format_if!(
1023 self.opts.exact_errors,
1024 "Unexpected open tag at end of body",
1025 "Unexpected open tag {:?} at end of body",
1026 name
1027 );
1028 }
1029 self.sink.parse_error(error);
1030 // FIXME: Do we keep checking after finding one bad tag?
1031 // The spec suggests not.
1032 return;
1033 }
1034 }
1035
1036 fn in_scope<TagSet, Pred>(&self, scope: TagSet, pred: Pred) -> bool
1037 where
1038 TagSet: Fn(ExpandedName) -> bool,
1039 Pred: Fn(Handle) -> bool,
1040 {
1041 for node in self.open_elems.borrow().iter().rev() {
1042 if pred(node.clone()) {
1043 return true;
1044 }
1045 if scope(self.sink.elem_name(node).expanded()) {
1046 return false;
1047 }
1048 }
1049
1050 // supposed to be impossible, because <html> is always in scope
1051
1052 false
1053 }
1054
1055 fn elem_in<TagSet>(&self, elem: &Handle, set: TagSet) -> bool
1056 where
1057 TagSet: Fn(ExpandedName) -> bool,
1058 {
1059 set(self.sink.elem_name(elem).expanded())
1060 }
1061
1062 fn html_elem_named(&self, elem: &Handle, name: LocalName) -> bool {
1063 let elem_name = self.sink.elem_name(elem);
1064 *elem_name.ns() == ns!(html) && *elem_name.local_name() == name
1065 }
1066
1067 fn in_html_elem_named(&self, name: LocalName) -> bool {
1068 self.open_elems
1069 .borrow()
1070 .iter()
1071 .any(|elem| self.html_elem_named(elem, name.clone()))
1072 }
1073
1074 fn current_node_named(&self, name: LocalName) -> bool {
1075 self.html_elem_named(&self.current_node(), name)
1076 }
1077
1078 fn in_scope_named<TagSet>(&self, scope: TagSet, name: LocalName) -> bool
1079 where
1080 TagSet: Fn(ExpandedName) -> bool,
1081 {
1082 self.in_scope(scope, |elem| self.html_elem_named(&elem, name.clone()))
1083 }
1084
1085 //§ closing-elements-that-have-implied-end-tags
1086 fn generate_implied_end<TagSet>(&self, set: TagSet)
1087 where
1088 TagSet: Fn(ExpandedName) -> bool,
1089 {
1090 loop {
1091 {
1092 let open_elems = self.open_elems.borrow();
1093 let elem = unwrap_or_return!(open_elems.last());
1094 let elem_name = self.sink.elem_name(elem);
1095 if !set(elem_name.expanded()) {
1096 return;
1097 }
1098 }
1099 self.pop();
1100 }
1101 }
1102
1103 fn generate_implied_end_except(&self, except: LocalName) {
1104 self.generate_implied_end(|p| {
1105 if *p.ns == ns!(html) && *p.local == except {
1106 false
1107 } else {
1108 cursory_implied_end(p)
1109 }
1110 });
1111 }
1112 //§ END
1113
1114 // Pop elements until the current element is in the set.
1115 fn pop_until_current<TagSet>(&self, tag_set: TagSet)
1116 where
1117 TagSet: Fn(ExpandedName) -> bool,
1118 {
1119 while !self.current_node_in(&tag_set) {
1120 self.open_elems.borrow_mut().pop();
1121 }
1122 }
1123
1124 // Pop elements until an element from the set has been popped. Returns the
1125 // number of elements popped.
1126 fn pop_until<P>(&self, pred: P) -> usize
1127 where
1128 P: Fn(ExpandedName) -> bool,
1129 {
1130 let mut n = 0;
1131 loop {
1132 n += 1;
1133 match self.open_elems.borrow_mut().pop() {
1134 None => break,
1135 Some(elem) => {
1136 if pred(self.sink.elem_name(&elem).expanded()) {
1137 break;
1138 }
1139 },
1140 }
1141 }
1142 n
1143 }
1144
1145 fn pop_until_named(&self, name: LocalName) -> usize {
1146 self.pop_until(|p| *p.ns == ns!(html) && *p.local == name)
1147 }
1148
1149 // Pop elements until one with the specified name has been popped.
1150 // Signal an error if it was not the first one.
1151 fn expect_to_close(&self, name: LocalName) {
1152 if self.pop_until_named(name.clone()) != 1 {
1153 self.sink.parse_error(format_if!(
1154 self.opts.exact_errors,
1155 "Unexpected open element",
1156 "Unexpected open element while closing {:?}",
1157 name
1158 ));
1159 }
1160 }
1161
1162 fn close_p_element(&self) {
1163 declare_tag_set!(implied = [cursory_implied_end] - "p");
1164 self.generate_implied_end(implied);
1165 self.expect_to_close(local_name!("p"));
1166 }
1167
1168 fn close_p_element_in_button_scope(&self) {
1169 if self.in_scope_named(button_scope, local_name!("p")) {
1170 self.close_p_element();
1171 }
1172 }
1173
1174 // Check <input> tags for type=hidden
1175 fn is_type_hidden(&self, tag: &Tag) -> bool {
1176 match tag
1177 .attrs
1178 .iter()
1179 .find(|&at| at.name.expanded() == expanded_name!("", "type"))
1180 {
1181 None => false,
1182 Some(at) => at.value.eq_ignore_ascii_case("hidden"),
1183 }
1184 }
1185
1186 fn foster_parent_in_body(&self, token: Token) -> ProcessResult<Handle> {
1187 warn!("foster parenting not implemented");
1188 self.foster_parenting.set(true);
1189 let res = self.step(InBody, token);
1190 // FIXME: what if res is Reprocess?
1191 self.foster_parenting.set(false);
1192 res
1193 }
1194
1195 fn process_chars_in_table(&self, token: Token) -> ProcessResult<Handle> {
1196 declare_tag_set!(table_outer = "table" "tbody" "tfoot" "thead" "tr");
1197 if self.current_node_in(table_outer) {
1198 assert!(self.pending_table_text.borrow().is_empty());
1199 self.orig_mode.set(Some(self.mode.get()));
1200 Reprocess(InTableText, token)
1201 } else {
1202 self.sink.parse_error(format_if!(
1203 self.opts.exact_errors,
1204 "Unexpected characters in table",
1205 "Unexpected characters {} in table",
1206 to_escaped_string(&token)
1207 ));
1208 self.foster_parent_in_body(token)
1209 }
1210 }
1211
1212 // https://html.spec.whatwg.org/multipage/#reset-the-insertion-mode-appropriately
1213 fn reset_insertion_mode(&self) -> InsertionMode {
1214 let open_elems = self.open_elems.borrow();
1215 for (i, mut node) in open_elems.iter().enumerate().rev() {
1216 let last = i == 0usize;
1217 let context_elem = self.context_elem.borrow();
1218 if let (true, Some(ctx)) = (last, context_elem.as_ref()) {
1219 node = ctx;
1220 }
1221 let elem_name = self.sink.elem_name(node);
1222 let name = match elem_name.expanded() {
1223 ExpandedName {
1224 ns: &ns!(html),
1225 local,
1226 } => local,
1227 _ => continue,
1228 };
1229 match *name {
1230 local_name!("select") => {
1231 for ancestor in self.open_elems.borrow()[0..i].iter().rev() {
1232 if self.html_elem_named(ancestor, local_name!("template")) {
1233 return InSelect;
1234 } else if self.html_elem_named(ancestor, local_name!("table")) {
1235 return InSelectInTable;
1236 }
1237 }
1238 return InSelect;
1239 },
1240 local_name!("td") | local_name!("th") => {
1241 if !last {
1242 return InCell;
1243 }
1244 },
1245 local_name!("tr") => return InRow,
1246 local_name!("tbody") | local_name!("thead") | local_name!("tfoot") => {
1247 return InTableBody;
1248 },
1249 local_name!("caption") => return InCaption,
1250 local_name!("colgroup") => return InColumnGroup,
1251 local_name!("table") => return InTable,
1252 local_name!("template") => return *self.template_modes.borrow().last().unwrap(),
1253 local_name!("head") => {
1254 if !last {
1255 return InHead;
1256 }
1257 },
1258 local_name!("body") => return InBody,
1259 local_name!("frameset") => return InFrameset,
1260 local_name!("html") => match *self.head_elem.borrow() {
1261 None => return BeforeHead,
1262 Some(_) => return AfterHead,
1263 },
1264
1265 _ => (),
1266 }
1267 }
1268 InBody
1269 }
1270
1271 fn close_the_cell(&self) {
1272 self.generate_implied_end(cursory_implied_end);
1273 if self.pop_until(td_th) != 1 {
1274 self.sink
1275 .parse_error(Borrowed("expected to close <td> or <th> with cell"));
1276 }
1277 self.clear_active_formatting_to_marker();
1278 }
1279
1280 fn append_text(&self, text: StrTendril) -> ProcessResult<Handle> {
1281 self.insert_appropriately(AppendText(text), None);
1282 Done
1283 }
1284
1285 fn append_comment(&self, text: StrTendril) -> ProcessResult<Handle> {
1286 let comment = self.sink.create_comment(text);
1287 self.insert_appropriately(AppendNode(comment), None);
1288 Done
1289 }
1290
1291 fn append_comment_to_doc(&self, text: StrTendril) -> ProcessResult<Handle> {
1292 let comment = self.sink.create_comment(text);
1293 self.sink.append(&self.doc_handle, AppendNode(comment));
1294 Done
1295 }
1296
1297 fn append_comment_to_html(&self, text: StrTendril) -> ProcessResult<Handle> {
1298 let open_elems = self.open_elems.borrow();
1299 let target = html_elem(&open_elems);
1300 let comment = self.sink.create_comment(text);
1301 self.sink.append(target, AppendNode(comment));
1302 Done
1303 }
1304
1305 //§ creating-and-inserting-nodes
1306 fn create_root(&self, attrs: Vec<Attribute>) {
1307 let elem = create_element(
1308 &self.sink,
1309 QualName::new(None, ns!(html), local_name!("html")),
1310 attrs,
1311 );
1312 self.push(&elem);
1313 self.sink.append(&self.doc_handle, AppendNode(elem));
1314 // FIXME: application cache selection algorithm
1315 }
1316
1317 // https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
1318 fn insert_element(
1319 &self,
1320 push: PushFlag,
1321 ns: Namespace,
1322 name: LocalName,
1323 attrs: Vec<Attribute>,
1324 ) -> Handle {
1325 declare_tag_set!(form_associatable =
1326 "button" "fieldset" "input" "object"
1327 "output" "select" "textarea" "img");
1328
1329 declare_tag_set!(listed = [form_associatable] - "img");
1330
1331 // Step 7.
1332 let qname = QualName::new(None, ns, name);
1333 let elem = create_element(&self.sink, qname.clone(), attrs.clone());
1334
1335 let insertion_point = self.appropriate_place_for_insertion(None);
1336 let (node1, node2) = match insertion_point {
1337 LastChild(ref p) | BeforeSibling(ref p) => (p.clone(), None),
1338 TableFosterParenting {
1339 ref element,
1340 ref prev_element,
1341 } => (element.clone(), Some(prev_element.clone())),
1342 };
1343
1344 // Step 12.
1345 if form_associatable(qname.expanded())
1346 && self.form_elem.borrow().is_some()
1347 && !self.in_html_elem_named(local_name!("template"))
1348 && !(listed(qname.expanded())
1349 && attrs
1350 .iter()
1351 .any(|a| a.name.expanded() == expanded_name!("", "form")))
1352 {
1353 let form = self.form_elem.borrow().as_ref().unwrap().clone();
1354 self.sink
1355 .associate_with_form(&elem, &form, (&node1, node2.as_ref()));
1356 }
1357
1358 self.insert_at(insertion_point, AppendNode(elem.clone()));
1359
1360 match push {
1361 Push => self.push(&elem),
1362 NoPush => (),
1363 }
1364 // FIXME: Remove from the stack if we can't append?
1365 elem
1366 }
1367
1368 fn insert_element_for(&self, tag: Tag) -> Handle {
1369 self.insert_element(Push, ns!(html), tag.name, tag.attrs)
1370 }
1371
1372 fn insert_and_pop_element_for(&self, tag: Tag) -> Handle {
1373 self.insert_element(NoPush, ns!(html), tag.name, tag.attrs)
1374 }
1375
1376 fn insert_phantom(&self, name: LocalName) -> Handle {
1377 self.insert_element(Push, ns!(html), name, vec![])
1378 }
1379
1380 /// <https://html.spec.whatwg.org/multipage/parsing.html#insert-an-element-at-the-adjusted-insertion-location>
1381 fn insert_foreign_element(
1382 &self,
1383 tag: Tag,
1384 ns: Namespace,
1385 only_add_to_element_stack: bool,
1386 ) -> Handle {
1387 let adjusted_insertion_location = self.appropriate_place_for_insertion(None);
1388 let qname = QualName::new(None, ns, tag.name);
1389 let elem = create_element(&self.sink, qname.clone(), tag.attrs.clone());
1390
1391 if !only_add_to_element_stack {
1392 self.insert_at(adjusted_insertion_location, AppendNode(elem.clone()));
1393 }
1394
1395 self.push(&elem);
1396
1397 elem
1398 }
1399 //§ END
1400
1401 /// <https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead>
1402 ///
1403 /// A start tag whose tag name is "template"
1404 fn should_attach_declarative_shadow(&self, tag: &Tag) -> bool {
1405 let adjusted_insertion_location = self.appropriate_place_for_insertion(None);
1406
1407 let (intended_parent, _node2) = match adjusted_insertion_location {
1408 LastChild(ref p) | BeforeSibling(ref p) => (p.clone(), None),
1409 TableFosterParenting {
1410 ref element,
1411 ref prev_element,
1412 } => (element.clone(), Some(prev_element.clone())),
1413 };
1414
1415 // template start tag's shadowrootmode is not in the none state
1416 let is_shadow_root_mode = tag.attrs.iter().any(|attr| {
1417 attr.name.local == local_name!("shadowrootmode")
1418 && (attr.value.as_ref() == "open" || attr.value.as_ref() == "closed")
1419 });
1420
1421 // Check if intended_parent's document allows declarative shadow roots
1422 let allow_declarative_shadow_roots =
1423 self.sink.allow_declarative_shadow_roots(&intended_parent);
1424
1425 // the adjusted current node is not the topmost element in the stack of open elements
1426 let adjusted_current_node_not_topmost = match self.open_elems.borrow().first() {
1427 // The stack grows downwards; the topmost node on the stack is the first one added to the stack
1428 // The current node is the bottommost node in this stack of open elements.
1429 //
1430 // (1) The adjusted current node is the context element if the parser was created as part of the HTML fragment parsing algorithm
1431 // and the stack of open elements has only one element in it (fragment case);
1432 // (2) otherwise, the adjusted current node is the current node (the bottomost node)
1433 //
1434 // => adjusted current node != topmost element in the stack when the stack size > 1
1435 Some(_) => self.open_elems.borrow().len() > 1,
1436 None => true,
1437 };
1438
1439 is_shadow_root_mode && allow_declarative_shadow_roots && adjusted_current_node_not_topmost
1440 }
1441
1442 /// <https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead>
1443 ///
1444 /// A start tag whose tag name is "template"
1445 fn attach_declarative_shadow(
1446 &self,
1447 tag: &Tag,
1448 shadow_host: &Handle,
1449 template: &Handle,
1450 ) -> Result<(), String> {
1451 self.sink
1452 .attach_declarative_shadow(shadow_host, template, tag.attrs.clone())
1453 }
1454
1455 fn create_formatting_element_for(&self, tag: Tag) -> Handle {
1456 // FIXME: This really wants unit tests.
1457 let mut first_match = None;
1458 let mut matches = 0usize;
1459 for (i, _, old_tag) in self.active_formatting_end_to_marker().iter() {
1460 if tag.equiv_modulo_attr_order(old_tag) {
1461 first_match = Some(i);
1462 matches += 1;
1463 }
1464 }
1465
1466 if matches >= 3 {
1467 self.active_formatting
1468 .borrow_mut()
1469 .remove(first_match.expect("matches with no index"));
1470 }
1471
1472 let elem = self.insert_element(Push, ns!(html), tag.name.clone(), tag.attrs.clone());
1473 self.active_formatting
1474 .borrow_mut()
1475 .push(Element(elem.clone(), tag));
1476 elem
1477 }
1478
1479 fn clear_active_formatting_to_marker(&self) {
1480 loop {
1481 match self.active_formatting.borrow_mut().pop() {
1482 None | Some(Marker) => break,
1483 _ => (),
1484 }
1485 }
1486 }
1487
1488 fn process_end_tag_in_body(&self, tag: Tag) {
1489 // Look back for a matching open element.
1490 let mut match_idx = None;
1491 for (i, elem) in self.open_elems.borrow().iter().enumerate().rev() {
1492 if self.html_elem_named(elem, tag.name.clone()) {
1493 match_idx = Some(i);
1494 break;
1495 }
1496
1497 if self.elem_in(elem, special_tag) {
1498 self.sink
1499 .parse_error(Borrowed("Found special tag while closing generic tag"));
1500 return;
1501 }
1502 }
1503
1504 // Can't use unwrap_or_return!() due to rust-lang/rust#16617.
1505 let match_idx = match match_idx {
1506 None => {
1507 // I believe this is impossible, because the root
1508 // <html> element is in special_tag.
1509 self.unexpected(&tag);
1510 return;
1511 },
1512 Some(x) => x,
1513 };
1514
1515 self.generate_implied_end_except(tag.name.clone());
1516
1517 if match_idx != self.open_elems.borrow().len() - 1 {
1518 // mis-nested tags
1519 self.unexpected(&tag);
1520 }
1521 self.open_elems.borrow_mut().truncate(match_idx);
1522 }
1523
1524 fn handle_misnested_a_tags(&self, tag: &Tag) {
1525 let node = unwrap_or_return!(self
1526 .active_formatting_end_to_marker()
1527 .iter()
1528 .find(|&(_, n, _)| self.html_elem_named(n, local_name!("a")))
1529 .map(|(_, n, _)| n.clone()));
1530
1531 self.unexpected(tag);
1532 self.adoption_agency(local_name!("a"));
1533 self.position_in_active_formatting(&node)
1534 .map(|index| self.active_formatting.borrow_mut().remove(index));
1535 self.remove_from_stack(&node);
1536 }
1537
1538 //§ tree-construction
1539 fn is_foreign(&self, token: &Token) -> bool {
1540 if let EOFToken = *token {
1541 return false;
1542 }
1543
1544 if self.open_elems.borrow().is_empty() {
1545 return false;
1546 }
1547
1548 let current = self.adjusted_current_node();
1549 let elem_name = self.sink.elem_name(&current);
1550 let name = elem_name.expanded();
1551 if let ns!(html) = *name.ns {
1552 return false;
1553 }
1554
1555 if mathml_text_integration_point(name) {
1556 match *token {
1557 CharacterTokens(..) | NullCharacterToken => return false,
1558 TagToken(Tag {
1559 kind: StartTag,
1560 ref name,
1561 ..
1562 }) if !matches!(*name, local_name!("mglyph") | local_name!("malignmark")) => {
1563 return false;
1564 },
1565 _ => (),
1566 }
1567 }
1568
1569 if svg_html_integration_point(name) {
1570 match *token {
1571 CharacterTokens(..) | NullCharacterToken => return false,
1572 TagToken(Tag { kind: StartTag, .. }) => return false,
1573 _ => (),
1574 }
1575 }
1576
1577 if let expanded_name!(mathml "annotation-xml") = name {
1578 match *token {
1579 TagToken(Tag {
1580 kind: StartTag,
1581 name: local_name!("svg"),
1582 ..
1583 }) => return false,
1584 CharacterTokens(..) | NullCharacterToken | TagToken(Tag { kind: StartTag, .. }) => {
1585 return !self
1586 .sink
1587 .is_mathml_annotation_xml_integration_point(&self.adjusted_current_node());
1588 },
1589 _ => {},
1590 };
1591 }
1592
1593 true
1594 }
1595 //§ END
1596
1597 fn enter_foreign(&self, mut tag: Tag, ns: Namespace) -> ProcessResult<Handle> {
1598 match ns {
1599 ns!(mathml) => self.adjust_mathml_attributes(&mut tag),
1600 ns!(svg) => self.adjust_svg_attributes(&mut tag),
1601 _ => (),
1602 }
1603 self.adjust_foreign_attributes(&mut tag);
1604
1605 if tag.self_closing {
1606 self.insert_element(NoPush, ns, tag.name, tag.attrs);
1607 DoneAckSelfClosing
1608 } else {
1609 self.insert_element(Push, ns, tag.name, tag.attrs);
1610 Done
1611 }
1612 }
1613
1614 fn adjust_svg_tag_name(&self, tag: &mut Tag) {
1615 let Tag { ref mut name, .. } = *tag;
1616 match *name {
1617 local_name!("altglyph") => *name = local_name!("altGlyph"),
1618 local_name!("altglyphdef") => *name = local_name!("altGlyphDef"),
1619 local_name!("altglyphitem") => *name = local_name!("altGlyphItem"),
1620 local_name!("animatecolor") => *name = local_name!("animateColor"),
1621 local_name!("animatemotion") => *name = local_name!("animateMotion"),
1622 local_name!("animatetransform") => *name = local_name!("animateTransform"),
1623 local_name!("clippath") => *name = local_name!("clipPath"),
1624 local_name!("feblend") => *name = local_name!("feBlend"),
1625 local_name!("fecolormatrix") => *name = local_name!("feColorMatrix"),
1626 local_name!("fecomponenttransfer") => *name = local_name!("feComponentTransfer"),
1627 local_name!("fecomposite") => *name = local_name!("feComposite"),
1628 local_name!("feconvolvematrix") => *name = local_name!("feConvolveMatrix"),
1629 local_name!("fediffuselighting") => *name = local_name!("feDiffuseLighting"),
1630 local_name!("fedisplacementmap") => *name = local_name!("feDisplacementMap"),
1631 local_name!("fedistantlight") => *name = local_name!("feDistantLight"),
1632 local_name!("fedropshadow") => *name = local_name!("feDropShadow"),
1633 local_name!("feflood") => *name = local_name!("feFlood"),
1634 local_name!("fefunca") => *name = local_name!("feFuncA"),
1635 local_name!("fefuncb") => *name = local_name!("feFuncB"),
1636 local_name!("fefuncg") => *name = local_name!("feFuncG"),
1637 local_name!("fefuncr") => *name = local_name!("feFuncR"),
1638 local_name!("fegaussianblur") => *name = local_name!("feGaussianBlur"),
1639 local_name!("feimage") => *name = local_name!("feImage"),
1640 local_name!("femerge") => *name = local_name!("feMerge"),
1641 local_name!("femergenode") => *name = local_name!("feMergeNode"),
1642 local_name!("femorphology") => *name = local_name!("feMorphology"),
1643 local_name!("feoffset") => *name = local_name!("feOffset"),
1644 local_name!("fepointlight") => *name = local_name!("fePointLight"),
1645 local_name!("fespecularlighting") => *name = local_name!("feSpecularLighting"),
1646 local_name!("fespotlight") => *name = local_name!("feSpotLight"),
1647 local_name!("fetile") => *name = local_name!("feTile"),
1648 local_name!("feturbulence") => *name = local_name!("feTurbulence"),
1649 local_name!("foreignobject") => *name = local_name!("foreignObject"),
1650 local_name!("glyphref") => *name = local_name!("glyphRef"),
1651 local_name!("lineargradient") => *name = local_name!("linearGradient"),
1652 local_name!("radialgradient") => *name = local_name!("radialGradient"),
1653 local_name!("textpath") => *name = local_name!("textPath"),
1654 _ => (),
1655 }
1656 }
1657
1658 fn adjust_attributes<F>(&self, tag: &mut Tag, mut map: F)
1659 where
1660 F: FnMut(LocalName) -> Option<QualName>,
1661 {
1662 for &mut Attribute { ref mut name, .. } in &mut tag.attrs {
1663 if let Some(replacement) = map(name.local.clone()) {
1664 *name = replacement;
1665 }
1666 }
1667 }
1668
1669 fn adjust_svg_attributes(&self, tag: &mut Tag) {
1670 self.adjust_attributes(tag, |k| match k {
1671 local_name!("attributename") => Some(qualname!("", "attributeName")),
1672 local_name!("attributetype") => Some(qualname!("", "attributeType")),
1673 local_name!("basefrequency") => Some(qualname!("", "baseFrequency")),
1674 local_name!("baseprofile") => Some(qualname!("", "baseProfile")),
1675 local_name!("calcmode") => Some(qualname!("", "calcMode")),
1676 local_name!("clippathunits") => Some(qualname!("", "clipPathUnits")),
1677 local_name!("diffuseconstant") => Some(qualname!("", "diffuseConstant")),
1678 local_name!("edgemode") => Some(qualname!("", "edgeMode")),
1679 local_name!("filterunits") => Some(qualname!("", "filterUnits")),
1680 local_name!("glyphref") => Some(qualname!("", "glyphRef")),
1681 local_name!("gradienttransform") => Some(qualname!("", "gradientTransform")),
1682 local_name!("gradientunits") => Some(qualname!("", "gradientUnits")),
1683 local_name!("kernelmatrix") => Some(qualname!("", "kernelMatrix")),
1684 local_name!("kernelunitlength") => Some(qualname!("", "kernelUnitLength")),
1685 local_name!("keypoints") => Some(qualname!("", "keyPoints")),
1686 local_name!("keysplines") => Some(qualname!("", "keySplines")),
1687 local_name!("keytimes") => Some(qualname!("", "keyTimes")),
1688 local_name!("lengthadjust") => Some(qualname!("", "lengthAdjust")),
1689 local_name!("limitingconeangle") => Some(qualname!("", "limitingConeAngle")),
1690 local_name!("markerheight") => Some(qualname!("", "markerHeight")),
1691 local_name!("markerunits") => Some(qualname!("", "markerUnits")),
1692 local_name!("markerwidth") => Some(qualname!("", "markerWidth")),
1693 local_name!("maskcontentunits") => Some(qualname!("", "maskContentUnits")),
1694 local_name!("maskunits") => Some(qualname!("", "maskUnits")),
1695 local_name!("numoctaves") => Some(qualname!("", "numOctaves")),
1696 local_name!("pathlength") => Some(qualname!("", "pathLength")),
1697 local_name!("patterncontentunits") => Some(qualname!("", "patternContentUnits")),
1698 local_name!("patterntransform") => Some(qualname!("", "patternTransform")),
1699 local_name!("patternunits") => Some(qualname!("", "patternUnits")),
1700 local_name!("pointsatx") => Some(qualname!("", "pointsAtX")),
1701 local_name!("pointsaty") => Some(qualname!("", "pointsAtY")),
1702 local_name!("pointsatz") => Some(qualname!("", "pointsAtZ")),
1703 local_name!("preservealpha") => Some(qualname!("", "preserveAlpha")),
1704 local_name!("preserveaspectratio") => Some(qualname!("", "preserveAspectRatio")),
1705 local_name!("primitiveunits") => Some(qualname!("", "primitiveUnits")),
1706 local_name!("refx") => Some(qualname!("", "refX")),
1707 local_name!("refy") => Some(qualname!("", "refY")),
1708 local_name!("repeatcount") => Some(qualname!("", "repeatCount")),
1709 local_name!("repeatdur") => Some(qualname!("", "repeatDur")),
1710 local_name!("requiredextensions") => Some(qualname!("", "requiredExtensions")),
1711 local_name!("requiredfeatures") => Some(qualname!("", "requiredFeatures")),
1712 local_name!("specularconstant") => Some(qualname!("", "specularConstant")),
1713 local_name!("specularexponent") => Some(qualname!("", "specularExponent")),
1714 local_name!("spreadmethod") => Some(qualname!("", "spreadMethod")),
1715 local_name!("startoffset") => Some(qualname!("", "startOffset")),
1716 local_name!("stddeviation") => Some(qualname!("", "stdDeviation")),
1717 local_name!("stitchtiles") => Some(qualname!("", "stitchTiles")),
1718 local_name!("surfacescale") => Some(qualname!("", "surfaceScale")),
1719 local_name!("systemlanguage") => Some(qualname!("", "systemLanguage")),
1720 local_name!("tablevalues") => Some(qualname!("", "tableValues")),
1721 local_name!("targetx") => Some(qualname!("", "targetX")),
1722 local_name!("targety") => Some(qualname!("", "targetY")),
1723 local_name!("textlength") => Some(qualname!("", "textLength")),
1724 local_name!("viewbox") => Some(qualname!("", "viewBox")),
1725 local_name!("viewtarget") => Some(qualname!("", "viewTarget")),
1726 local_name!("xchannelselector") => Some(qualname!("", "xChannelSelector")),
1727 local_name!("ychannelselector") => Some(qualname!("", "yChannelSelector")),
1728 local_name!("zoomandpan") => Some(qualname!("", "zoomAndPan")),
1729 _ => None,
1730 });
1731 }
1732
1733 fn adjust_mathml_attributes(&self, tag: &mut Tag) {
1734 self.adjust_attributes(tag, |k| match k {
1735 local_name!("definitionurl") => Some(qualname!("", "definitionURL")),
1736 _ => None,
1737 });
1738 }
1739
1740 fn adjust_foreign_attributes(&self, tag: &mut Tag) {
1741 self.adjust_attributes(tag, |k| match k {
1742 local_name!("xlink:actuate") => Some(qualname!("xlink" xlink "actuate")),
1743 local_name!("xlink:arcrole") => Some(qualname!("xlink" xlink "arcrole")),
1744 local_name!("xlink:href") => Some(qualname!("xlink" xlink "href")),
1745 local_name!("xlink:role") => Some(qualname!("xlink" xlink "role")),
1746 local_name!("xlink:show") => Some(qualname!("xlink" xlink "show")),
1747 local_name!("xlink:title") => Some(qualname!("xlink" xlink "title")),
1748 local_name!("xlink:type") => Some(qualname!("xlink" xlink "type")),
1749 local_name!("xml:lang") => Some(qualname!("xml" xml "lang")),
1750 local_name!("xml:space") => Some(qualname!("xml" xml "space")),
1751 local_name!("xmlns") => Some(qualname!("" xmlns "xmlns")),
1752 local_name!("xmlns:xlink") => Some(qualname!("xmlns" xmlns "xlink")),
1753 _ => None,
1754 });
1755 }
1756
1757 fn foreign_start_tag(&self, mut tag: Tag) -> ProcessResult<Handle> {
1758 let current_ns = self
1759 .sink
1760 .elem_name(&self.adjusted_current_node())
1761 .ns()
1762 .clone();
1763 match current_ns {
1764 ns!(mathml) => self.adjust_mathml_attributes(&mut tag),
1765 ns!(svg) => {
1766 self.adjust_svg_tag_name(&mut tag);
1767 self.adjust_svg_attributes(&mut tag);
1768 },
1769 _ => (),
1770 }
1771 self.adjust_foreign_attributes(&mut tag);
1772 if tag.self_closing {
1773 // FIXME(#118): <script /> in SVG
1774 self.insert_element(NoPush, current_ns, tag.name, tag.attrs);
1775 DoneAckSelfClosing
1776 } else {
1777 self.insert_element(Push, current_ns, tag.name, tag.attrs);
1778 Done
1779 }
1780 }
1781
1782 fn unexpected_start_tag_in_foreign_content(&self, tag: Tag) -> ProcessResult<Handle> {
1783 self.unexpected(&tag);
1784 while !self.current_node_in(|n| {
1785 *n.ns == ns!(html) || mathml_text_integration_point(n) || svg_html_integration_point(n)
1786 }) {
1787 self.pop();
1788 }
1789 self.step(self.mode.get(), TagToken(tag))
1790 }
1791}
1792