use crate::Tag;
use crate::dom::{Data, NONE};
use super::{Builder, Mode, Ns, Scope, formatting, heading, special, void};
impl<'a> Builder<'a> {
pub(super) fn end_tag(&mut self, name: (usize, usize)) {
self.skip_lf = false;
self.scratch.clear();
let tag = self.tag_of(name);
if self.in_frameset_modes() {
self.frameset_end_tag(tag);
return;
}
if self.in_head_noscript() && self.head_noscript_end(tag) {
return;
}
if self.in_foreign() && matches!(tag, Tag::P | Tag::Br) {
while self.top_foreign() {
self.pop();
}
}
if self.stack.last().is_some_and(|o| o.ns != Ns::Html) {
let raw = &self.src[name.0..name.1];
for i in (0..self.stack.len()).rev() {
let o = self.stack[i];
if o.ns == Ns::Html {
break;
}
if self.element_name_eq(o.id, raw) {
self.pop_to(i);
return;
}
}
}
if let Some(cur) = self.current()
&& !cur.ns.foreign()
&& cur.tag == tag
&& (tag != Tag::Other || self.element_name_eq(cur.id, &self.src[name.0..name.1]))
&& !matches!(tag, Tag::Html | Tag::Body)
&& !formatting(tag)
{
if cur.id == self.head {
self.mode = Mode::AfterHead;
}
if cur.id == self.form {
self.form = NONE;
}
self.pop();
return;
}
if self.current_is(Tag::Colgroup)
&& !matches!(tag, Tag::Colgroup | Tag::Col | Tag::Template)
{
self.pop();
}
use Tag::*;
match self.mode {
Mode::InBody => {}
_ => match tag {
Head if self.mode == Mode::InHead => {
self.close_head();
return;
}
Template => {}
Body | Html | Br => {
self.ensure_body();
if tag != Br {
return;
}
}
_ => return,
},
}
match tag {
Body | Html | Head => {}
Form if self.in_template() => {
if let Some(i) = self.in_scope(Form, Scope::Default) {
self.pop_to(i);
}
}
Form => {
let form = std::mem::replace(&mut self.form, NONE);
if let Some(i) = self.in_scope(Form, Scope::Default)
&& self.stack[i].id == form
{
self.stack.remove(i);
}
}
t if formatting(t) => {
if !self.adoption_agency(tag) {
self.any_other_end(tag, name);
}
}
Br => {
self.frameset_ok = false;
self.reconstruct();
self.insert_html(Br, name);
}
P => match self.in_scope(P, Scope::Button) {
Some(i) => self.pop_to(i),
None => {
self.insert(P, name, Ns::Html, false, true);
}
},
Li => {
if let Some(i) = self.in_scope(Li, Scope::ListItem) {
self.pop_to(i);
}
}
H1 | H2 | H3 | H4 | H5 | H6 => {
if let Some(i) = self.find_in_scope(heading, Scope::Default) {
self.pop_to(i);
}
}
Table | Tbody | Thead | Tfoot | Tr | Td | Th | Caption | Colgroup => {
if let Some(i) = self.in_scope(tag, Scope::Table) {
self.pop_to(i);
}
}
Template => {
if let Some(i) = self
.stack
.iter()
.rposition(|o| o.tag == Template && o.ns == Ns::Html)
{
self.pop_to(i);
}
}
Dialog => {
if let Some(i) = self.in_scope(Dialog, Scope::Default) {
self.pop_to(i);
}
}
t if void(t) => {}
t if special(t) => {
if let Some(i) = self.in_scope(t, Scope::Default) {
self.pop_to(i);
}
}
_ => self.any_other_end(tag, name),
}
}
pub(super) fn any_other_end(&mut self, tag: Tag, name: (usize, usize)) {
let raw = &self.src[name.0..name.1];
for i in (0..self.stack.len()).rev() {
let o = self.stack[i];
let same = o.tag == tag && (tag != Tag::Other || self.element_name_eq(o.id, raw));
if same && !o.ns.foreign() {
self.pop_to(i);
return;
}
let stop = match o.ns {
Ns::Html => special(o.tag),
Ns::Integration | Ns::Annotation => true,
Ns::Foreign => false,
};
if stop {
return;
}
}
}
pub(super) fn element_name_eq(&self, id: u32, raw: &str) -> bool {
match self.nodes[id as usize].data {
Data::Element {
tag: Tag::Other,
name,
..
} => self.str(name).eq_ignore_ascii_case(raw),
Data::Element { tag, .. } => tag.name().eq_ignore_ascii_case(raw),
_ => false,
}
}
}