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