1 // Copyright 2014-2017 The html5ever Project Developers. See the
2 // COPYRIGHT file at the top-level directory of this distribution.
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.
10 // The tree builder rules, as a single, enormous nested match expression.
12 use tree_builder
::types
::*;
13 use tree_builder
::tag_sets
::*;
14 use tokenizer
::states
::{Rcdata, Rawtext, ScriptData, Plaintext}
;
16 use std
::borrow
::ToOwned
;
18 use tendril
::SliceExt
;
20 fn any_not_whitespace(x
: &StrTendril
) -> bool
{
21 // FIXME: this might be much faster as a byte scan
22 x
.chars().any(|c
| !is_ascii_whitespace(c
))
25 fn current_node
<Handle
>(open_elems
: &[Handle
]) -> &Handle
{
26 open_elems
.last().expect("no current element")
30 impl<Handle
, Sink
> TreeBuilder
<Handle
, Sink
>
32 Sink
: TreeSink
<Handle
=Handle
>,
34 fn step(&mut self, mode
: InsertionMode
, token
: Token
) -> ProcessResult
<Handle
> {
35 self.debug_step(mode
, &token
);
38 //§ the-initial-insertion-mode
39 Initial
=> match_token
!(token
{
40 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
41 CharacterTokens(Whitespace
, _
) => Done
,
42 CommentToken(text
) => self.append_comment_to_doc(text
),
44 if !self.opts
.iframe_srcdoc
{
45 self.unexpected(&token
);
46 self.set_quirks_mode(Quirks
);
48 Reprocess(BeforeHtml
, token
)
52 //§ the-before-html-insertion-mode
53 BeforeHtml
=> match_token
!(token
{
54 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
55 CharacterTokens(Whitespace
, _
) => Done
,
56 CommentToken(text
) => self.append_comment_to_doc(text
),
59 self.create_root(tag
.attrs
);
60 self.mode
= BeforeHead
;
64 </head
> </body
> </html
> </br
> => else,
66 tag @
</_
> => self.unexpected(&tag
),
69 self.create_root(vec
!());
70 Reprocess(BeforeHead
, token
)
74 //§ the-before-head-insertion-mode
75 BeforeHead
=> match_token
!(token
{
76 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
77 CharacterTokens(Whitespace
, _
) => Done
,
78 CommentToken(text
) => self.append_comment(text
),
80 <html
> => self.step(InBody
, token
),
83 self.head_elem
= Some(self.insert_element_for(tag
));
88 </head
> </body
> </html
> </br
> => else,
90 tag @
</_
> => self.unexpected(&tag
),
93 self.head_elem
= Some(self.insert_phantom(local_name
!("head")));
94 Reprocess(InHead
, token
)
98 //§ parsing-main-inhead
99 InHead
=> match_token
!(token
{
100 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
101 CharacterTokens(Whitespace
, text
) => self.append_text(text
),
102 CommentToken(text
) => self.append_comment(text
),
104 <html
> => self.step(InBody
, token
),
106 tag @
<base
> <basefont
> <bgsound
> <link
> <meta
> => {
107 // FIXME: handle <meta charset=...> and <meta http-equiv="Content-Type">
108 self.insert_and_pop_element_for(tag
);
113 self.parse_raw_data(tag
, Rcdata
)
116 tag @
<noframes
> <style
> <noscript
> => {
117 if (!self.opts
.scripting_enabled
) && (tag
.name
== local_name
!("noscript")) {
118 self.insert_element_for(tag
);
119 self.mode
= InHeadNoscript
;
122 self.parse_raw_data(tag
, Rawtext
)
127 let elem
= create_element(
128 &mut self.sink
, QualName
::new(None
, ns
!(html
), local_name
!("script")),
130 if self.is_fragment() {
131 self.sink
.mark_script_already_started(&elem
);
133 self.insert_appropriately(AppendNode(elem
.clone()), None
);
134 self.open_elems
.push(elem
);
135 self.to_raw_text_mode(ScriptData
)
140 self.mode
= AfterHead
;
144 </body
> </html
> </br
> => else,
146 tag @
<template
> => {
147 self.insert_element_for(tag
);
148 self.active_formatting
.push(Marker
);
149 self.frameset_ok
= false;
150 self.mode
= InTemplate
;
151 self.template_modes
.push(InTemplate
);
155 tag @
</template
> => {
156 if !self.in_html_elem_named(local_name
!("template")) {
157 self.unexpected(&tag
);
159 self.generate_implied_end(thorough_implied_end
);
160 self.expect_to_close(local_name
!("template"));
161 self.clear_active_formatting_to_marker();
162 self.template_modes
.pop();
163 self.mode
= self.reset_insertion_mode();
168 <head
> => self.unexpected(&token
),
169 tag @
</_
> => self.unexpected(&tag
),
173 Reprocess(AfterHead
, token
)
177 //§ parsing-main-inheadnoscript
178 InHeadNoscript
=> match_token
!(token
{
179 <html
> => self.step(InBody
, token
),
187 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
188 CharacterTokens(Whitespace
, _
) => self.step(InHead
, token
),
190 CommentToken(_
) => self.step(InHead
, token
),
192 <basefont
> <bgsound
> <link
> <meta
> <noframes
> <style
>
193 => self.step(InHead
, token
),
197 <head
> <noscript
> => self.unexpected(&token
),
198 tag @
</_
> => self.unexpected(&tag
),
201 self.unexpected(&token
);
203 Reprocess(InHead
, token
)
207 //§ the-after-head-insertion-mode
208 AfterHead
=> match_token
!(token
{
209 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
210 CharacterTokens(Whitespace
, text
) => self.append_text(text
),
211 CommentToken(text
) => self.append_comment(text
),
213 <html
> => self.step(InBody
, token
),
216 self.insert_element_for(tag
);
217 self.frameset_ok
= false;
222 tag @
<frameset
> => {
223 self.insert_element_for(tag
);
224 self.mode
= InFrameset
;
228 <base
> <basefont
> <bgsound
> <link
> <meta
>
229 <noframes
> <script
> <style
> <template
> <title
> => {
230 self.unexpected(&token
);
231 let head
= self.head_elem
.as_ref().expect("no head element").clone();
233 let result
= self.step(InHead
, token
);
234 self.remove_from_stack(&head
);
238 </template
> => self.step(InHead
, token
),
240 </body
> </html
> </br
> => else,
242 <head
> => self.unexpected(&token
),
243 tag @
</_
> => self.unexpected(&tag
),
246 self.insert_phantom(local_name
!("body"));
247 Reprocess(InBody
, token
)
251 //§ parsing-main-inbody
252 InBody
=> match_token
!(token
{
253 NullCharacterToken
=> self.unexpected(&token
),
255 CharacterTokens(_
, text
) => {
256 self.reconstruct_formatting();
257 if any_not_whitespace(&text
) {
258 self.frameset_ok
= false;
260 self.append_text(text
)
263 CommentToken(text
) => self.append_comment(text
),
266 self.unexpected(&tag
);
267 if !self.in_html_elem_named(local_name
!("template")) {
268 let top
= html_elem(&self.open_elems
);
269 self.sink
.add_attrs_if_missing(top
, tag
.attrs
);
274 <base
> <basefont
> <bgsound
> <link
> <meta
> <noframes
>
275 <script
> <style
> <template
> <title
> </template
> => {
276 self.step(InHead
, token
)
280 self.unexpected(&tag
);
281 match self.body_elem().cloned() {
282 Some(ref node
) if self.open_elems
.len() != 1 &&
283 !self.in_html_elem_named(local_name
!("template")) => {
284 self.frameset_ok
= false;
285 self.sink
.add_attrs_if_missing(node
, tag
.attrs
)
292 tag @
<frameset
> => {
293 self.unexpected(&tag
);
294 if !self.frameset_ok { return Done; }
296 let body
= unwrap_or_return
!(self.body_elem(), Done
).clone();
297 self.sink
.remove_from_parent(&body
);
299 // FIXME: can we get here in the fragment case?
300 // What to do with the first element then?
301 self.open_elems
.truncate(1);
302 self.insert_element_for(tag
);
303 self.mode
= InFrameset
;
308 if !self.template_modes
.is_empty() {
309 self.step(InTemplate
, token
)
311 self.check_body_end();
317 if self.in_scope_named(default_scope
, local_name
!("body")) {
318 self.check_body_end();
319 self.mode
= AfterBody
;
321 self.sink
.parse_error(Borrowed("</body> with no <body> in scope"));
327 if self.in_scope_named(default_scope
, local_name
!("body")) {
328 self.check_body_end();
329 Reprocess(AfterBody
, token
)
331 self.sink
.parse_error(Borrowed("</html> with no <body> in scope"));
336 tag @
<address
> <article
> <aside
> <blockquote
> <center
> <details
> <dialog
>
337 <dir
> <div
> <dl
> <fieldset
> <figcaption
> <figure
> <footer
> <header
>
338 <hgroup
> <main
> <nav
> <ol
> <p
> <section
> <summary
> <ul
> => {
339 self.close_p_element_in_button_scope();
340 self.insert_element_for(tag
);
345 self.close_p_element_in_button_scope();
346 self.insert_element_for(tag
);
350 tag @
<h1
> <h2
> <h3
> <h4
> <h5
> <h6
> => {
351 self.close_p_element_in_button_scope();
352 if self.current_node_in(heading_tag
) {
353 self.sink
.parse_error(Borrowed("nested heading tags"));
356 self.insert_element_for(tag
);
360 tag @
<pre
> <listing
> => {
361 self.close_p_element_in_button_scope();
362 self.insert_element_for(tag
);
363 self.ignore_lf
= true;
364 self.frameset_ok
= false;
369 if self.form_elem
.is_some() &&
370 !self.in_html_elem_named(local_name
!("template")) {
371 self.sink
.parse_error(Borrowed("nested forms"));
373 self.close_p_element_in_button_scope();
374 let elem
= self.insert_element_for(tag
);
375 if !self.in_html_elem_named(local_name
!("template")) {
376 self.form_elem
= Some(elem
);
382 tag @
<li
> <dd
> <dt
> => {
383 declare_tag_set
!(close_list
= "li");
384 declare_tag_set
!(close_defn
= "dd" "dt");
385 declare_tag_set
!(extra_special
= [special_tag
] - "address" "div" "p");
386 let list
= match tag
.name
{
387 local_name
!("li") => true,
388 local_name
!("dd") | local_name
!("dt") => false,
392 self.frameset_ok
= false;
394 let mut to_close
= None
;
395 for node
in self.open_elems
.iter().rev() {
396 let name
= self.sink
.elem_name(node
);
397 let can_close
= if list
{
403 to_close
= Some(name
.local
.clone());
406 if extra_special(name
) {
413 self.generate_implied_end_except(name
.clone());
414 self.expect_to_close(name
);
419 self.close_p_element_in_button_scope();
420 self.insert_element_for(tag
);
424 tag @
<plaintext
> => {
425 self.close_p_element_in_button_scope();
426 self.insert_element_for(tag
);
431 if self.in_scope_named(default_scope
, local_name
!("button")) {
432 self.sink
.parse_error(Borrowed("nested buttons"));
433 self.generate_implied_end(cursory_implied_end
);
434 self.pop_until_named(local_name
!("button"));
436 self.reconstruct_formatting();
437 self.insert_element_for(tag
);
438 self.frameset_ok
= false;
442 tag @
</address
> </article
> </aside
> </blockquote
> </button
> </center
>
443 </details
> </dialog
> </dir
> </div
> </dl
> </fieldset
> </figcaption
>
444 </figure
> </footer
> </header
> </hgroup
> </listing
> </main
> </menu
>
445 </nav
> </ol
> </pre
> </section
> </summary
> </ul
> => {
446 if !self.in_scope_named(default_scope
, tag
.name
.clone()) {
447 self.unexpected(&tag
);
449 self.generate_implied_end(cursory_implied_end
);
450 self.expect_to_close(tag
.name
);
456 if !self.in_html_elem_named(local_name
!("template")) {
457 // Can't use unwrap_or_return!() due to rust-lang/rust#16617.
458 let node
= match self.form_elem
.take() {
460 self.sink
.parse_error(Borrowed("Null form element pointer on </form>"));
465 if !self.in_scope(default_scope
, |n
| self.sink
.same_node(&node
, &n
)) {
466 self.sink
.parse_error(Borrowed("Form element not in scope on </form>"));
469 self.generate_implied_end(cursory_implied_end
);
470 let current
= self.current_node().clone();
471 self.remove_from_stack(&node
);
472 if !self.sink
.same_node(¤t
, &node
) {
473 self.sink
.parse_error(Borrowed("Bad open element on </form>"));
476 if !self.in_scope_named(default_scope
, local_name
!("form")) {
477 self.sink
.parse_error(Borrowed("Form element not in scope on </form>"));
480 self.generate_implied_end(cursory_implied_end
);
481 if !self.current_node_named(local_name
!("form")) {
482 self.sink
.parse_error(Borrowed("Bad open element on </form>"));
484 self.pop_until_named(local_name
!("form"));
490 if !self.in_scope_named(button_scope
, local_name
!("p")) {
491 self.sink
.parse_error(Borrowed("No <p> tag to close"));
492 self.insert_phantom(local_name
!("p"));
494 self.close_p_element();
498 tag @
</li
> </dd
> </dt
> => {
499 let in_scope
= if tag
.name
== local_name
!("li") {
500 self.in_scope_named(list_item_scope
, tag
.name
.clone())
502 self.in_scope_named(default_scope
, tag
.name
.clone())
505 self.generate_implied_end_except(tag
.name
.clone());
506 self.expect_to_close(tag
.name
);
508 self.sink
.parse_error(Borrowed("No matching tag to close"));
513 tag @
</h1
> </h2
> </h3
> </h4
> </h5
> </h6
> => {
514 if self.in_scope(default_scope
, |n
| self.elem_in(&n
, heading_tag
)) {
515 self.generate_implied_end(cursory_implied_end
);
516 if !self.current_node_named(tag
.name
) {
517 self.sink
.parse_error(Borrowed("Closing wrong heading tag"));
519 self.pop_until(heading_tag
);
521 self.sink
.parse_error(Borrowed("No heading tag to close"));
527 self.handle_misnested_a_tags(&tag
);
528 self.reconstruct_formatting();
529 self.create_formatting_element_for(tag
);
533 tag @
<b
> <big
> <code
> <em
> <font
> <i
> <s
> <small
> <strike
> <strong
> <tt
> <u
> => {
534 self.reconstruct_formatting();
535 self.create_formatting_element_for(tag
);
540 self.reconstruct_formatting();
541 if self.in_scope_named(default_scope
, local_name
!("nobr")) {
542 self.sink
.parse_error(Borrowed("Nested <nobr>"));
543 self.adoption_agency(local_name
!("nobr"));
544 self.reconstruct_formatting();
546 self.create_formatting_element_for(tag
);
550 tag @
</a
> </b
> </big
> </code
> </em
> </font
> </i
> </nobr
>
551 </s
> </small
> </strike
> </strong
> </tt
> </u
> => {
552 self.adoption_agency(tag
.name
);
556 tag @
<applet
> <marquee
> <object
> => {
557 self.reconstruct_formatting();
558 self.insert_element_for(tag
);
559 self.active_formatting
.push(Marker
);
560 self.frameset_ok
= false;
564 tag @
</applet
> </marquee
> </object
> => {
565 if !self.in_scope_named(default_scope
, tag
.name
.clone()) {
566 self.unexpected(&tag
);
568 self.generate_implied_end(cursory_implied_end
);
569 self.expect_to_close(tag
.name
);
570 self.clear_active_formatting_to_marker();
576 if self.quirks_mode
!= Quirks
{
577 self.close_p_element_in_button_scope();
579 self.insert_element_for(tag
);
580 self.frameset_ok
= false;
586 self.unexpected(&tag
);
587 self.step(InBody
, TagToken(Tag
{
594 tag @
<area
> <br
> <embed
> <img
> <keygen
> <wbr
> <input
> => {
595 let keep_frameset_ok
= match tag
.name
{
596 local_name
!("input") => self.is_type_hidden(&tag
),
599 self.reconstruct_formatting();
600 self.insert_and_pop_element_for(tag
);
601 if !keep_frameset_ok
{
602 self.frameset_ok
= false;
607 tag @
<param
> <source
> <track
> => {
608 self.insert_and_pop_element_for(tag
);
613 self.close_p_element_in_button_scope();
614 self.insert_and_pop_element_for(tag
);
615 self.frameset_ok
= false;
620 self.unexpected(&tag
);
621 self.step(InBody
, TagToken(Tag
{
622 name
: local_name
!("img"),
627 tag @
<textarea
> => {
628 self.ignore_lf
= true;
629 self.frameset_ok
= false;
630 self.parse_raw_data(tag
, Rcdata
)
634 self.close_p_element_in_button_scope();
635 self.reconstruct_formatting();
636 self.frameset_ok
= false;
637 self.parse_raw_data(tag
, Rawtext
)
641 self.frameset_ok
= false;
642 self.parse_raw_data(tag
, Rawtext
)
646 self.parse_raw_data(tag
, Rawtext
)
649 // <noscript> handled in wildcard case below
652 self.reconstruct_formatting();
653 self.insert_element_for(tag
);
654 self.frameset_ok
= false;
655 // NB: mode == InBody but possibly self.mode != mode, if
656 // we're processing "as in the rules for InBody".
657 self.mode
= match self.mode
{
658 InTable
| InCaption
| InTableBody
659 | InRow
| InCell
=> InSelectInTable
,
665 tag @
<optgroup
> <option
> => {
666 if self.current_node_named(local_name
!("option")) {
669 self.reconstruct_formatting();
670 self.insert_element_for(tag
);
674 tag @
<rb
> <rtc
> => {
675 if self.in_scope_named(default_scope
, local_name
!("ruby")) {
676 self.generate_implied_end(cursory_implied_end
);
678 if !self.current_node_named(local_name
!("ruby")) {
679 self.unexpected(&tag
);
681 self.insert_element_for(tag
);
686 if self.in_scope_named(default_scope
, local_name
!("ruby")) {
687 self.generate_implied_end_except(local_name
!("rtc"));
689 if !self.current_node_named(local_name
!("rtc")) && !self.current_node_named(local_name
!("ruby")) {
690 self.unexpected(&tag
);
692 self.insert_element_for(tag
);
696 tag @
<math
> => self.enter_foreign(tag
, ns
!(mathml
)),
698 tag @
<svg
> => self.enter_foreign(tag
, ns
!(svg
)),
700 <caption
> <col
> <colgroup
> <frame
> <head
>
701 <tbody
> <td
> <tfoot
> <th
> <thead
> <tr
> => {
702 self.unexpected(&token
);
707 if self.opts
.scripting_enabled
&& tag
.name
== local_name
!("noscript") {
708 self.parse_raw_data(tag
, Rawtext
)
710 self.reconstruct_formatting();
711 self.insert_element_for(tag
);
717 self.process_end_tag_in_body(tag
);
721 // FIXME: This should be unreachable, but match_token requires a
723 _
=> panic
!("impossible case in InBody mode"),
726 //§ parsing-main-incdata
727 Text
=> match_token
!(token
{
728 CharacterTokens(_
, text
) => self.append_text(text
),
731 self.unexpected(&token
);
732 if self.current_node_named(local_name
!("script")) {
733 let current
= current_node(&self.open_elems
);
734 self.sink
.mark_script_already_started(current
);
737 Reprocess(self.orig_mode
.take().unwrap(), token
)
741 let node
= self.pop();
742 self.mode
= self.orig_mode
.take().unwrap();
743 if tag
.name
== local_name
!("script") {
749 // The spec doesn't say what to do here.
750 // Other tokens are impossible?
751 _
=> panic
!("impossible case in Text mode"),
754 //§ parsing-main-intable
755 InTable
=> match_token
!(token
{
756 // FIXME: hack, should implement pat | pat for match_token instead
757 NullCharacterToken
=> self.process_chars_in_table(token
),
759 CharacterTokens(..) => self.process_chars_in_table(token
),
761 CommentToken(text
) => self.append_comment(text
),
764 self.pop_until_current(table_scope
);
765 self.active_formatting
.push(Marker
);
766 self.insert_element_for(tag
);
767 self.mode
= InCaption
;
771 tag @
<colgroup
> => {
772 self.pop_until_current(table_scope
);
773 self.insert_element_for(tag
);
774 self.mode
= InColumnGroup
;
779 self.pop_until_current(table_scope
);
780 self.insert_phantom(local_name
!("colgroup"));
781 Reprocess(InColumnGroup
, token
)
784 tag @
<tbody
> <tfoot
> <thead
> => {
785 self.pop_until_current(table_scope
);
786 self.insert_element_for(tag
);
787 self.mode
= InTableBody
;
792 self.pop_until_current(table_scope
);
793 self.insert_phantom(local_name
!("tbody"));
794 Reprocess(InTableBody
, token
)
798 self.unexpected(&token
);
799 if self.in_scope_named(table_scope
, local_name
!("table")) {
800 self.pop_until_named(local_name
!("table"));
801 Reprocess(self.reset_insertion_mode(), token
)
808 if self.in_scope_named(table_scope
, local_name
!("table")) {
809 self.pop_until_named(local_name
!("table"));
810 self.mode
= self.reset_insertion_mode();
812 self.unexpected(&token
);
817 </body
> </caption
> </col
> </colgroup
> </html
>
818 </tbody
> </td
> </tfoot
> </th
> </thead
> </tr
> =>
819 self.unexpected(&token
),
821 <style
> <script
> <template
> </template
>
822 => self.step(InHead
, token
),
825 self.unexpected(&tag
);
826 if self.is_type_hidden(&tag
) {
827 self.insert_and_pop_element_for(tag
);
830 self.foster_parent_in_body(TagToken(tag
))
835 self.unexpected(&tag
);
836 if !self.in_html_elem_named(local_name
!("template")) && self.form_elem
.is_none() {
837 self.form_elem
= Some(self.insert_and_pop_element_for(tag
));
842 EOFToken
=> self.step(InBody
, token
),
845 self.unexpected(&token
);
846 self.foster_parent_in_body(token
)
850 //§ parsing-main-intabletext
851 InTableText
=> match_token
!(token
{
852 NullCharacterToken
=> self.unexpected(&token
),
854 CharacterTokens(split
, text
) => {
855 self.pending_table_text
.push((split
, text
));
860 let pending
= replace(&mut self.pending_table_text
, vec
!());
861 let contains_nonspace
= pending
.iter().any(|&(split
, ref text
)| {
864 NotWhitespace
=> true,
865 NotSplit
=> any_not_whitespace(text
),
869 if contains_nonspace
{
870 self.sink
.parse_error(Borrowed("Non-space table text"));
871 for (split
, text
) in pending
.into_iter() {
872 match self.foster_parent_in_body(CharacterTokens(split
, text
)) {
874 _
=> panic
!("not prepared to handle this!"),
878 for (_
, text
) in pending
.into_iter() {
879 self.append_text(text
);
883 Reprocess(self.orig_mode
.take().unwrap(), token
)
887 //§ parsing-main-incaption
888 InCaption
=> match_token
!(token
{
889 tag @
<caption
> <col
> <colgroup
> <tbody
> <td
> <tfoot
>
890 <th
> <thead
> <tr
> </table
> </caption
> => {
891 if self.in_scope_named(table_scope
, local_name
!("caption")) {
892 self.generate_implied_end(cursory_implied_end
);
893 self.expect_to_close(local_name
!("caption"));
894 self.clear_active_formatting_to_marker();
896 Tag { kind: EndTag, name: local_name!("caption"), .. }
=> {
900 _
=> Reprocess(InTable
, TagToken(tag
))
903 self.unexpected(&tag
);
908 </body
> </col
> </colgroup
> </html
> </tbody
>
909 </td
> </tfoot
> </th
> </thead
> </tr
> => self.unexpected(&token
),
911 token
=> self.step(InBody
, token
),
914 //§ parsing-main-incolgroup
915 InColumnGroup
=> match_token
!(token
{
916 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
917 CharacterTokens(Whitespace
, text
) => self.append_text(text
),
918 CommentToken(text
) => self.append_comment(text
),
920 <html
> => self.step(InBody
, token
),
923 self.insert_and_pop_element_for(tag
);
928 if self.current_node_named(local_name
!("colgroup")) {
932 self.unexpected(&token
);
937 </col
> => self.unexpected(&token
),
939 <template
> </template
> => self.step(InHead
, token
),
941 EOFToken
=> self.step(InBody
, token
),
944 if self.current_node_named(local_name
!("colgroup")) {
946 Reprocess(InTable
, token
)
948 self.unexpected(&token
)
953 //§ parsing-main-intbody
954 InTableBody
=> match_token
!(token
{
956 self.pop_until_current(table_body_context
);
957 self.insert_element_for(tag
);
963 self.unexpected(&token
);
964 self.pop_until_current(table_body_context
);
965 self.insert_phantom(local_name
!("tr"));
966 Reprocess(InRow
, token
)
969 tag @
</tbody
> </tfoot
> </thead
> => {
970 if self.in_scope_named(table_scope
, tag
.name
.clone()) {
971 self.pop_until_current(table_body_context
);
975 self.unexpected(&tag
);
980 <caption
> <col
> <colgroup
> <tbody
> <tfoot
> <thead
> </table
> => {
981 declare_tag_set
!(table_outer
= "table" "tbody" "tfoot");
982 if self.in_scope(table_scope
, |e
| self.elem_in(&e
, table_outer
)) {
983 self.pop_until_current(table_body_context
);
985 Reprocess(InTable
, token
)
987 self.unexpected(&token
)
991 </body
> </caption
> </col
> </colgroup
> </html
> </td
> </th
> </tr
>
992 => self.unexpected(&token
),
994 token
=> self.step(InTable
, token
),
997 //§ parsing-main-intr
998 InRow
=> match_token
!(token
{
1000 self.pop_until_current(table_row_context
);
1001 self.insert_element_for(tag
);
1003 self.active_formatting
.push(Marker
);
1008 if self.in_scope_named(table_scope
, local_name
!("tr")) {
1009 self.pop_until_current(table_row_context
);
1010 let node
= self.pop();
1011 self.assert_named(&node
, local_name
!("tr"));
1012 self.mode
= InTableBody
;
1014 self.unexpected(&token
);
1019 <caption
> <col
> <colgroup
> <tbody
> <tfoot
> <thead
> <tr
> </table
> => {
1020 if self.in_scope_named(table_scope
, local_name
!("tr")) {
1021 self.pop_until_current(table_row_context
);
1022 let node
= self.pop();
1023 self.assert_named(&node
, local_name
!("tr"));
1024 Reprocess(InTableBody
, token
)
1026 self.unexpected(&token
)
1030 tag @
</tbody
> </tfoot
> </thead
> => {
1031 if self.in_scope_named(table_scope
, tag
.name
.clone()) {
1032 if self.in_scope_named(table_scope
, local_name
!("tr")) {
1033 self.pop_until_current(table_row_context
);
1034 let node
= self.pop();
1035 self.assert_named(&node
, local_name
!("tr"));
1036 Reprocess(InTableBody
, TagToken(tag
))
1041 self.unexpected(&tag
)
1045 </body
> </caption
> </col
> </colgroup
> </html
> </td
> </th
>
1046 => self.unexpected(&token
),
1048 token
=> self.step(InTable
, token
),
1051 //§ parsing-main-intd
1052 InCell
=> match_token
!(token
{
1053 tag @
</td
> </th
> => {
1054 if self.in_scope_named(table_scope
, tag
.name
.clone()) {
1055 self.generate_implied_end(cursory_implied_end
);
1056 self.expect_to_close(tag
.name
);
1057 self.clear_active_formatting_to_marker();
1060 self.unexpected(&tag
);
1065 <caption
> <col
> <colgroup
> <tbody
> <td
> <tfoot
> <th
> <thead
> <tr
> => {
1066 if self.in_scope(table_scope
, |n
| self.elem_in(&n
, td_th
)) {
1067 self.close_the_cell();
1068 Reprocess(InRow
, token
)
1070 self.unexpected(&token
)
1074 </body
> </caption
> </col
> </colgroup
> </html
>
1075 => self.unexpected(&token
),
1077 tag @
</table
> </tbody
> </tfoot
> </thead
> </tr
> => {
1078 if self.in_scope_named(table_scope
, tag
.name
.clone()) {
1079 self.close_the_cell();
1080 Reprocess(InRow
, TagToken(tag
))
1082 self.unexpected(&tag
)
1086 token
=> self.step(InBody
, token
),
1089 //§ parsing-main-inselect
1090 InSelect
=> match_token
!(token
{
1091 NullCharacterToken
=> self.unexpected(&token
),
1092 CharacterTokens(_
, text
) => self.append_text(text
),
1093 CommentToken(text
) => self.append_comment(text
),
1095 <html
> => self.step(InBody
, token
),
1098 if self.current_node_named(local_name
!("option")) {
1101 self.insert_element_for(tag
);
1105 tag @
<optgroup
> => {
1106 if self.current_node_named(local_name
!("option")) {
1109 if self.current_node_named(local_name
!("optgroup")) {
1112 self.insert_element_for(tag
);
1117 if self.open_elems
.len() >= 2
1118 && self.current_node_named(local_name
!("option"))
1119 && self.html_elem_named(&self.open_elems
[self.open_elems
.len() - 2],
1120 local_name
!("optgroup")) {
1123 if self.current_node_named(local_name
!("optgroup")) {
1126 self.unexpected(&token
);
1132 if self.current_node_named(local_name
!("option")) {
1135 self.unexpected(&token
);
1140 tag @
<select
> </select
> => {
1141 let in_scope
= self.in_scope_named(select_scope
, local_name
!("select"));
1143 if !in_scope
|| tag
.kind
== StartTag
{
1144 self.unexpected(&tag
);
1148 self.pop_until_named(local_name
!("select"));
1149 self.mode
= self.reset_insertion_mode();
1154 <input
> <keygen
> <textarea
> => {
1155 self.unexpected(&token
);
1156 if self.in_scope_named(select_scope
, local_name
!("select")) {
1157 self.pop_until_named(local_name
!("select"));
1158 Reprocess(self.reset_insertion_mode(), token
)
1164 <script
> <template
> </template
> => self.step(InHead
, token
),
1166 EOFToken
=> self.step(InBody
, token
),
1168 token
=> self.unexpected(&token
),
1171 //§ parsing-main-inselectintable
1172 InSelectInTable
=> match_token
!(token
{
1173 <caption
> <table
> <tbody
> <tfoot
> <thead
> <tr
> <td
> <th
> => {
1174 self.unexpected(&token
);
1175 self.pop_until_named(local_name
!("select"));
1176 Reprocess(self.reset_insertion_mode(), token
)
1179 tag @
</caption
> </table
> </tbody
> </tfoot
> </thead
> </tr
> </td
> </th
> => {
1180 self.unexpected(&tag
);
1181 if self.in_scope_named(table_scope
, tag
.name
.clone()) {
1182 self.pop_until_named(local_name
!("select"));
1183 Reprocess(self.reset_insertion_mode(), TagToken(tag
))
1189 token
=> self.step(InSelect
, token
),
1192 //§ parsing-main-intemplate
1193 InTemplate
=> match_token
!(token
{
1194 CharacterTokens(_
, _
) => self.step(InBody
, token
),
1195 CommentToken(_
) => self.step(InBody
, token
),
1197 <base
> <basefont
> <bgsound
> <link
> <meta
> <noframes
> <script
>
1198 <style
> <template
> <title
> </template
> => {
1199 self.step(InHead
, token
)
1202 <caption
> <colgroup
> <tbody
> <tfoot
> <thead
> => {
1203 self.template_modes
.pop();
1204 self.template_modes
.push(InTable
);
1205 Reprocess(InTable
, token
)
1209 self.template_modes
.pop();
1210 self.template_modes
.push(InColumnGroup
);
1211 Reprocess(InColumnGroup
, token
)
1215 self.template_modes
.pop();
1216 self.template_modes
.push(InTableBody
);
1217 Reprocess(InTableBody
, token
)
1221 self.template_modes
.pop();
1222 self.template_modes
.push(InRow
);
1223 Reprocess(InRow
, token
)
1227 if !self.in_html_elem_named(local_name
!("template")) {
1230 self.unexpected(&token
);
1231 self.pop_until_named(local_name
!("template"));
1232 self.clear_active_formatting_to_marker();
1233 self.template_modes
.pop();
1234 self.mode
= self.reset_insertion_mode();
1235 Reprocess(self.reset_insertion_mode(), token
)
1240 self.template_modes
.pop();
1241 self.template_modes
.push(InBody
);
1242 Reprocess(InBody
, TagToken(tag
))
1245 token
=> self.unexpected(&token
),
1248 //§ parsing-main-afterbody
1249 AfterBody
=> match_token
!(token
{
1250 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
1251 CharacterTokens(Whitespace
, _
) => self.step(InBody
, token
),
1252 CommentToken(text
) => self.append_comment_to_html(text
),
1254 <html
> => self.step(InBody
, token
),
1257 if self.is_fragment() {
1258 self.unexpected(&token
);
1260 self.mode
= AfterAfterBody
;
1265 EOFToken
=> self.stop_parsing(),
1268 self.unexpected(&token
);
1269 Reprocess(InBody
, token
)
1273 //§ parsing-main-inframeset
1274 InFrameset
=> match_token
!(token
{
1275 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
1276 CharacterTokens(Whitespace
, text
) => self.append_text(text
),
1277 CommentToken(text
) => self.append_comment(text
),
1279 <html
> => self.step(InBody
, token
),
1281 tag @
<frameset
> => {
1282 self.insert_element_for(tag
);
1287 if self.open_elems
.len() == 1 {
1288 self.unexpected(&token
);
1291 if !self.is_fragment() && !self.current_node_named(local_name
!("frameset")) {
1292 self.mode
= AfterFrameset
;
1299 self.insert_and_pop_element_for(tag
);
1303 <noframes
> => self.step(InHead
, token
),
1306 if self.open_elems
.len() != 1 {
1307 self.unexpected(&token
);
1312 token
=> self.unexpected(&token
),
1315 //§ parsing-main-afterframeset
1316 AfterFrameset
=> match_token
!(token
{
1317 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
1318 CharacterTokens(Whitespace
, text
) => self.append_text(text
),
1319 CommentToken(text
) => self.append_comment(text
),
1321 <html
> => self.step(InBody
, token
),
1324 self.mode
= AfterAfterFrameset
;
1328 <noframes
> => self.step(InHead
, token
),
1330 EOFToken
=> self.stop_parsing(),
1332 token
=> self.unexpected(&token
),
1335 //§ the-after-after-body-insertion-mode
1336 AfterAfterBody
=> match_token
!(token
{
1337 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
1338 CharacterTokens(Whitespace
, _
) => self.step(InBody
, token
),
1339 CommentToken(text
) => self.append_comment_to_doc(text
),
1341 <html
> => self.step(InBody
, token
),
1343 EOFToken
=> self.stop_parsing(),
1346 self.unexpected(&token
);
1347 Reprocess(InBody
, token
)
1351 //§ the-after-after-frameset-insertion-mode
1352 AfterAfterFrameset
=> match_token
!(token
{
1353 CharacterTokens(NotSplit
, text
) => SplitWhitespace(text
),
1354 CharacterTokens(Whitespace
, _
) => self.step(InBody
, token
),
1355 CommentToken(text
) => self.append_comment_to_doc(text
),
1357 <html
> => self.step(InBody
, token
),
1359 EOFToken
=> self.stop_parsing(),
1361 <noframes
> => self.step(InHead
, token
),
1363 token
=> self.unexpected(&token
),
1369 fn step_foreign(&mut self, token
: Token
) -> ProcessResult
<Handle
> {
1370 match_token
!(token
{
1371 NullCharacterToken
=> {
1372 self.unexpected(&token
);
1373 self.append_text("\u{fffd}".to_tendril())
1376 CharacterTokens(_
, text
) => {
1377 if any_not_whitespace(&text
) {
1378 self.frameset_ok
= false;
1380 self.append_text(text
)
1383 CommentToken(text
) => self.append_comment(text
),
1385 tag @
<b
> <big
> <blockquote
> <body
> <br
> <center
> <code
> <dd
> <div
> <dl
>
1386 <dt
> <em
> <embed
> <h1
> <h2
> <h3
> <h4
> <h5
> <h6
> <head
> <hr
> <i
>
1387 <img
> <li
> <listing
> <menu
> <meta
> <nobr
> <ol
> <p
> <pre
> <ruby
>
1388 <s
> <small
> <span
> <strong
> <strike
> <sub
> <sup
> <table
> <tt
>
1389 <u
> <ul
> <var
> => self.unexpected_start_tag_in_foreign_content(tag
),
1392 let unexpected
= tag
.attrs
.iter().any(|attr
| {
1393 matches
!(attr
.name
.expanded(),
1394 expanded_name
!("", "color") |
1395 expanded_name
!("", "face") |
1396 expanded_name
!("", "size"))
1399 self.unexpected_start_tag_in_foreign_content(tag
)
1401 self.foreign_start_tag(tag
)
1405 tag @
<_
> => self.foreign_start_tag(tag
),
1407 // FIXME(#118): </script> in SVG
1410 let mut first
= true;
1411 let mut stack_idx
= self.open_elems
.len() - 1;
1420 let node_name
= self.sink
.elem_name(&self.open_elems
[stack_idx
]);
1421 html
= *node_name
.ns
== ns
!(html
);
1422 eq
= node_name
.local
.eq_ignore_ascii_case(&tag
.name
);
1425 let mode
= self.mode
;
1426 return self.step(mode
, TagToken(tag
));
1430 self.open_elems
.truncate(stack_idx
);
1435 self.unexpected(&tag
);
1442 // FIXME: This should be unreachable, but match_token requires a
1444 _
=> panic
!("impossible case in foreign content"),