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