html5ever/tree_builder/
rules.rs

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 tree builder rules, as a single, enormous nested match expression.
11
12use crate::encoding::extract_a_character_encoding_from_a_meta_element;
13use crate::interface::Quirks;
14use crate::tokenizer::states::{Rawtext, Rcdata, ScriptData};
15use crate::tokenizer::TagKind::{EndTag, StartTag};
16use crate::tree_builder::tag_sets::*;
17use crate::tree_builder::types::*;
18use crate::tree_builder::{
19    html_elem, ElemName, NodeOrText::AppendNode, StrTendril, Tag, TreeBuilder, TreeSink,
20};
21use crate::QualName;
22use markup5ever::interface::tree_builder::create_element_with_flags;
23use markup5ever::{expanded_name, local_name, ns};
24use std::borrow::Cow::Borrowed;
25
26use crate::tendril::SliceExt;
27
28fn any_not_whitespace(x: &StrTendril) -> bool {
29    // FIXME: this might be much faster as a byte scan
30    x.chars().any(|c| !c.is_ascii_whitespace())
31}
32
33fn current_node<Handle>(open_elems: &[Handle]) -> &Handle {
34    open_elems.last().expect("no current element")
35}
36
37/// Helper macro that generates a [pattern](https://doc.rust-lang.org/reference/patterns.html) representing
38/// a [`Tag`] to make matching on [`Tag`]s less verbose.
39///
40/// This macro accepts 4 forms:
41///
42/// - `tag!(<div>)` where `div` can be any valid tag name. This matches a start tag where the tag name is "div".
43///   If the tag name contains characters other than [a-zA-Z0-9_] then it should be quoted a `<"div">`.
44/// - `tag!(</div>)` where `div` can be any valid tag name. This matches a end tag where the tag name is "div".
45///   If the tag name contains characters other than [a-zA-Z0-9_] then it should be quoted a `</"div">`.
46/// - `tag!(<>)`. This matches any start tag (regardless of tag name).
47/// - `tag!(</>)`. This matches any end tag (regardless of tag name).
48///
49/// Additionally any of the above can be freely combined with `|` to create an "or" match pattern.
50/// For example `tag!(<head> | </>)` will match a "head" start tag or any end tag.
51#[rustfmt::skip]
52macro_rules! tag {
53    // Any start tag
54    (<>) => {
55        crate::tokenizer::Tag { kind: crate::tokenizer::StartTag, .. }
56    };
57    (<>|$($tail:tt)*) => {
58        tag!(<>) | tag!($($tail)*)
59    };
60
61    // Any end tag
62    (</>) => {
63        crate::tokenizer::Tag { kind: crate::tokenizer::EndTag, .. }
64    };
65    (</>|$($tail:tt)*) => {
66        tag!(</>) | tag!($($tail)*)
67    };
68
69    // Named start tag
70    (<$tag:tt>) => {
71        crate::tokenizer::Tag { kind: crate::tokenizer::StartTag, name: local_name!($tag), .. }
72    };
73    (<$tag:tt>|$($tail:tt)*) => {
74        tag!(<$tag>) | tag!($($tail)*)
75    };
76
77    // Named end tag
78    (</$tag:tt>) => {
79        crate::tokenizer::Tag { kind: crate::tokenizer::EndTag, name: local_name!($tag), .. }
80    };
81    (</$tag:tt>|$($tail:tt)*) => {
82        tag!(</$tag>) | tag!($($tail)*)
83    };
84}
85
86#[doc(hidden)]
87impl<Handle, Sink> TreeBuilder<Handle, Sink>
88where
89    Handle: Clone,
90    Sink: TreeSink<Handle = Handle>,
91{
92    /// Process an HTML token.
93    ///
94    /// <https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhtml>
95    pub(crate) fn step(&self, mode: InsertionMode, token: Token) -> ProcessResult<Handle> {
96        self.debug_step(mode, &token);
97
98        match mode {
99            // § The "initial" insertion mode
100            // https://html.spec.whatwg.org/multipage/parsing.html#the-initial-insertion-mode
101            InsertionMode::Initial => match token {
102                Token::Characters(SplitStatus::NotSplit, text) => {
103                    ProcessResult::SplitWhitespace(text)
104                },
105                Token::Characters(SplitStatus::Whitespace, _) => ProcessResult::Done,
106                Token::Comment(text) => self.append_comment_to_doc(text),
107                token => {
108                    if !self.opts.iframe_srcdoc {
109                        self.unexpected(&token);
110                        self.set_quirks_mode(Quirks);
111                    }
112                    ProcessResult::Reprocess(InsertionMode::BeforeHtml, token)
113                },
114            },
115
116            // § The "before html" insertion mode
117            // https://html.spec.whatwg.org/multipage/parsing.html#the-before-html-insertion-mode
118            InsertionMode::BeforeHtml => {
119                let anything_else = |token: Token| {
120                    self.create_root(vec![]);
121                    ProcessResult::Reprocess(InsertionMode::BeforeHead, token)
122                };
123
124                match token {
125                    Token::Comment(text) => self.append_comment_to_doc(text),
126
127                    Token::Characters(SplitStatus::NotSplit, text) => {
128                        ProcessResult::SplitWhitespace(text)
129                    },
130
131                    Token::Characters(SplitStatus::Whitespace, _) => ProcessResult::Done,
132                    Token::Tag(tag @ tag!(<html>)) => {
133                        self.create_root(tag.attrs);
134                        self.mode.set(InsertionMode::BeforeHead);
135                        ProcessResult::Done
136                    },
137
138                    Token::Tag(tag!(</head> | </body> | </html> | </br>)) => anything_else(token),
139                    Token::Tag(tag @ tag!(</>)) => self.unexpected(&tag),
140
141                    token => anything_else(token),
142                }
143            },
144
145            // § The "before head" insertion mode
146            // https://html.spec.whatwg.org/multipage/parsing.html#the-before-head-insertion-mode
147            InsertionMode::BeforeHead => {
148                let anything_else = |token: Token| {
149                    *self.head_elem.borrow_mut() = Some(self.insert_phantom(local_name!("head")));
150                    ProcessResult::Reprocess(InsertionMode::InHead, token)
151                };
152                match token {
153                    Token::Characters(SplitStatus::NotSplit, text) => {
154                        ProcessResult::SplitWhitespace(text)
155                    },
156                    Token::Characters(SplitStatus::Whitespace, _) => ProcessResult::Done,
157                    Token::Comment(text) => self.append_comment(text),
158
159                    Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
160
161                    Token::Tag(tag @ tag!(<head>)) => {
162                        *self.head_elem.borrow_mut() = Some(self.insert_element_for(tag));
163                        self.mode.set(InsertionMode::InHead);
164                        ProcessResult::Done
165                    },
166
167                    Token::Tag(tag!(</head> | </body> | </html> | </br>)) => anything_else(token),
168
169                    Token::Tag(tag @ tag!(</>)) => self.unexpected(&tag),
170
171                    token => anything_else(token),
172                }
173            },
174
175            // § The "in head" insertion mode
176            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead
177            InsertionMode::InHead => {
178                let anything_else = |token: Token| {
179                    self.pop();
180                    ProcessResult::Reprocess(InsertionMode::AfterHead, token)
181                };
182
183                match token {
184                    Token::Characters(SplitStatus::NotSplit, text) => {
185                        ProcessResult::SplitWhitespace(text)
186                    },
187                    Token::Characters(SplitStatus::Whitespace, text) => self.append_text(text),
188                    Token::Comment(text) => self.append_comment(text),
189
190                    Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
191
192                    Token::Tag(tag @ tag!(<base> | <basefont> | <bgsound> | <link> | <meta>)) => {
193                        self.insert_and_pop_element_for(tag.clone());
194
195                        // Step 1. If the element has a charset attribute, and getting an encoding from its value
196                        // results in an encoding, and the confidence is currently tentative, then change the encoding
197                        // to the resulting encoding.
198                        // NOTE: We don't verify the validity of the encoding here. If the embedder detects the
199                        // encoding to be invalid then they can safely continue spinning the tokenizer.
200                        if let Some(charset) = tag.get_attribute(&local_name!("charset")) {
201                            return ProcessResult::EncodingIndicator(charset);
202                        }
203                        // Step 2. Otherwise, if the element has an http-equiv attribute whose value is an ASCII
204                        // case-insensitive match for the string "Content-Type", and the element has a content
205                        // attribute, and applying the algorithm for extracting a character encoding from a meta
206                        // element to that attribute's value returns an encoding, and the confidence is currently
207                        // tentative, then change the encoding to the extracted encoding.
208                        else if tag
209                            .get_attribute(&local_name!("http-equiv"))
210                            .is_some_and(|value| value.eq_ignore_ascii_case("content-type"))
211                        {
212                            if let Some(encoding) = tag
213                                .get_attribute(&local_name!("content"))
214                                .and_then(extract_a_character_encoding_from_a_meta_element)
215                            {
216                                return ProcessResult::EncodingIndicator(encoding);
217                            }
218                        }
219                        ProcessResult::DoneAckSelfClosing
220                    },
221
222                    Token::Tag(tag @ tag!(<title>)) => self.parse_raw_data(tag, Rcdata),
223
224                    Token::Tag(tag @ tag!(<noframes> | <style> | <noscript>)) => {
225                        if (!self.opts.scripting_enabled) && (tag.name == local_name!("noscript")) {
226                            self.insert_element_for(tag);
227                            self.mode.set(InsertionMode::InHeadNoscript);
228                            ProcessResult::Done
229                        } else {
230                            self.parse_raw_data(tag, Rawtext)
231                        }
232                    },
233
234                    Token::Tag(tag @ tag!(<script>)) => {
235                        let elem = create_element_with_flags(
236                            &self.sink,
237                            QualName::new(None, ns!(html), local_name!("script")),
238                            tag.attrs,
239                            tag.had_duplicate_attributes,
240                        );
241                        if self.is_fragment() {
242                            self.sink.mark_script_already_started(&elem);
243                        }
244                        self.insert_appropriately(AppendNode(elem.clone()), None);
245                        self.open_elems.borrow_mut().push(elem);
246                        self.to_raw_text_mode(ScriptData)
247                    },
248
249                    Token::Tag(tag!(</head>)) => {
250                        self.pop();
251                        self.mode.set(InsertionMode::AfterHead);
252                        ProcessResult::Done
253                    },
254
255                    Token::Tag(tag!(</body> | </html> | </br>)) => anything_else(token),
256
257                    Token::Tag(tag @ tag!(<template>)) => {
258                        self.active_formatting
259                            .borrow_mut()
260                            .push(FormatEntry::Marker);
261                        self.frameset_ok.set(false);
262                        self.mode.set(InsertionMode::InTemplate);
263                        self.template_modes
264                            .borrow_mut()
265                            .push(InsertionMode::InTemplate);
266
267                        if (self.should_attach_declarative_shadow(&tag)) {
268                            // Attach shadow path
269
270                            // Step 1. Let declarative shadow host element be adjusted current node.
271                            let mut shadow_host = self.open_elems.borrow().last().unwrap().clone();
272                            if self.is_fragment() && self.open_elems.borrow().len() == 1 {
273                                shadow_host = self.context_elem.borrow().clone().unwrap();
274                            }
275
276                            // Step 2. Let template be the result of insert a foreign element for template start tag, with HTML namespace and true.
277                            let template =
278                                self.insert_foreign_element(tag.clone(), ns!(html), true);
279
280                            // Step 3 - 8.
281                            // Attach a shadow root with declarative shadow host element, mode, clonable, serializable, delegatesFocus, and "named".
282                            let succeeded =
283                                self.attach_declarative_shadow(&tag, &shadow_host, &template);
284                            if !succeeded {
285                                // Step 8.1.1. Insert an element at the adjusted insertion location with template.
286                                // Pop the current template element created in step 2 first.
287                                self.pop();
288                                self.insert_element_for(tag);
289                            }
290                        } else {
291                            self.insert_element_for(tag);
292                        }
293
294                        ProcessResult::Done
295                    },
296
297                    Token::Tag(tag @ tag!(</template>)) => {
298                        if !self.in_html_elem_named(local_name!("template")) {
299                            self.unexpected(&tag);
300                        } else {
301                            self.generate_implied_end_tags(thorough_implied_end);
302                            self.expect_to_close(local_name!("template"));
303                            self.clear_active_formatting_to_marker();
304                            self.template_modes.borrow_mut().pop();
305                            self.mode.set(self.reset_insertion_mode());
306                        }
307                        ProcessResult::Done
308                    },
309
310                    Token::Tag(tag!(<head> | </>)) => self.unexpected(&token),
311
312                    token => anything_else(token),
313                }
314            },
315
316            // § The "in head noscript" insertion mode
317            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inheadnoscript
318            InsertionMode::InHeadNoscript => {
319                let anything_else = |token: Token| {
320                    self.unexpected(&token);
321                    self.pop();
322                    ProcessResult::Reprocess(InsertionMode::InHead, token)
323                };
324                match token {
325                    Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
326
327                    Token::Tag(tag!(</noscript>)) => {
328                        self.pop();
329                        self.mode.set(InsertionMode::InHead);
330                        ProcessResult::Done
331                    },
332
333                    Token::Characters(SplitStatus::NotSplit, text) => {
334                        ProcessResult::SplitWhitespace(text)
335                    },
336                    Token::Characters(SplitStatus::Whitespace, _) => {
337                        self.step(InsertionMode::InHead, token)
338                    },
339
340                    Token::Comment(_) => self.step(InsertionMode::InHead, token),
341
342                    Token::Tag(
343                        tag!(<basefont> | <bgsound> | <link> | <meta> | <noframes> | <style>),
344                    ) => self.step(InsertionMode::InHead, token),
345
346                    Token::Tag(tag!(</br>)) => anything_else(token),
347
348                    Token::Tag(tag!(<head> | <noscript> | </>)) => self.unexpected(&token),
349
350                    token => anything_else(token),
351                }
352            },
353
354            // § The "after head" insertion mode
355            // https://html.spec.whatwg.org/multipage/parsing.html#the-after-head-insertion-mode
356            InsertionMode::AfterHead => {
357                let anything_else = |token: Token| {
358                    self.insert_phantom(local_name!("body"));
359                    ProcessResult::Reprocess(InsertionMode::InBody, token)
360                };
361
362                match token {
363                    Token::Characters(SplitStatus::NotSplit, text) => {
364                        ProcessResult::SplitWhitespace(text)
365                    },
366                    Token::Characters(SplitStatus::Whitespace, text) => self.append_text(text),
367                    Token::Comment(text) => self.append_comment(text),
368
369                    Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
370
371                    Token::Tag(tag @ tag!(<body>)) => {
372                        self.insert_element_for(tag);
373                        self.frameset_ok.set(false);
374                        self.mode.set(InsertionMode::InBody);
375                        ProcessResult::Done
376                    },
377
378                    Token::Tag(tag @ tag!(<frameset>)) => {
379                        self.insert_element_for(tag);
380                        self.mode.set(InsertionMode::InFrameset);
381                        ProcessResult::Done
382                    },
383
384                    Token::Tag(
385                        tag!(<base> | <basefont> | <bgsound> | <link> | <meta> |
386                                <noframes> | <script> | <style> | <template> | <title>
387                        ),
388                    ) => {
389                        self.unexpected(&token);
390                        let head = self
391                            .head_elem
392                            .borrow()
393                            .as_ref()
394                            .expect("no head element")
395                            .clone();
396                        self.push(&head);
397                        let result = self.step(InsertionMode::InHead, token);
398                        self.remove_from_stack(&head);
399                        result
400                    },
401
402                    Token::Tag(tag!(</template>)) => self.step(InsertionMode::InHead, token),
403
404                    Token::Tag(tag!(</body> | </html> | </br>)) => anything_else(token),
405
406                    Token::Tag(tag!(<head> | </>)) => self.unexpected(&token),
407
408                    token => anything_else(token),
409                }
410            },
411
412            // § The "in body" insertion mode
413            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody
414            InsertionMode::InBody => match token {
415                Token::NullCharacter => self.unexpected(&token),
416
417                Token::Characters(_, text) => {
418                    self.reconstruct_active_formatting_elements();
419                    if any_not_whitespace(&text) {
420                        self.frameset_ok.set(false);
421                    }
422                    self.append_text(text)
423                },
424
425                Token::Comment(text) => self.append_comment(text),
426
427                Token::Tag(tag @ tag!(<html>)) => {
428                    self.unexpected(&tag);
429                    if !self.in_html_elem_named(local_name!("template")) {
430                        let open_elems = self.open_elems.borrow();
431                        let top = html_elem(&open_elems);
432                        self.sink.add_attrs_if_missing(top, tag.attrs);
433                    }
434                    ProcessResult::Done
435                },
436
437                Token::Tag(
438                    tag!(<base> | <basefont> | <bgsound> | <link> | <meta> | <noframes>
439                            | <script> | <style> | <template> | <title> | </template>),
440                ) => self.step(InsertionMode::InHead, token),
441
442                Token::Tag(tag @ tag!(<body>)) => {
443                    self.unexpected(&tag);
444                    let body_elem = self.body_elem().as_deref().cloned();
445                    match body_elem {
446                        Some(ref node)
447                            if self.open_elems.borrow().len() != 1
448                                && !self.in_html_elem_named(local_name!("template")) =>
449                        {
450                            self.frameset_ok.set(false);
451                            self.sink.add_attrs_if_missing(node, tag.attrs)
452                        },
453                        _ => {},
454                    }
455                    ProcessResult::Done
456                },
457
458                Token::Tag(tag @ tag!(<frameset>)) => {
459                    self.unexpected(&tag);
460                    if !self.frameset_ok.get() {
461                        return ProcessResult::Done;
462                    }
463
464                    let Some(body) = self.body_elem().map(|b| b.clone()) else {
465                        return ProcessResult::Done;
466                    };
467                    self.sink.remove_from_parent(&body);
468
469                    // FIXME: can we get here in the fragment case?
470                    // What to do with the first element then?
471                    self.open_elems.borrow_mut().truncate(1);
472                    self.insert_element_for(tag);
473                    self.mode.set(InsertionMode::InFrameset);
474                    ProcessResult::Done
475                },
476
477                Token::Eof => {
478                    if !self.template_modes.borrow().is_empty() {
479                        self.step(InsertionMode::InTemplate, token)
480                    } else {
481                        self.check_body_end();
482                        self.stop_parsing()
483                    }
484                },
485
486                Token::Tag(tag!(</body>)) => {
487                    if self.in_scope_named(default_scope, local_name!("body")) {
488                        self.check_body_end();
489                        self.mode.set(InsertionMode::AfterBody);
490                    } else {
491                        self.sink
492                            .parse_error(Borrowed("</body> with no <body> in scope"));
493                    }
494                    ProcessResult::Done
495                },
496
497                Token::Tag(tag!(</html>)) => {
498                    if self.in_scope_named(default_scope, local_name!("body")) {
499                        self.check_body_end();
500                        ProcessResult::Reprocess(InsertionMode::AfterBody, token)
501                    } else {
502                        self.sink
503                            .parse_error(Borrowed("</html> with no <body> in scope"));
504                        ProcessResult::Done
505                    }
506                },
507
508                Token::Tag(
509                    tag @
510                    tag!(<address> | <article> | <aside> | <blockquote> | <center> | <details> | <dialog> |
511                          <dir> | <div> | <dl> | <fieldset> | <figcaption> | <figure> | <footer> | <header> |
512                          <hgroup> | <main> | <nav> | <ol> | <p> | <search> | <section> | <summary> | <ul>),
513                ) => {
514                    self.close_p_element_in_button_scope();
515                    self.insert_element_for(tag);
516                    ProcessResult::Done
517                },
518
519                Token::Tag(tag @ tag!(<menu>)) => {
520                    self.close_p_element_in_button_scope();
521                    self.insert_element_for(tag);
522                    ProcessResult::Done
523                },
524
525                Token::Tag(tag @ tag!(<h1> | <h2> | <h3> | <h4> | <h5> | <h6>)) => {
526                    self.close_p_element_in_button_scope();
527                    if self.current_node_in(heading_tag) {
528                        self.sink.parse_error(Borrowed("nested heading tags"));
529                        self.pop();
530                    }
531                    self.insert_element_for(tag);
532                    ProcessResult::Done
533                },
534
535                Token::Tag(tag @ tag!(<pre> | <listing>)) => {
536                    self.close_p_element_in_button_scope();
537                    self.insert_element_for(tag);
538                    self.ignore_lf.set(true);
539                    self.frameset_ok.set(false);
540                    ProcessResult::Done
541                },
542
543                Token::Tag(tag @ tag!(<form>)) => {
544                    if self.form_elem.borrow().is_some()
545                        && !self.in_html_elem_named(local_name!("template"))
546                    {
547                        self.sink.parse_error(Borrowed("nested forms"));
548                    } else {
549                        self.close_p_element_in_button_scope();
550                        let elem = self.insert_element_for(tag);
551                        if !self.in_html_elem_named(local_name!("template")) {
552                            *self.form_elem.borrow_mut() = Some(elem);
553                        }
554                    }
555                    ProcessResult::Done
556                },
557
558                Token::Tag(tag @ tag!(<li> | <dd> | <dt>)) => {
559                    declare_tag_set!(close_list = "li");
560                    declare_tag_set!(close_defn = "dd" "dt");
561                    declare_tag_set!(extra_special = [special_tag] - "address" "div" "p");
562                    let list = match tag.name {
563                        local_name!("li") => true,
564                        local_name!("dd") | local_name!("dt") => false,
565                        _ => unreachable!(),
566                    };
567
568                    self.frameset_ok.set(false);
569
570                    let mut to_close = None;
571                    for node in self.open_elems.borrow().iter().rev() {
572                        let elem_name = self.sink.elem_name(node);
573                        let name = elem_name.expanded();
574                        let can_close = if list {
575                            close_list(name)
576                        } else {
577                            close_defn(name)
578                        };
579                        if can_close {
580                            to_close = Some(name.local.clone());
581                            break;
582                        }
583                        if extra_special(name) {
584                            break;
585                        }
586                    }
587
588                    if let Some(name) = to_close {
589                        self.generate_implied_end_except(name.clone());
590                        self.expect_to_close(name);
591                    }
592
593                    self.close_p_element_in_button_scope();
594                    self.insert_element_for(tag);
595                    ProcessResult::Done
596                },
597
598                Token::Tag(tag @ tag!(<plaintext>)) => {
599                    self.close_p_element_in_button_scope();
600                    self.insert_element_for(tag);
601                    ProcessResult::ToPlaintext
602                },
603
604                Token::Tag(tag @ tag!(<button>)) => {
605                    if self.in_scope_named(default_scope, local_name!("button")) {
606                        self.sink.parse_error(Borrowed("nested buttons"));
607                        self.generate_implied_end_tags(cursory_implied_end);
608                        self.pop_until_named(local_name!("button"));
609                    }
610                    self.reconstruct_active_formatting_elements();
611                    self.insert_element_for(tag);
612                    self.frameset_ok.set(false);
613                    ProcessResult::Done
614                },
615
616                Token::Tag(
617                    tag @
618                    tag!(</address> | </article> | </aside> | </blockquote> | </button> | </center> |
619                              </details> | </dialog> | </dir> | </div> | </dl> | </fieldset> | </figcaption> |
620                              </figure> | </footer> | </header> | </hgroup> | </listing> | </main> | </menu> |
621                              </nav> | </ol> | </pre> | </search> | </section> | </select> | </summary> | </ul>),
622                ) => {
623                    if !self.in_scope_named(default_scope, tag.name.clone()) {
624                        self.unexpected(&tag);
625                    } else {
626                        self.generate_implied_end_tags(cursory_implied_end);
627                        self.expect_to_close(tag.name);
628                    }
629                    ProcessResult::Done
630                },
631
632                Token::Tag(tag!(</form>)) => {
633                    if !self.in_html_elem_named(local_name!("template")) {
634                        let Some(node) = self.form_elem.take() else {
635                            self.sink
636                                .parse_error(Borrowed("Null form element pointer on </form>"));
637                            return ProcessResult::Done;
638                        };
639                        if !self.in_scope(default_scope, |n| self.sink.same_node(&node, &n)) {
640                            self.sink
641                                .parse_error(Borrowed("Form element not in scope on </form>"));
642                            return ProcessResult::Done;
643                        }
644                        self.generate_implied_end_tags(cursory_implied_end);
645                        let current = self.current_node().clone();
646                        self.remove_from_stack(&node);
647                        if !self.sink.same_node(&current, &node) {
648                            self.sink
649                                .parse_error(Borrowed("Bad open element on </form>"));
650                        }
651                    } else {
652                        if !self.in_scope_named(default_scope, local_name!("form")) {
653                            self.sink
654                                .parse_error(Borrowed("Form element not in scope on </form>"));
655                            return ProcessResult::Done;
656                        }
657                        self.generate_implied_end_tags(cursory_implied_end);
658                        if !self.current_node_named(local_name!("form")) {
659                            self.sink
660                                .parse_error(Borrowed("Bad open element on </form>"));
661                        }
662                        self.pop_until_named(local_name!("form"));
663                    }
664                    ProcessResult::Done
665                },
666                // FIXME: This branch does not exist like this in the specification, because it should run for
667                // implicitly closed option tags too. See https://github.com/servo/html5ever/issues/712.
668                Token::Tag(tag @ tag!(</option>)) => {
669                    let option_in_stack = self
670                        .open_elems
671                        .borrow()
672                        .iter()
673                        .find(|elem| self.html_elem_named(elem, local_name!("option")))
674                        .cloned();
675
676                    self.process_end_tag_in_body(tag);
677
678                    if let Some(option) = option_in_stack {
679                        if !self
680                            .open_elems
681                            .borrow()
682                            .iter()
683                            .any(|elem| self.sink.same_node(elem, &option))
684                        {
685                            self.sink
686                                .maybe_clone_an_option_into_selectedcontent(&option);
687                        }
688                    }
689
690                    ProcessResult::Done
691                },
692
693                Token::Tag(tag!(</p>)) => {
694                    if !self.in_scope_named(button_scope, local_name!("p")) {
695                        self.sink.parse_error(Borrowed("No <p> tag to close"));
696                        self.insert_phantom(local_name!("p"));
697                    }
698                    self.close_p_element();
699                    ProcessResult::Done
700                },
701
702                Token::Tag(tag @ tag!(</li> | </dd> | </dt>)) => {
703                    let in_scope = if tag.name == local_name!("li") {
704                        self.in_scope_named(list_item_scope, tag.name.clone())
705                    } else {
706                        self.in_scope_named(default_scope, tag.name.clone())
707                    };
708                    if in_scope {
709                        self.generate_implied_end_except(tag.name.clone());
710                        self.expect_to_close(tag.name);
711                    } else {
712                        self.sink.parse_error(Borrowed("No matching tag to close"));
713                    }
714                    ProcessResult::Done
715                },
716
717                Token::Tag(tag @ tag!(</h1> | </h2> | </h3> | </h4> | </h5> | </h6>)) => {
718                    if self.in_scope(default_scope, |n| self.elem_in(&n, heading_tag)) {
719                        self.generate_implied_end_tags(cursory_implied_end);
720                        if !self.current_node_named(tag.name) {
721                            self.sink.parse_error(Borrowed("Closing wrong heading tag"));
722                        }
723                        self.pop_until(heading_tag);
724                    } else {
725                        self.sink.parse_error(Borrowed("No heading tag to close"));
726                    }
727                    ProcessResult::Done
728                },
729
730                Token::Tag(tag @ tag!(<a>)) => {
731                    self.handle_misnested_a_tags(&tag);
732                    self.reconstruct_active_formatting_elements();
733                    self.create_formatting_element_for(tag);
734                    ProcessResult::Done
735                },
736
737                Token::Tag(
738                    tag @
739                    tag!(<b> | <big> | <code> | <em> | <font> | <i> | <s> | <small> | <strike> | <strong> | <tt> | <u>),
740                ) => {
741                    self.reconstruct_active_formatting_elements();
742                    self.create_formatting_element_for(tag);
743                    ProcessResult::Done
744                },
745
746                Token::Tag(tag @ tag!(<nobr>)) => {
747                    self.reconstruct_active_formatting_elements();
748                    if self.in_scope_named(default_scope, local_name!("nobr")) {
749                        self.sink.parse_error(Borrowed("Nested <nobr>"));
750                        self.adoption_agency(local_name!("nobr"));
751                        self.reconstruct_active_formatting_elements();
752                    }
753                    self.create_formatting_element_for(tag);
754                    ProcessResult::Done
755                },
756
757                Token::Tag(
758                    tag @ tag!(</a> | </b> | </big> | </code> | </em> | </font> | </i> | </nobr> |
759                                </s> | </small> | </strike> | </strong> | </tt> | </u>),
760                ) => {
761                    self.adoption_agency(tag.name);
762                    ProcessResult::Done
763                },
764
765                Token::Tag(tag @ tag!(<applet> | <marquee> | <object>)) => {
766                    self.reconstruct_active_formatting_elements();
767                    self.insert_element_for(tag);
768                    self.active_formatting
769                        .borrow_mut()
770                        .push(FormatEntry::Marker);
771                    self.frameset_ok.set(false);
772                    ProcessResult::Done
773                },
774
775                Token::Tag(tag @ tag!(</applet> | </marquee> | </object>)) => {
776                    if !self.in_scope_named(default_scope, tag.name.clone()) {
777                        self.unexpected(&tag);
778                    } else {
779                        self.generate_implied_end_tags(cursory_implied_end);
780                        self.expect_to_close(tag.name);
781                        self.clear_active_formatting_to_marker();
782                    }
783                    ProcessResult::Done
784                },
785
786                Token::Tag(tag @ tag!(<table>)) => {
787                    if self.quirks_mode.get() != Quirks {
788                        self.close_p_element_in_button_scope();
789                    }
790                    self.insert_element_for(tag);
791                    self.frameset_ok.set(false);
792                    self.mode.set(InsertionMode::InTable);
793                    ProcessResult::Done
794                },
795
796                Token::Tag(tag @ tag!(</br>)) => {
797                    self.unexpected(&tag);
798                    self.step(
799                        InsertionMode::InBody,
800                        Token::Tag(Tag {
801                            kind: StartTag,
802                            attrs: vec![],
803                            ..tag
804                        }),
805                    )
806                },
807
808                Token::Tag(tag @ tag!(<area> | <br> | <embed> | <img> | <keygen> | <wbr>)) => {
809                    self.reconstruct_active_formatting_elements();
810                    self.insert_and_pop_element_for(tag);
811                    self.frameset_ok.set(false);
812                    ProcessResult::DoneAckSelfClosing
813                },
814
815                Token::Tag(tag @ tag!(<input>)) => {
816                    if self.is_fragment()
817                        && self.html_elem_named(
818                            self.context_elem.borrow().as_ref().unwrap(),
819                            local_name!("select"),
820                        )
821                    {
822                        self.unexpected(&tag);
823                    }
824
825                    if self.in_scope_named(default_scope, local_name!("select")) {
826                        self.unexpected(&tag);
827                        self.pop_until_named(local_name!("select"));
828                    }
829
830                    let is_type_hidden = self.is_type_hidden(&tag);
831
832                    self.reconstruct_active_formatting_elements();
833                    self.insert_and_pop_element_for(tag);
834
835                    if !is_type_hidden {
836                        self.frameset_ok.set(false);
837                    }
838
839                    ProcessResult::DoneAckSelfClosing
840                },
841
842                Token::Tag(tag @ tag!(<param> | <source> | <track>)) => {
843                    self.insert_and_pop_element_for(tag);
844                    ProcessResult::DoneAckSelfClosing
845                },
846
847                Token::Tag(tag @ tag!(<hr>)) => {
848                    self.close_p_element_in_button_scope();
849                    if self.in_scope_named(default_scope, local_name!("select")) {
850                        self.generate_implied_end_tags(cursory_implied_end);
851                        if self.in_scope_named(default_scope, local_name!("option"))
852                            || self.in_scope_named(default_scope, local_name!("optgroup"))
853                        {
854                            self.sink.parse_error(Borrowed("hr in option"));
855                        }
856                    }
857
858                    self.insert_and_pop_element_for(tag);
859                    self.frameset_ok.set(false);
860                    ProcessResult::DoneAckSelfClosing
861                },
862
863                Token::Tag(tag @ tag!(<image>)) => {
864                    self.unexpected(&tag);
865                    self.step(
866                        InsertionMode::InBody,
867                        Token::Tag(Tag {
868                            name: local_name!("img"),
869                            ..tag
870                        }),
871                    )
872                },
873
874                Token::Tag(tag @ tag!(<textarea>)) => {
875                    self.ignore_lf.set(true);
876                    self.frameset_ok.set(false);
877                    self.parse_raw_data(tag, Rcdata)
878                },
879
880                Token::Tag(tag @ tag!(<xmp>)) => {
881                    self.close_p_element_in_button_scope();
882                    self.reconstruct_active_formatting_elements();
883                    self.frameset_ok.set(false);
884                    self.parse_raw_data(tag, Rawtext)
885                },
886
887                Token::Tag(tag @ tag!(<iframe>)) => {
888                    self.frameset_ok.set(false);
889                    self.parse_raw_data(tag, Rawtext)
890                },
891
892                Token::Tag(tag @ tag!(<noembed>)) => self.parse_raw_data(tag, Rawtext),
893
894                // <noscript> handled in wildcard case below
895                Token::Tag(tag @ tag!(<select>)) => {
896                    if self.is_fragment()
897                        && self.html_elem_named(
898                            self.context_elem.borrow().as_ref().unwrap(),
899                            local_name!("select"),
900                        )
901                    {
902                        self.unexpected(&tag);
903                    } else if self.in_scope_named(default_scope, local_name!("select")) {
904                        self.unexpected(&tag);
905                        self.pop_until_named(local_name!("select"));
906                    } else {
907                        self.reconstruct_active_formatting_elements();
908                        self.insert_element_for(tag);
909                        self.frameset_ok.set(false);
910                    }
911
912                    ProcessResult::Done
913                },
914
915                Token::Tag(tag @ tag!(<option>)) => {
916                    if self.in_scope_named(default_scope, local_name!("select")) {
917                        self.generate_implied_end_except(local_name!("optgroup"));
918                        if self.in_scope_named(default_scope, local_name!("option")) {
919                            self.sink.parse_error(Borrowed("nested options"));
920                        }
921                    } else if self.current_node_named(local_name!("option")) {
922                        self.pop();
923                    }
924
925                    self.reconstruct_active_formatting_elements();
926                    self.insert_element_for(tag);
927                    ProcessResult::Done
928                },
929
930                Token::Tag(tag @ tag!(<optgroup>)) => {
931                    if self.in_scope_named(default_scope, local_name!("select")) {
932                        self.generate_implied_end_tags(cursory_implied_end);
933                        if self.in_scope_named(default_scope, local_name!("option"))
934                            || self.in_scope_named(default_scope, local_name!("optgroup"))
935                        {
936                            self.sink.parse_error(Borrowed("nested options"));
937                        }
938                    } else if self.current_node_named(local_name!("option")) {
939                        self.pop();
940                    }
941
942                    self.reconstruct_active_formatting_elements();
943                    self.insert_element_for(tag);
944                    ProcessResult::Done
945                },
946
947                Token::Tag(tag @ tag!(<rb> | <rtc>)) => {
948                    if self.in_scope_named(default_scope, local_name!("ruby")) {
949                        self.generate_implied_end_tags(cursory_implied_end);
950                    }
951                    if !self.current_node_named(local_name!("ruby")) {
952                        self.unexpected(&tag);
953                    }
954                    self.insert_element_for(tag);
955                    ProcessResult::Done
956                },
957
958                Token::Tag(tag @ tag!(<rp> | <rt>)) => {
959                    if self.in_scope_named(default_scope, local_name!("ruby")) {
960                        self.generate_implied_end_except(local_name!("rtc"));
961                    }
962                    if !self.current_node_named(local_name!("rtc"))
963                        && !self.current_node_named(local_name!("ruby"))
964                    {
965                        self.unexpected(&tag);
966                    }
967                    self.insert_element_for(tag);
968                    ProcessResult::Done
969                },
970
971                Token::Tag(tag @ tag!(<math>)) => {
972                    self.reconstruct_active_formatting_elements();
973                    self.enter_foreign(tag, ns!(mathml))
974                },
975
976                Token::Tag(tag @ tag!(<svg>)) => {
977                    self.reconstruct_active_formatting_elements();
978                    self.enter_foreign(tag, ns!(svg))
979                },
980
981                Token::Tag(
982                    tag!(<caption> | <col> | <colgroup> | <frame> | <head> |
983                                <tbody> | <td> | <tfoot> | <th> | <thead> | <tr>),
984                ) => {
985                    self.unexpected(&token);
986                    ProcessResult::Done
987                },
988
989                Token::Tag(tag @ tag!(<>)) => {
990                    if self.opts.scripting_enabled && tag.name == local_name!("noscript") {
991                        self.parse_raw_data(tag, Rawtext)
992                    } else {
993                        self.reconstruct_active_formatting_elements();
994                        self.insert_element_for(tag);
995                        ProcessResult::Done
996                    }
997                },
998
999                Token::Tag(tag @ tag!(</>)) => {
1000                    self.process_end_tag_in_body(tag);
1001                    ProcessResult::Done
1002                },
1003            },
1004
1005            // § The "text" insertion mode
1006            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incdata
1007            InsertionMode::Text => match token {
1008                Token::Characters(_, text) => self.append_text(text),
1009
1010                Token::Eof => {
1011                    self.unexpected(&token);
1012                    if self.current_node_named(local_name!("script")) {
1013                        let open_elems = self.open_elems.borrow();
1014                        let current = current_node(&open_elems);
1015                        self.sink.mark_script_already_started(current);
1016                    }
1017                    self.pop();
1018                    ProcessResult::Reprocess(self.orig_mode.take().unwrap(), token)
1019                },
1020
1021                Token::Tag(tag @ tag!(</>)) => {
1022                    let node = self.pop();
1023                    self.mode.set(self.orig_mode.take().unwrap());
1024                    if tag.name == local_name!("script") {
1025                        return ProcessResult::Script(node);
1026                    }
1027                    ProcessResult::Done
1028                },
1029
1030                // The spec doesn't say what to do here.
1031                // Other tokens are impossible?
1032                _ => unreachable!("impossible case in Text mode"),
1033            },
1034
1035            // § The "in table" insertion mode
1036            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intable
1037            InsertionMode::InTable => match token {
1038                Token::NullCharacter | Token::Characters(..) => self.process_chars_in_table(token),
1039
1040                Token::Comment(text) => self.append_comment(text),
1041
1042                Token::Tag(tag @ tag!(<caption>)) => {
1043                    self.pop_until_current(table_scope);
1044                    self.active_formatting
1045                        .borrow_mut()
1046                        .push(FormatEntry::Marker);
1047                    self.insert_element_for(tag);
1048                    self.mode.set(InsertionMode::InCaption);
1049                    ProcessResult::Done
1050                },
1051
1052                Token::Tag(tag @ tag!(<colgroup>)) => {
1053                    self.pop_until_current(table_scope);
1054                    self.insert_element_for(tag);
1055                    self.mode.set(InsertionMode::InColumnGroup);
1056                    ProcessResult::Done
1057                },
1058
1059                Token::Tag(tag!(<col>)) => {
1060                    self.pop_until_current(table_scope);
1061                    self.insert_phantom(local_name!("colgroup"));
1062                    ProcessResult::Reprocess(InsertionMode::InColumnGroup, token)
1063                },
1064
1065                Token::Tag(tag @ tag!(<tbody> | <tfoot> | <thead>)) => {
1066                    self.pop_until_current(table_scope);
1067                    self.insert_element_for(tag);
1068                    self.mode.set(InsertionMode::InTableBody);
1069                    ProcessResult::Done
1070                },
1071
1072                Token::Tag(tag!(<td> | <th> | <tr>)) => {
1073                    self.pop_until_current(table_scope);
1074                    self.insert_phantom(local_name!("tbody"));
1075                    ProcessResult::Reprocess(InsertionMode::InTableBody, token)
1076                },
1077
1078                Token::Tag(tag!(<table>)) => {
1079                    self.unexpected(&token);
1080                    if self.in_scope_named(table_scope, local_name!("table")) {
1081                        self.pop_until_named(local_name!("table"));
1082                        ProcessResult::Reprocess(self.reset_insertion_mode(), token)
1083                    } else {
1084                        ProcessResult::Done
1085                    }
1086                },
1087
1088                Token::Tag(tag!(</table>)) => {
1089                    if self.in_scope_named(table_scope, local_name!("table")) {
1090                        self.pop_until_named(local_name!("table"));
1091                        self.mode.set(self.reset_insertion_mode());
1092                    } else {
1093                        self.unexpected(&token);
1094                    }
1095                    ProcessResult::Done
1096                },
1097
1098                Token::Tag(
1099                    tag!(</body> | </caption> | </col> | </colgroup> | </html> |
1100                        </tbody> | </td> | </tfoot> | </th> | </thead> | </tr>),
1101                ) => self.unexpected(&token),
1102
1103                Token::Tag(tag!(<style> | <script> | <template> | </template>)) => {
1104                    self.step(InsertionMode::InHead, token)
1105                },
1106
1107                Token::Tag(tag @ tag!(<input>)) => {
1108                    self.unexpected(&tag);
1109                    if self.is_type_hidden(&tag) {
1110                        self.insert_and_pop_element_for(tag);
1111                        ProcessResult::DoneAckSelfClosing
1112                    } else {
1113                        self.foster_parent_in_body(Token::Tag(tag))
1114                    }
1115                },
1116
1117                Token::Tag(tag @ tag!(<form>)) => {
1118                    self.unexpected(&tag);
1119                    if !self.in_html_elem_named(local_name!("template"))
1120                        && self.form_elem.borrow().is_none()
1121                    {
1122                        *self.form_elem.borrow_mut() = Some(self.insert_and_pop_element_for(tag));
1123                    }
1124                    ProcessResult::Done
1125                },
1126
1127                Token::Eof => self.step(InsertionMode::InBody, token),
1128
1129                token => {
1130                    self.unexpected(&token);
1131                    self.foster_parent_in_body(token)
1132                },
1133            },
1134
1135            // § The "in table text" insertion mode
1136            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intabletext
1137            InsertionMode::InTableText => match token {
1138                Token::NullCharacter => self.unexpected(&token),
1139
1140                Token::Characters(split, text) => {
1141                    self.pending_table_text.borrow_mut().push((split, text));
1142                    ProcessResult::Done
1143                },
1144
1145                token => {
1146                    let pending = self.pending_table_text.take();
1147                    let contains_nonspace = pending.iter().any(|&(split, ref text)| match split {
1148                        SplitStatus::Whitespace => false,
1149                        SplitStatus::NotWhitespace => true,
1150                        SplitStatus::NotSplit => any_not_whitespace(text),
1151                    });
1152
1153                    if contains_nonspace {
1154                        self.sink.parse_error(Borrowed("Non-space table text"));
1155                        for (split, text) in pending.into_iter() {
1156                            match self.foster_parent_in_body(Token::Characters(split, text)) {
1157                                ProcessResult::Done => (),
1158                                _ => panic!("not prepared to handle this!"),
1159                            }
1160                        }
1161                    } else {
1162                        for (_, text) in pending.into_iter() {
1163                            self.append_text(text);
1164                        }
1165                    }
1166
1167                    ProcessResult::Reprocess(self.orig_mode.take().unwrap(), token)
1168                },
1169            },
1170
1171            // § The "in caption" insertion mode
1172            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incaption
1173            InsertionMode::InCaption => match token {
1174                Token::Tag(
1175                    tag @ tag!(<caption> | <col> | <colgroup> | <tbody> | <td> | <tfoot> |
1176                                <th> | <thead> | <tr> | </table> | </caption>),
1177                ) => {
1178                    if self.in_scope_named(table_scope, local_name!("caption")) {
1179                        self.generate_implied_end_tags(cursory_implied_end);
1180                        self.expect_to_close(local_name!("caption"));
1181                        self.clear_active_formatting_to_marker();
1182                        match tag {
1183                            Tag {
1184                                kind: EndTag,
1185                                name: local_name!("caption"),
1186                                ..
1187                            } => {
1188                                self.mode.set(InsertionMode::InTable);
1189                                ProcessResult::Done
1190                            },
1191                            _ => ProcessResult::Reprocess(InsertionMode::InTable, Token::Tag(tag)),
1192                        }
1193                    } else {
1194                        self.unexpected(&tag);
1195                        ProcessResult::Done
1196                    }
1197                },
1198
1199                Token::Tag(
1200                    tag!(</body> | </col> | </colgroup> | </html> | </tbody> |
1201                            </td> | </tfoot> | </th> | </thead> | </tr>),
1202                ) => self.unexpected(&token),
1203
1204                token => self.step(InsertionMode::InBody, token),
1205            },
1206
1207            // § The "in column group" insertion mode
1208            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incolgroup
1209            InsertionMode::InColumnGroup => match token {
1210                Token::Characters(SplitStatus::NotSplit, text) => {
1211                    ProcessResult::SplitWhitespace(text)
1212                },
1213                Token::Characters(SplitStatus::Whitespace, text) => self.append_text(text),
1214                Token::Comment(text) => self.append_comment(text),
1215
1216                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1217
1218                Token::Tag(tag @ tag!(<col>)) => {
1219                    self.insert_and_pop_element_for(tag);
1220                    ProcessResult::DoneAckSelfClosing
1221                },
1222
1223                Token::Tag(tag!(</colgroup>)) => {
1224                    if self.current_node_named(local_name!("colgroup")) {
1225                        self.pop();
1226                        self.mode.set(InsertionMode::InTable);
1227                    } else {
1228                        self.unexpected(&token);
1229                    }
1230                    ProcessResult::Done
1231                },
1232
1233                Token::Tag(tag!(</col>)) => self.unexpected(&token),
1234
1235                Token::Tag(tag!(<template> | </template>)) => {
1236                    self.step(InsertionMode::InHead, token)
1237                },
1238
1239                Token::Eof => self.step(InsertionMode::InBody, token),
1240
1241                token => {
1242                    if self.current_node_named(local_name!("colgroup")) {
1243                        self.pop();
1244                        ProcessResult::Reprocess(InsertionMode::InTable, token)
1245                    } else {
1246                        self.unexpected(&token)
1247                    }
1248                },
1249            },
1250
1251            // § The "in table body" insertion mode
1252            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intbody
1253            InsertionMode::InTableBody => match token {
1254                Token::Tag(tag @ tag!(<tr>)) => {
1255                    self.pop_until_current(table_body_context);
1256                    self.insert_element_for(tag);
1257                    self.mode.set(InsertionMode::InRow);
1258                    ProcessResult::Done
1259                },
1260
1261                Token::Tag(tag!(<th> | <td>)) => {
1262                    self.unexpected(&token);
1263                    self.pop_until_current(table_body_context);
1264                    self.insert_phantom(local_name!("tr"));
1265                    ProcessResult::Reprocess(InsertionMode::InRow, token)
1266                },
1267
1268                Token::Tag(tag @ tag!(</tbody> | </tfoot> | </thead>)) => {
1269                    if self.in_scope_named(table_scope, tag.name.clone()) {
1270                        self.pop_until_current(table_body_context);
1271                        self.pop();
1272                        self.mode.set(InsertionMode::InTable);
1273                    } else {
1274                        self.unexpected(&tag);
1275                    }
1276                    ProcessResult::Done
1277                },
1278
1279                Token::Tag(
1280                    tag!(<caption> | <col> | <colgroup> | <tbody> | <tfoot> | <thead> | </table>),
1281                ) => {
1282                    declare_tag_set!(table_outer = "table" "tbody" "tfoot");
1283                    if self.in_scope(table_scope, |e| self.elem_in(&e, table_outer)) {
1284                        self.pop_until_current(table_body_context);
1285                        self.pop();
1286                        ProcessResult::Reprocess(InsertionMode::InTable, token)
1287                    } else {
1288                        self.unexpected(&token)
1289                    }
1290                },
1291
1292                Token::Tag(
1293                    tag!(</body> | </caption> | </col> | </colgroup> | </html> | </td> | </th> | </tr>),
1294                ) => self.unexpected(&token),
1295
1296                token => self.step(InsertionMode::InTable, token),
1297            },
1298
1299            // § The "in row" insertion mode
1300            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intr
1301            InsertionMode::InRow => match token {
1302                Token::Tag(tag @ tag!(<th> | <td>)) => {
1303                    self.pop_until_current(table_row_context);
1304                    self.insert_element_for(tag);
1305                    self.mode.set(InsertionMode::InCell);
1306                    self.active_formatting
1307                        .borrow_mut()
1308                        .push(FormatEntry::Marker);
1309                    ProcessResult::Done
1310                },
1311
1312                Token::Tag(tag!(</tr>)) => {
1313                    if self.in_scope_named(table_scope, local_name!("tr")) {
1314                        self.pop_until_current(table_row_context);
1315                        let node = self.pop();
1316                        self.assert_named(&node, local_name!("tr"));
1317                        self.mode.set(InsertionMode::InTableBody);
1318                    } else {
1319                        self.unexpected(&token);
1320                    }
1321                    ProcessResult::Done
1322                },
1323
1324                Token::Tag(
1325                    tag!(<caption> | <col> | <colgroup> | <tbody> | <tfoot> | <thead> | <tr> | </table>),
1326                ) => {
1327                    if self.in_scope_named(table_scope, local_name!("tr")) {
1328                        self.pop_until_current(table_row_context);
1329                        let node = self.pop();
1330                        self.assert_named(&node, local_name!("tr"));
1331                        ProcessResult::Reprocess(InsertionMode::InTableBody, token)
1332                    } else {
1333                        self.unexpected(&token)
1334                    }
1335                },
1336
1337                Token::Tag(tag @ tag!(</tbody> | </tfoot> | </thead>)) => {
1338                    if self.in_scope_named(table_scope, tag.name.clone()) {
1339                        if self.in_scope_named(table_scope, local_name!("tr")) {
1340                            self.pop_until_current(table_row_context);
1341                            let node = self.pop();
1342                            self.assert_named(&node, local_name!("tr"));
1343                            ProcessResult::Reprocess(InsertionMode::InTableBody, Token::Tag(tag))
1344                        } else {
1345                            ProcessResult::Done
1346                        }
1347                    } else {
1348                        self.unexpected(&tag)
1349                    }
1350                },
1351
1352                Token::Tag(
1353                    tag!(</body> | </caption> | </col> | </colgroup> | </html> | </td> | </th>),
1354                ) => self.unexpected(&token),
1355
1356                token => self.step(InsertionMode::InTable, token),
1357            },
1358
1359            // § The "in cell" insertion mode
1360            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intd
1361            InsertionMode::InCell => match token {
1362                Token::Tag(tag @ tag!(</td> | </th>)) => {
1363                    if self.in_scope_named(table_scope, tag.name.clone()) {
1364                        self.generate_implied_end_tags(cursory_implied_end);
1365                        self.expect_to_close(tag.name);
1366                        self.clear_active_formatting_to_marker();
1367                        self.mode.set(InsertionMode::InRow);
1368                    } else {
1369                        self.unexpected(&tag);
1370                    }
1371                    ProcessResult::Done
1372                },
1373
1374                Token::Tag(
1375                    tag!(<caption> | <col> | <colgroup> | <tbody> | <td> | <tfoot> | <th> | <thead> | <tr>),
1376                ) => {
1377                    if self.in_scope(table_scope, |n| self.elem_in(&n, td_th)) {
1378                        self.close_the_cell();
1379                        ProcessResult::Reprocess(InsertionMode::InRow, token)
1380                    } else {
1381                        self.unexpected(&token)
1382                    }
1383                },
1384
1385                Token::Tag(tag!(</body> | </caption> | </col> | </colgroup> | </html>)) => {
1386                    self.unexpected(&token)
1387                },
1388
1389                Token::Tag(tag @ tag!(</table> | </tbody> | </tfoot> | </thead> | </tr>)) => {
1390                    if self.in_scope_named(table_scope, tag.name.clone()) {
1391                        self.close_the_cell();
1392                        ProcessResult::Reprocess(InsertionMode::InRow, Token::Tag(tag))
1393                    } else {
1394                        self.unexpected(&tag)
1395                    }
1396                },
1397
1398                token => self.step(InsertionMode::InBody, token),
1399            },
1400
1401            // § The "in template" insertion mode
1402            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intemplate
1403            InsertionMode::InTemplate => match token {
1404                Token::Characters(_, _) => self.step(InsertionMode::InBody, token),
1405                Token::Comment(_) => self.step(InsertionMode::InBody, token),
1406
1407                Token::Tag(
1408                    tag!(<base> | <basefont> | <bgsound> | <link> | <meta> | <noframes> | <script> |
1409                                <style> | <template> | <title> | </template>),
1410                ) => self.step(InsertionMode::InHead, token),
1411
1412                Token::Tag(tag!(<caption> | <colgroup> | <tbody> | <tfoot> | <thead>)) => {
1413                    self.template_modes.borrow_mut().pop();
1414                    self.template_modes
1415                        .borrow_mut()
1416                        .push(InsertionMode::InTable);
1417                    ProcessResult::Reprocess(InsertionMode::InTable, token)
1418                },
1419
1420                Token::Tag(tag!(<col>)) => {
1421                    self.template_modes.borrow_mut().pop();
1422                    self.template_modes
1423                        .borrow_mut()
1424                        .push(InsertionMode::InColumnGroup);
1425                    ProcessResult::Reprocess(InsertionMode::InColumnGroup, token)
1426                },
1427
1428                Token::Tag(tag!(<tr>)) => {
1429                    self.template_modes.borrow_mut().pop();
1430                    self.template_modes
1431                        .borrow_mut()
1432                        .push(InsertionMode::InTableBody);
1433                    ProcessResult::Reprocess(InsertionMode::InTableBody, token)
1434                },
1435
1436                Token::Tag(tag!(<td> | <th>)) => {
1437                    self.template_modes.borrow_mut().pop();
1438                    self.template_modes.borrow_mut().push(InsertionMode::InRow);
1439                    ProcessResult::Reprocess(InsertionMode::InRow, token)
1440                },
1441
1442                Token::Eof => {
1443                    if !self.in_html_elem_named(local_name!("template")) {
1444                        self.stop_parsing()
1445                    } else {
1446                        self.unexpected(&token);
1447                        self.pop_until_named(local_name!("template"));
1448                        self.clear_active_formatting_to_marker();
1449                        self.template_modes.borrow_mut().pop();
1450                        self.mode.set(self.reset_insertion_mode());
1451                        ProcessResult::Reprocess(self.reset_insertion_mode(), token)
1452                    }
1453                },
1454
1455                Token::Tag(tag @ tag!(<>)) => {
1456                    self.template_modes.borrow_mut().pop();
1457                    self.template_modes.borrow_mut().push(InsertionMode::InBody);
1458                    ProcessResult::Reprocess(InsertionMode::InBody, Token::Tag(tag))
1459                },
1460
1461                token => self.unexpected(&token),
1462            },
1463
1464            // § The "after body" insertion mode
1465            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-afterbody
1466            InsertionMode::AfterBody => match token {
1467                Token::Characters(SplitStatus::NotSplit, text) => {
1468                    ProcessResult::SplitWhitespace(text)
1469                },
1470                Token::Characters(SplitStatus::Whitespace, _) => {
1471                    self.step(InsertionMode::InBody, token)
1472                },
1473                Token::Comment(text) => self.append_comment_to_html(text),
1474
1475                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1476
1477                Token::Tag(tag!(</html>)) => {
1478                    if self.is_fragment() {
1479                        self.unexpected(&token);
1480                    } else {
1481                        self.mode.set(InsertionMode::AfterAfterBody);
1482                    }
1483                    ProcessResult::Done
1484                },
1485
1486                Token::Eof => self.stop_parsing(),
1487
1488                token => {
1489                    self.unexpected(&token);
1490                    ProcessResult::Reprocess(InsertionMode::InBody, token)
1491                },
1492            },
1493
1494            // § The "in frameset" insertion mode
1495            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inframeset
1496            InsertionMode::InFrameset => match token {
1497                Token::Characters(SplitStatus::NotSplit, text) => {
1498                    ProcessResult::SplitWhitespace(text)
1499                },
1500                Token::Characters(SplitStatus::Whitespace, text) => self.append_text(text),
1501                Token::Comment(text) => self.append_comment(text),
1502
1503                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1504
1505                Token::Tag(tag @ tag!(<frameset>)) => {
1506                    self.insert_element_for(tag);
1507                    ProcessResult::Done
1508                },
1509
1510                Token::Tag(tag!(</frameset>)) => {
1511                    if self.open_elems.borrow().len() == 1 {
1512                        self.unexpected(&token);
1513                    } else {
1514                        self.pop();
1515                        if !self.is_fragment() && !self.current_node_named(local_name!("frameset"))
1516                        {
1517                            self.mode.set(InsertionMode::AfterFrameset);
1518                        }
1519                    }
1520                    ProcessResult::Done
1521                },
1522
1523                Token::Tag(tag @ tag!(<frame>)) => {
1524                    self.insert_and_pop_element_for(tag);
1525                    ProcessResult::DoneAckSelfClosing
1526                },
1527
1528                Token::Tag(tag!(<noframes>)) => self.step(InsertionMode::InHead, token),
1529
1530                Token::Eof => {
1531                    if self.open_elems.borrow().len() != 1 {
1532                        self.unexpected(&token);
1533                    }
1534                    self.stop_parsing()
1535                },
1536
1537                token => self.unexpected(&token),
1538            },
1539
1540            // § The "after frameset" insertion mode
1541            // <html.spec.whatwg.org/multipage/parsing.html#parsing-main-afterframeset>
1542            InsertionMode::AfterFrameset => match token {
1543                Token::Characters(SplitStatus::NotSplit, text) => {
1544                    ProcessResult::SplitWhitespace(text)
1545                },
1546                Token::Characters(SplitStatus::Whitespace, text) => self.append_text(text),
1547                Token::Comment(text) => self.append_comment(text),
1548
1549                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1550
1551                Token::Tag(tag!(</html>)) => {
1552                    self.mode.set(InsertionMode::AfterAfterFrameset);
1553                    ProcessResult::Done
1554                },
1555
1556                Token::Tag(tag!(<noframes>)) => self.step(InsertionMode::InHead, token),
1557
1558                Token::Eof => self.stop_parsing(),
1559
1560                token => self.unexpected(&token),
1561            },
1562
1563            // § The "after after body" insertion mode
1564            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-afterframeset
1565            InsertionMode::AfterAfterBody => match token {
1566                Token::Characters(SplitStatus::NotSplit, text) => {
1567                    ProcessResult::SplitWhitespace(text)
1568                },
1569                Token::Characters(SplitStatus::Whitespace, _) => {
1570                    self.step(InsertionMode::InBody, token)
1571                },
1572                Token::Comment(text) => self.append_comment_to_doc(text),
1573
1574                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1575
1576                Token::Eof => self.stop_parsing(),
1577
1578                token => {
1579                    self.unexpected(&token);
1580                    ProcessResult::Reprocess(InsertionMode::InBody, token)
1581                },
1582            },
1583
1584            // § The "after after frameset" insertion mode
1585            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-afterframeset
1586            InsertionMode::AfterAfterFrameset => match token {
1587                Token::Characters(SplitStatus::NotSplit, text) => {
1588                    ProcessResult::SplitWhitespace(text)
1589                },
1590                Token::Characters(SplitStatus::Whitespace, _) => {
1591                    self.step(InsertionMode::InBody, token)
1592                },
1593                Token::Comment(text) => self.append_comment_to_doc(text),
1594
1595                Token::Tag(tag!(<html>)) => self.step(InsertionMode::InBody, token),
1596
1597                Token::Eof => self.stop_parsing(),
1598
1599                Token::Tag(tag!(<noframes>)) => self.step(InsertionMode::InHead, token),
1600
1601                token => self.unexpected(&token),
1602            },
1603        }
1604    }
1605
1606    /// § The rules for parsing tokens in foreign content
1607    /// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-afterframeset
1608    pub(crate) fn step_foreign(&self, token: Token) -> ProcessResult<Handle> {
1609        match token {
1610            Token::NullCharacter => {
1611                self.unexpected(&token);
1612                self.append_text("\u{fffd}".to_tendril())
1613            },
1614
1615            Token::Characters(_, text) => {
1616                if any_not_whitespace(&text) {
1617                    self.frameset_ok.set(false);
1618                }
1619                self.append_text(text)
1620            },
1621
1622            Token::Comment(text) => self.append_comment(text),
1623
1624            Token::Tag(
1625                tag @
1626                tag!(<b> | <big> | <blockquote> | <body> | <br> | <center> | <code> | <dd> | <div> | <dl> |
1627                <dt> | <em> | <embed> | <h1> | <h2> | <h3> | <h4> | <h5> | <h6> | <head> | <hr> | <i> |
1628                <img> | <li> | <listing> | <menu> | <meta> | <nobr> | <ol> | <p> | <pre> | <ruby> |
1629                <s> | <small> | <span> | <strong> | <strike> | <sub> | <sup> | <table> | <tt> |
1630                <u> | <ul> | <var> | </br> | </p>),
1631            ) => self.unexpected_start_tag_in_foreign_content(tag),
1632
1633            Token::Tag(tag @ tag!(<font>)) => {
1634                let unexpected = tag.attrs.iter().any(|attr| {
1635                    matches!(
1636                        attr.name.expanded(),
1637                        expanded_name!("", "color")
1638                            | expanded_name!("", "face")
1639                            | expanded_name!("", "size")
1640                    )
1641                });
1642                if unexpected {
1643                    self.unexpected_start_tag_in_foreign_content(tag)
1644                } else {
1645                    self.foreign_start_tag(tag)
1646                }
1647            },
1648
1649            Token::Tag(tag @ tag!(<>)) => self.foreign_start_tag(tag),
1650
1651            // FIXME(#118): </script> in SVG
1652            Token::Tag(tag @ tag!(</>)) => {
1653                let mut first = true;
1654                let mut stack_idx = self.open_elems.borrow().len() - 1;
1655                loop {
1656                    if stack_idx == 0 {
1657                        return ProcessResult::Done;
1658                    }
1659
1660                    let html;
1661                    let eq;
1662                    {
1663                        let open_elems = self.open_elems.borrow();
1664                        let node_name = self.sink.elem_name(&open_elems[stack_idx]);
1665                        html = *node_name.ns() == ns!(html);
1666                        eq = node_name.local_name().eq_ignore_ascii_case(&tag.name);
1667                    }
1668                    if !first && html {
1669                        let mode = self.mode.get();
1670                        return self.step(mode, Token::Tag(tag));
1671                    }
1672
1673                    if eq {
1674                        self.open_elems.borrow_mut().truncate(stack_idx);
1675                        return ProcessResult::Done;
1676                    }
1677
1678                    if first {
1679                        self.unexpected(&tag);
1680                        first = false;
1681                    }
1682                    stack_idx -= 1;
1683                }
1684            },
1685
1686            // FIXME: Why is this unreachable?
1687            Token::Eof => panic!("impossible case in foreign content"),
1688        }
1689    }
1690}