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