1use 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 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 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 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 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 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 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 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 let template = self.insert_foreign_element(tag.clone(), ns!(html), true);
173
174 let succeeded = self.attach_declarative_shadow(&tag, &shadow_host, &template);
177 if !succeeded {
178 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 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 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 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 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(¤t, &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 tag @ <select> => {
685 self.reconstruct_active_formatting_elements();
686 self.insert_element_for(tag);
687 self.frameset_ok.set(false);
688 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 _ => panic!("impossible case in InBody mode"),
757 }),
758
759 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 _ => panic!("impossible case in Text mode"),
786 }),
787
788 InsertionMode::InTable => match_token!(token {
790 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }
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 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 _ => panic!("impossible case in foreign content"),
1492 })
1493 }
1494}