mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
feat(html/parser): Allow self-closing />
on non-void HTML elements via a flag (#8460)
This commit is contained in:
parent
f2ede40e38
commit
566063dca5
@ -638,6 +638,7 @@ fn html5lib_tests_verify(input: PathBuf) {
|
||||
let parser_config = ParserConfig {
|
||||
scripting_enabled,
|
||||
iframe_srcdoc: false,
|
||||
allow_self_closing: false,
|
||||
};
|
||||
let codegen_config = CodegenConfig {
|
||||
minify: false,
|
||||
|
@ -28,6 +28,8 @@ pub type PResult<T> = Result<T, Error>;
|
||||
pub struct ParserConfig {
|
||||
pub scripting_enabled: bool,
|
||||
pub iframe_srcdoc: bool,
|
||||
// #8459
|
||||
pub allow_self_closing: bool,
|
||||
}
|
||||
|
||||
enum Bookmark<RcNode> {
|
||||
@ -1110,6 +1112,27 @@ where
|
||||
_ => self.insertion_mode.clone(),
|
||||
};
|
||||
|
||||
/// Convenience: allow non-void HTML elements to self-close when
|
||||
/// a relevant config flag is set. It is achieved by processing the
|
||||
/// matching end tag right after the starting self-closing tag.
|
||||
macro_rules! maybe_allow_self_closing {
|
||||
($is_self_closing: ident, $tag_name: ident) => {
|
||||
if self.config.allow_self_closing && *$is_self_closing {
|
||||
let mut end_token_and_info = TokenAndInfo {
|
||||
span: token_and_info.span,
|
||||
acknowledged: false,
|
||||
token: Token::EndTag {
|
||||
tag_name: $tag_name.to_owned(),
|
||||
raw_tag_name: None,
|
||||
is_self_closing: false,
|
||||
attributes: vec![],
|
||||
},
|
||||
};
|
||||
self.process_token(&mut end_token_and_info, None)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match insertion_mode {
|
||||
// The "initial" insertion mode
|
||||
InsertionMode::Initial => {
|
||||
@ -1555,18 +1578,18 @@ where
|
||||
}
|
||||
// The "before head" insertion mode
|
||||
InsertionMode::BeforeHead => {
|
||||
let anything_else =
|
||||
|parser: &mut Parser<I>, token_and_info: &mut TokenAndInfo| -> PResult<()> {
|
||||
let element = parser.insert_html_element(
|
||||
&mut parser.create_fake_token_and_info("head", None),
|
||||
)?;
|
||||
let anything_else = |parser: &mut Parser<I>,
|
||||
token_and_info: &mut TokenAndInfo|
|
||||
-> PResult<()> {
|
||||
let element = parser
|
||||
.insert_html_element(&parser.create_fake_token_and_info("head", None))?;
|
||||
|
||||
parser.head_element_pointer = Some(element);
|
||||
parser.insertion_mode = InsertionMode::InHead;
|
||||
parser.process_token(token_and_info, None)?;
|
||||
parser.head_element_pointer = Some(element);
|
||||
parser.insertion_mode = InsertionMode::InHead;
|
||||
parser.process_token(token_and_info, None)?;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// When the user agent is to apply the rules for the "before head" insertion
|
||||
// mode, the user agent must handle the token as follows:
|
||||
@ -1811,7 +1834,11 @@ where
|
||||
// 9. Let the original insertion mode be the current insertion mode.
|
||||
//
|
||||
// 10. Switch the insertion mode to "text".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "script" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "script" => {
|
||||
let adjusted_insertion_location =
|
||||
self.get_appropriate_place_for_inserting_node(None)?;
|
||||
let node = self.create_element_for_token(
|
||||
@ -1828,6 +1855,7 @@ where
|
||||
self.input.set_input_state(State::ScriptData);
|
||||
self.original_insertion_mode = self.insertion_mode.clone();
|
||||
self.insertion_mode = InsertionMode::Text;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "head"
|
||||
//
|
||||
@ -1861,13 +1889,18 @@ where
|
||||
//
|
||||
// Push "in template" onto the stack of template insertion modes so that it is
|
||||
// the new current template insertion mode.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "template" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "template" => {
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.active_formatting_elements.insert_marker();
|
||||
self.frameset_ok = false;
|
||||
self.insertion_mode = InsertionMode::InTemplate;
|
||||
self.template_insertion_mode_stack
|
||||
.push(InsertionMode::InTemplate);
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "template"
|
||||
//
|
||||
@ -2095,9 +2128,9 @@ where
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut body_token = parser.create_fake_token_and_info("body", span);
|
||||
let body_token = parser.create_fake_token_and_info("body", span);
|
||||
|
||||
parser.insert_html_element(&mut body_token)?;
|
||||
parser.insert_html_element(&body_token)?;
|
||||
parser.insertion_mode = InsertionMode::InBody;
|
||||
parser.process_token(token_and_info, None)?;
|
||||
|
||||
@ -2143,19 +2176,29 @@ where
|
||||
// Set the frameset-ok flag to "not ok".
|
||||
//
|
||||
// Switch the insertion mode to "in body".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "body" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "body" => {
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.frameset_ok = false;
|
||||
self.insertion_mode = InsertionMode::InBody;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "frameset"
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
//
|
||||
// Switch the insertion mode to "in frameset".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "frameset" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "frameset" => {
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InFrameset;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "base", "basefont", "bgsound", "link",
|
||||
// "meta", "noframes", "script", "style", "template", "title"
|
||||
@ -2693,40 +2736,44 @@ where
|
||||
// element.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(
|
||||
&**tag_name,
|
||||
"address"
|
||||
| "article"
|
||||
| "aside"
|
||||
| "blockquote"
|
||||
| "center"
|
||||
| "details"
|
||||
| "dialog"
|
||||
| "dir"
|
||||
| "div"
|
||||
| "dl"
|
||||
| "fieldset"
|
||||
| "figcaption"
|
||||
| "figure"
|
||||
| "footer"
|
||||
| "header"
|
||||
| "hgroup"
|
||||
| "main"
|
||||
| "menu"
|
||||
| "nav"
|
||||
| "ol"
|
||||
| "p"
|
||||
| "section"
|
||||
| "summary"
|
||||
| "ul"
|
||||
) =>
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(
|
||||
&**tag_name,
|
||||
"address"
|
||||
| "article"
|
||||
| "aside"
|
||||
| "blockquote"
|
||||
| "center"
|
||||
| "details"
|
||||
| "dialog"
|
||||
| "dir"
|
||||
| "div"
|
||||
| "dl"
|
||||
| "fieldset"
|
||||
| "figcaption"
|
||||
| "figure"
|
||||
| "footer"
|
||||
| "header"
|
||||
| "hgroup"
|
||||
| "main"
|
||||
| "menu"
|
||||
| "nav"
|
||||
| "ol"
|
||||
| "p"
|
||||
| "section"
|
||||
| "summary"
|
||||
| "ul"
|
||||
) =>
|
||||
{
|
||||
if self.open_elements_stack.has_in_button_scope("p") {
|
||||
self.close_p_element(token_and_info, false);
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6"
|
||||
//
|
||||
@ -2738,9 +2785,11 @@ where
|
||||
// off the stack of open elements.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(&**tag_name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6") =>
|
||||
{
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "h1" | "h2" | "h3" | "h4" | "h5" | "h6") => {
|
||||
if self.open_elements_stack.has_in_button_scope("p") {
|
||||
self.close_p_element(token_and_info, false);
|
||||
}
|
||||
@ -2763,6 +2812,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "pre", "listing"
|
||||
//
|
||||
@ -2807,7 +2857,11 @@ where
|
||||
// Insert an HTML element for the token, and, if there is no template element on
|
||||
// the stack of open elements, set the form element pointer to point to the
|
||||
// element created.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "form" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "form" => {
|
||||
if self.form_element_pointer.is_some()
|
||||
&& !self.open_elements_stack.contains_template_element()
|
||||
{
|
||||
@ -2826,6 +2880,8 @@ where
|
||||
if !self.open_elements_stack.contains_template_element() {
|
||||
self.form_element_pointer = Some(element);
|
||||
}
|
||||
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "li"
|
||||
//
|
||||
@ -2856,7 +2912,11 @@ where
|
||||
// close a p element.
|
||||
//
|
||||
// Finally, insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "li" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "li" => {
|
||||
self.frameset_ok = false;
|
||||
|
||||
// Initialise node to be the current node (the bottommost node of
|
||||
@ -2907,6 +2967,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "dd", "dt"
|
||||
//
|
||||
@ -2948,7 +3009,11 @@ where
|
||||
// close a p element.
|
||||
//
|
||||
// Finally, insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if matches!(&**tag_name, "dd" | "dt") => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "dd" | "dt") => {
|
||||
self.frameset_ok = false;
|
||||
|
||||
// Initialise node to be the current node (the bottommost node of
|
||||
@ -3022,6 +3087,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "plaintext"
|
||||
//
|
||||
@ -3266,8 +3332,7 @@ where
|
||||
));
|
||||
|
||||
self.insert_html_element(
|
||||
&mut self
|
||||
.create_fake_token_and_info("p", Some(token_and_info.span)),
|
||||
&self.create_fake_token_and_info("p", Some(token_and_info.span)),
|
||||
)?;
|
||||
}
|
||||
|
||||
@ -3426,7 +3491,11 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token. Push onto the list of active formatting
|
||||
// elements that element.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "a" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "a" => {
|
||||
if !self.active_formatting_elements.items.is_empty() {
|
||||
let mut node = None;
|
||||
|
||||
@ -3468,6 +3537,8 @@ where
|
||||
element,
|
||||
token_and_info.clone(),
|
||||
));
|
||||
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "b", "big", "code", "em", "font", "i",
|
||||
// "s", "small", "strike", "strong", "tt", "u"
|
||||
@ -3476,21 +3547,24 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token. Push onto the list of active formatting
|
||||
// elements that element.
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(
|
||||
&**tag_name,
|
||||
"b" | "big"
|
||||
| "code"
|
||||
| "em"
|
||||
| "font"
|
||||
| "i"
|
||||
| "s"
|
||||
| "small"
|
||||
| "strike"
|
||||
| "strong"
|
||||
| "tt"
|
||||
| "u"
|
||||
) =>
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(
|
||||
&**tag_name,
|
||||
"b" | "big"
|
||||
| "code"
|
||||
| "em"
|
||||
| "font"
|
||||
| "i"
|
||||
| "s"
|
||||
| "small"
|
||||
| "strike"
|
||||
| "strong"
|
||||
| "tt"
|
||||
| "u"
|
||||
) =>
|
||||
{
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
|
||||
@ -3501,6 +3575,8 @@ where
|
||||
element,
|
||||
token_and_info.clone(),
|
||||
));
|
||||
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "nobr"
|
||||
//
|
||||
@ -3512,7 +3588,11 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token. Push onto the list of active formatting
|
||||
// elements that element.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "nobr" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "nobr" => {
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
|
||||
if self.open_elements_stack.has_in_scope("nobr") {
|
||||
@ -3532,6 +3612,7 @@ where
|
||||
element,
|
||||
token_and_info.clone(),
|
||||
));
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is one of: "a", "b", "big", "code", "em", "font",
|
||||
// "i", "nobr", "s", "small", "strike", "strong", "tt", "u"
|
||||
@ -3630,7 +3711,11 @@ where
|
||||
// Set the frameset-ok flag to "not ok".
|
||||
//
|
||||
// Switch the insertion mode to "in table".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "table" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "table" => {
|
||||
if get_document_mode!(self.document.as_ref().unwrap())
|
||||
!= DocumentMode::Quirks
|
||||
&& self.open_elements_stack.has_in_button_scope("p")
|
||||
@ -3641,6 +3726,7 @@ where
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.frameset_ok = false;
|
||||
self.insertion_mode = InsertionMode::InTable;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "br"
|
||||
//
|
||||
@ -3659,7 +3745,7 @@ where
|
||||
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.insert_html_element(
|
||||
&mut self.create_fake_token_and_info("br", Some(token_and_info.span)),
|
||||
&self.create_fake_token_and_info("br", Some(token_and_info.span)),
|
||||
)?;
|
||||
self.open_elements_stack.pop();
|
||||
|
||||
@ -3865,7 +3951,11 @@ where
|
||||
// Set the frameset-ok flag to "not ok".
|
||||
//
|
||||
// Follow the generic raw text element parsing algorithm.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "xmp" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "xmp" => {
|
||||
if self.open_elements_stack.has_in_button_scope("p") {
|
||||
self.close_p_element(token_and_info, false);
|
||||
}
|
||||
@ -3873,28 +3963,36 @@ where
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.frameset_ok = false;
|
||||
self.parse_generic_text_element(token_and_info, true)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "iframe"
|
||||
//
|
||||
// Set the frameset-ok flag to "not ok".
|
||||
//
|
||||
// Follow the generic raw text element parsing algorithm.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "iframe" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "iframe" => {
|
||||
self.frameset_ok = false;
|
||||
self.parse_generic_text_element(token_and_info, true)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "noembed"
|
||||
//
|
||||
// A start tag whose tag name is "noscript", if the scripting flag is enabled
|
||||
//
|
||||
// Follow the generic raw text element parsing algorithm.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "noembed" => {
|
||||
self.parse_generic_text_element(token_and_info, true)?;
|
||||
}
|
||||
Token::StartTag { tag_name, .. }
|
||||
if tag_name == "noscript" && self.config.scripting_enabled =>
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "noembed"
|
||||
|| (tag_name == "noscript" && self.config.scripting_enabled) =>
|
||||
{
|
||||
self.parse_generic_text_element(token_and_info, true)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "select"
|
||||
//
|
||||
@ -3907,7 +4005,11 @@ where
|
||||
// If the insertion mode is one of "in table", "in caption", "in table body",
|
||||
// "in row", or "in cell", then switch the insertion mode to "in select in
|
||||
// table". Otherwise, switch the insertion mode to "in select".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "select" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "select" => {
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.frameset_ok = false;
|
||||
@ -3924,6 +4026,8 @@ where
|
||||
self.insertion_mode = InsertionMode::InSelect;
|
||||
}
|
||||
}
|
||||
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "optgroup", "option"
|
||||
//
|
||||
@ -3933,9 +4037,11 @@ where
|
||||
// Reconstruct the active formatting elements, if any.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(&**tag_name, "optgroup" | "option") =>
|
||||
{
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "optgroup" | "option") => {
|
||||
match self.open_elements_stack.items.last() {
|
||||
Some(node) if is_html_element!(node, "option") => {
|
||||
self.open_elements_stack.pop();
|
||||
@ -3945,6 +4051,7 @@ where
|
||||
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "rb", "rtc"
|
||||
//
|
||||
@ -3953,7 +4060,11 @@ where
|
||||
// parse error.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if matches!(&**tag_name, "rb" | "rtc") => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "rb" | "rtc") => {
|
||||
let is_scope = self.open_elements_stack.has_in_scope("ruby");
|
||||
|
||||
if is_scope {
|
||||
@ -3978,6 +4089,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "rp", "rt"
|
||||
//
|
||||
@ -3986,7 +4098,11 @@ where
|
||||
// rtc element or a ruby element, this is a parse error.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if matches!(&**tag_name, "rp" | "rt") => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "rp" | "rt") => {
|
||||
let in_scope = self.open_elements_stack.has_in_scope("ruby");
|
||||
|
||||
if in_scope {
|
||||
@ -4012,6 +4128,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "math"
|
||||
//
|
||||
@ -4112,9 +4229,14 @@ where
|
||||
// Reconstruct the active formatting elements, if any.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { .. } => {
|
||||
Token::StartTag {
|
||||
is_self_closing,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// Any other end tag
|
||||
Token::EndTag { .. } => {
|
||||
@ -4445,11 +4567,16 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token, then switch the insertion mode to "in
|
||||
// caption".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "caption" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "caption" => {
|
||||
self.open_elements_stack.clear_back_to_table_context();
|
||||
self.active_formatting_elements.insert_marker();
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InCaption;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "colgroup"
|
||||
//
|
||||
@ -4457,10 +4584,15 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token, then switch the insertion mode to "in
|
||||
// column group".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "colgroup" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "colgroup" => {
|
||||
self.open_elements_stack.clear_back_to_table_context();
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InColumnGroup;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "col"
|
||||
//
|
||||
@ -4473,7 +4605,7 @@ where
|
||||
Token::StartTag { tag_name, .. } if tag_name == "col" => {
|
||||
self.open_elements_stack.clear_back_to_table_context();
|
||||
self.insert_html_element(
|
||||
&mut self.create_fake_token_and_info("colgroup", None),
|
||||
&self.create_fake_token_and_info("colgroup", None),
|
||||
)?;
|
||||
self.insertion_mode = InsertionMode::InColumnGroup;
|
||||
self.process_token(token_and_info, None)?;
|
||||
@ -4484,12 +4616,15 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token, then switch the insertion mode to "in
|
||||
// table body".
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(&**tag_name, "tbody" | "tfoot" | "thead") =>
|
||||
{
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "tbody" | "tfoot" | "thead") => {
|
||||
self.open_elements_stack.clear_back_to_table_context();
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InTableBody;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "td", "th", "tr"
|
||||
//
|
||||
@ -4503,9 +4638,7 @@ where
|
||||
if matches!(&**tag_name, "td" | "th" | "tr") =>
|
||||
{
|
||||
self.open_elements_stack.clear_back_to_table_context();
|
||||
self.insert_html_element(
|
||||
&mut self.create_fake_token_and_info("tbody", None),
|
||||
)?;
|
||||
self.insert_html_element(&self.create_fake_token_and_info("tbody", None))?;
|
||||
self.insertion_mode = InsertionMode::InTableBody;
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
@ -5089,10 +5222,15 @@ where
|
||||
//
|
||||
// Insert an HTML element for the token, then switch the insertion mode to "in
|
||||
// row".
|
||||
Token::StartTag { tag_name, .. } if tag_name == "tr" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "tr" => {
|
||||
self.open_elements_stack.clear_back_to_table_body_context();
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InRow;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is one of: "th", "td"
|
||||
//
|
||||
@ -5110,7 +5248,7 @@ where
|
||||
ErrorKind::StartTagInTableBody(tag_name.clone()),
|
||||
));
|
||||
self.open_elements_stack.clear_back_to_table_body_context();
|
||||
self.insert_html_element(&mut self.create_fake_token_and_info("tr", None))?;
|
||||
self.insert_html_element(&self.create_fake_token_and_info("tr", None))?;
|
||||
self.insertion_mode = InsertionMode::InRow;
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
@ -5233,11 +5371,16 @@ where
|
||||
// cell".
|
||||
//
|
||||
// Insert a marker at the end of the list of active formatting elements.
|
||||
Token::StartTag { tag_name, .. } if matches!(&**tag_name, "th" | "td") => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if matches!(&**tag_name, "th" | "td") => {
|
||||
self.open_elements_stack.clear_back_to_table_row_context();
|
||||
self.insert_html_element(token_and_info)?;
|
||||
self.insertion_mode = InsertionMode::InCell;
|
||||
self.active_formatting_elements.insert_marker();
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "tr"
|
||||
//
|
||||
@ -5554,7 +5697,11 @@ where
|
||||
// open elements.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "option" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "option" => {
|
||||
match self.open_elements_stack.items.last() {
|
||||
Some(node) if is_html_element!(node, "option") => {
|
||||
self.open_elements_stack.pop();
|
||||
@ -5563,6 +5710,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// A start tag whose tag name is "optgroup"
|
||||
//
|
||||
@ -5573,7 +5721,11 @@ where
|
||||
// open elements.
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "optgroup" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "optgroup" => {
|
||||
match self.open_elements_stack.items.last() {
|
||||
Some(node) if is_html_element!(node, "option") => {
|
||||
self.open_elements_stack.pop();
|
||||
@ -5589,6 +5741,7 @@ where
|
||||
}
|
||||
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "optgroup"
|
||||
//
|
||||
@ -6173,8 +6326,13 @@ where
|
||||
// A start tag whose tag name is "frameset"
|
||||
//
|
||||
// Insert an HTML element for the token.
|
||||
Token::StartTag { tag_name, .. } if tag_name == "frameset" => {
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
is_self_closing,
|
||||
..
|
||||
} if tag_name == "frameset" => {
|
||||
self.insert_html_element(token_and_info)?;
|
||||
maybe_allow_self_closing!(is_self_closing, tag_name);
|
||||
}
|
||||
// An end tag whose tag name is "frameset"
|
||||
//
|
||||
@ -6609,7 +6767,7 @@ where
|
||||
// 4. Set node to the previous entry in the stack of open elements.
|
||||
//
|
||||
// 5. Return to the step labeled loop.
|
||||
fn any_other_end_tag_for_in_body_insertion_mode(&mut self, token_and_info: &mut TokenAndInfo) {
|
||||
fn any_other_end_tag_for_in_body_insertion_mode(&mut self, token_and_info: &TokenAndInfo) {
|
||||
let mut match_idx = None;
|
||||
let tag_name = match &token_and_info.token {
|
||||
Token::StartTag { tag_name, .. } | Token::EndTag { tag_name, .. } => tag_name,
|
||||
@ -7079,7 +7237,7 @@ where
|
||||
// algorithms for dealing with misnested content.
|
||||
fn run_the_adoption_agency_algorithm(
|
||||
&mut self,
|
||||
token_and_info: &mut TokenAndInfo,
|
||||
token_and_info: &TokenAndInfo,
|
||||
is_closing: bool,
|
||||
) -> PResult<()> {
|
||||
// 1.
|
||||
@ -7474,14 +7632,14 @@ where
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut token_and_info = match self.active_formatting_elements.items[entry_index] {
|
||||
let token_and_info = match self.active_formatting_elements.items[entry_index] {
|
||||
ActiveFormattingElement::Element(_, ref t) => t.clone(),
|
||||
ActiveFormattingElement::Marker => {
|
||||
panic!("Found marker during formatting element reconstruction")
|
||||
}
|
||||
};
|
||||
|
||||
let new_element = self.insert_html_element(&mut token_and_info)?;
|
||||
let new_element = self.insert_html_element(&token_and_info)?;
|
||||
|
||||
self.active_formatting_elements.items[entry_index] =
|
||||
ActiveFormattingElement::Element(new_element, token_and_info);
|
||||
@ -7540,7 +7698,7 @@ where
|
||||
// invoked in response to a start tag token.
|
||||
fn parse_generic_text_element(
|
||||
&mut self,
|
||||
token_and_info: &mut TokenAndInfo,
|
||||
token_and_info: &TokenAndInfo,
|
||||
is_raw_text_element_algorithm: bool,
|
||||
) -> PResult<()> {
|
||||
// Insert an HTML element for the token.
|
||||
@ -7564,7 +7722,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_p_element(&mut self, token_and_info: &mut TokenAndInfo, is_close_p: bool) {
|
||||
fn close_p_element(&mut self, token_and_info: &TokenAndInfo, is_close_p: bool) {
|
||||
// When the steps above say the user agent is to close a p element, it means
|
||||
// that the user agent must run the following steps:
|
||||
|
||||
@ -8334,13 +8492,13 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_html_element(&mut self, token_and_info: &mut TokenAndInfo) -> PResult<RcNode> {
|
||||
fn insert_html_element(&mut self, token_and_info: &TokenAndInfo) -> PResult<RcNode> {
|
||||
self.insert_foreign_element(token_and_info, Namespace::HTML, None)
|
||||
}
|
||||
|
||||
fn insert_foreign_element(
|
||||
&mut self,
|
||||
token_and_info: &mut TokenAndInfo,
|
||||
token_and_info: &TokenAndInfo,
|
||||
namespace: Namespace,
|
||||
adjust_attributes: Option<AdjustAttributes>,
|
||||
) -> PResult<RcNode> {
|
||||
|
@ -721,6 +721,7 @@ fn html5lib_test_tree_construction(input: PathBuf) {
|
||||
let config = ParserConfig {
|
||||
scripting_enabled,
|
||||
iframe_srcdoc: false,
|
||||
allow_self_closing: false,
|
||||
};
|
||||
let mut parser = Parser::new(lexer, config);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user