mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 06:05:02 +03:00
fix(html/parser): Fix parsing of foreign elements (#4422)
This commit is contained in:
parent
747b8a1889
commit
4ccbdf2a7d
@ -6,7 +6,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
|
||||
<img x="90" y="-65" width="128" height="146" transform="rotate(45)" xlink:href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png">
|
||||
<image x="90" y="-65" width="128" height="146" transform="rotate(45)" xlink:href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png"></image>
|
||||
</svg>
|
||||
|
||||
|
||||
</svg></body></html>
|
||||
</body></html>
|
||||
|
@ -6,7 +6,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<svg version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink width=200 height=200>
|
||||
<img x=90 y=-65 width=128 height=146 transform=rotate(45) xlink:href=https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png>
|
||||
<image x=90 y=-65 width=128 height=146 transform=rotate(45) xlink:href=https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png></image>
|
||||
</svg>
|
||||
|
||||
|
||||
</svg></body></html>
|
||||
</body></html>
|
||||
|
@ -93,6 +93,7 @@ where
|
||||
input: Buffer<I>,
|
||||
stopped: bool,
|
||||
is_fragment_case: bool,
|
||||
context_element: Option<RcNode>,
|
||||
insertion_mode: InsertionMode,
|
||||
original_insertion_mode: InsertionMode,
|
||||
template_insertion_mode_stack: Vec<InsertionMode>,
|
||||
@ -118,6 +119,7 @@ where
|
||||
input: Buffer::new(input),
|
||||
stopped: false,
|
||||
is_fragment_case: false,
|
||||
context_element: None,
|
||||
insertion_mode: Default::default(),
|
||||
original_insertion_mode: Default::default(),
|
||||
template_insertion_mode_stack: vec![],
|
||||
@ -295,15 +297,474 @@ where
|
||||
//
|
||||
// Process the token according to the rules given in the section corresponding
|
||||
// to the current insertion mode in HTML content.
|
||||
// TODO
|
||||
if false {
|
||||
todo!()
|
||||
let adjusted_current_node = self.get_adjusted_current_node();
|
||||
|
||||
if self.open_elements_stack.items.is_empty()
|
||||
|| is_element_in_html_namespace(adjusted_current_node)
|
||||
|| (is_mathml_text_integration_point(adjusted_current_node)
|
||||
&& matches!(&token_and_info.token, Token::StartTag { tag_name, .. } if &*tag_name != "mglyph" && &*tag_name != "malignmark"))
|
||||
|| (is_mathml_text_integration_point(adjusted_current_node)
|
||||
&& matches!(&token_and_info.token, Token::Character { .. }))
|
||||
|| (is_mathml_annotation_xml(adjusted_current_node)
|
||||
&& matches!(&token_and_info.token, Token::StartTag { tag_name, .. } if &*tag_name == "svg"))
|
||||
|| (is_html_integration_point(adjusted_current_node)
|
||||
&& matches!(&token_and_info.token, Token::StartTag { .. }))
|
||||
|| (is_html_integration_point(adjusted_current_node)
|
||||
&& matches!(&token_and_info.token, Token::Character { .. }))
|
||||
|| matches!(&token_and_info.token, Token::Eof)
|
||||
{
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
// Otherwise
|
||||
// Process the token according to the rules given in the section for parsing tokens in
|
||||
// foreign content.
|
||||
else {
|
||||
self.process_token(token_and_info, None)?
|
||||
self.process_token_in_foreign_content(token_and_info)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// The adjusted current node is the context element if the parser was created as
|
||||
// part of the HTML fragment parsing algorithm and the stack of open elements
|
||||
// has only one element in it (fragment case); otherwise, the adjusted current
|
||||
// node is the current node.
|
||||
fn get_adjusted_current_node(&self) -> Option<&RcNode> {
|
||||
if self.is_fragment_case && self.open_elements_stack.items.len() == 1 {
|
||||
self.context_element.as_ref();
|
||||
}
|
||||
|
||||
self.open_elements_stack.items.last()
|
||||
}
|
||||
|
||||
fn process_token_in_foreign_content(
|
||||
&mut self,
|
||||
token_and_info: &mut TokenAndInfo,
|
||||
) -> PResult<()> {
|
||||
let TokenAndInfo { token, .. } = &token_and_info;
|
||||
|
||||
match token {
|
||||
// A character token that is U+0000 NULL
|
||||
//
|
||||
// Parse error. Insert a U+FFFD REPLACEMENT CHARACTER character.
|
||||
Token::Character { value, raw } if *value == '\x00' => {
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
|
||||
token_and_info.token = Token::Character {
|
||||
value: '\u{FFFD}',
|
||||
raw: raw.clone(),
|
||||
};
|
||||
|
||||
self.insert_character(token_and_info)?;
|
||||
}
|
||||
// A character token that is one of U+0009 CHARACTER TABULATION, U+000A LINE FEED (LF),
|
||||
// U+000C FORM FEED (FF), U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
|
||||
//
|
||||
// Insert the token's character.
|
||||
Token::Character { value, .. }
|
||||
if matches!(value, '\x09' | '\x0A' | '\x0C' | '\x0D' | '\x20') =>
|
||||
{
|
||||
self.insert_character(token_and_info)?;
|
||||
}
|
||||
// Any other character token
|
||||
//
|
||||
// Insert the token's character.
|
||||
//
|
||||
// Set the frameset-ok flag to "not ok".
|
||||
Token::Character { .. } => {
|
||||
self.insert_character(token_and_info)?;
|
||||
|
||||
self.frameset_ok = false;
|
||||
}
|
||||
// A comment token
|
||||
//
|
||||
// Insert a comment.
|
||||
Token::Comment { .. } => {
|
||||
self.insert_comment(token_and_info)?;
|
||||
}
|
||||
// A DOCTYPE token
|
||||
// Parse error. Ignore the token.
|
||||
Token::Doctype { .. } => {
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
}
|
||||
// A start tag whose tag name is one of: "b", "big", "blockquote", "body", "br",
|
||||
// "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4",
|
||||
// "h5", "h6", "head", "hr", "i", "img", "li", "listing", "menu", "meta", "nobr", "ol",
|
||||
// "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table",
|
||||
// "tt", "u", "ul", "var"
|
||||
//
|
||||
// A start tag whose tag name is "font", if the token has any attributes named "color",
|
||||
// "face", or "size"
|
||||
//
|
||||
// An end tag whose tag name is "br", "p"
|
||||
//
|
||||
// Parse error.
|
||||
//
|
||||
// While the current node is not a MathML text integration point, an HTML integration
|
||||
// point, or an element in the HTML namespace, pop elements from the stack of open
|
||||
// elements.
|
||||
//
|
||||
// Reprocess the token according to the rules given in the section corresponding to the
|
||||
// current insertion mode in HTML content.
|
||||
Token::StartTag { tag_name, .. }
|
||||
if matches!(
|
||||
&**tag_name,
|
||||
"b" | "big"
|
||||
| "blockquote"
|
||||
| "body"
|
||||
| "br"
|
||||
| "center"
|
||||
| "code"
|
||||
| "dd"
|
||||
| "div"
|
||||
| "dl"
|
||||
| "dt"
|
||||
| "em"
|
||||
| "embed"
|
||||
| "h1"
|
||||
| "h2"
|
||||
| "h3"
|
||||
| "h4"
|
||||
| "h5"
|
||||
| "h6"
|
||||
| "head"
|
||||
| "hr"
|
||||
| "i"
|
||||
| "img"
|
||||
| "li"
|
||||
| "listing"
|
||||
| "menu"
|
||||
| "meta"
|
||||
| "nobr"
|
||||
| "ol"
|
||||
| "p"
|
||||
| "pre"
|
||||
| "ruby"
|
||||
| "s"
|
||||
| "small"
|
||||
| "span"
|
||||
| "strong"
|
||||
| "strike"
|
||||
| "sub"
|
||||
| "sup"
|
||||
| "table"
|
||||
| "tt"
|
||||
| "u"
|
||||
| "ul"
|
||||
| "var"
|
||||
) =>
|
||||
{
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
self.open_elements_stack.pop_until_in_foreign();
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
attributes,
|
||||
..
|
||||
} if tag_name == "font"
|
||||
&& attributes
|
||||
.iter()
|
||||
.any(|attribute| matches!(&*attribute.name, "color" | "face" | "size")) =>
|
||||
{
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
self.open_elements_stack.pop_until_in_foreign();
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
Token::EndTag { tag_name, .. } if matches!(tag_name.as_ref(), "br" | "p") => {
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
self.open_elements_stack.pop_until_in_foreign();
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
// Any other start tag
|
||||
//
|
||||
// If the adjusted current node is an element in the MathML namespace, adjust MathML
|
||||
// attributes for the token. (This fixes the case of MathML attributes that are not all
|
||||
// lowercase.)
|
||||
//
|
||||
// If the adjusted current node is an element in the SVG namespace, and the token's tag
|
||||
// name is one of the ones in the first column of the following table, change the tag
|
||||
// name to the name given in the corresponding cell in the second column. (This fixes
|
||||
// the case of SVG elements that are not all lowercase.)
|
||||
//
|
||||
// Tag name Element name
|
||||
// altglyph altGlyph
|
||||
// altglyphdef altGlyphDef
|
||||
// altglyphitem altGlyphItem
|
||||
// animatecolor animateColor
|
||||
// animatemotion animateMotion
|
||||
// animatetransform animateTransform
|
||||
// clippath clipPath
|
||||
// feblend feBlend
|
||||
// fecolormatrix feColorMatrix
|
||||
// fecomponenttransfer feComponentTransfer
|
||||
// fecomposite feComposite
|
||||
// feconvolvematrix feConvolveMatrix
|
||||
// fediffuselighting feDiffuseLighting
|
||||
// fedisplacementmap feDisplacementMap
|
||||
// fedistantlight feDistantLight
|
||||
// fedropshadow feDropShadow
|
||||
// feflood feFlood
|
||||
// fefunca feFuncA
|
||||
// fefuncb feFuncB
|
||||
// fefuncg feFuncG
|
||||
// fefuncr feFuncR
|
||||
// fegaussianblur feGaussianBlur
|
||||
// feimage feImage
|
||||
// femerge feMerge
|
||||
// femergenode feMergeNode
|
||||
// femorphology feMorphology
|
||||
// feoffset feOffset
|
||||
// fepointlight fePointLight
|
||||
// fespecularlighting feSpecularLighting
|
||||
// fespotlight feSpotLight
|
||||
// fetile feTile
|
||||
// feturbulence feTurbulence
|
||||
// foreignobject foreignObject
|
||||
// glyphref glyphRef
|
||||
// lineargradient linearGradient
|
||||
// radialgradient radialGradient
|
||||
// textpath textPath
|
||||
//
|
||||
// If the adjusted current node is an element in the SVG namespace, adjust SVG
|
||||
// attributes for the token. (This fixes the case of SVG attributes that are not all
|
||||
// lowercase.)
|
||||
//
|
||||
// Adjust foreign attributes for the token. (This fixes the use of namespaced
|
||||
// attributes, in particular XLink in SVG.)
|
||||
//
|
||||
// Insert a foreign element for the token, in the same namespace as the adjusted current
|
||||
// node.
|
||||
//
|
||||
// If the token has its self-closing flag set, then run the appropriate steps from the
|
||||
// following list:
|
||||
//
|
||||
// If the token's tag name is "script", and the new current node is in the SVG
|
||||
// namespace
|
||||
//
|
||||
// Acknowledge the token's self-closing flag, and then act as
|
||||
// described in the steps for a "script" end tag below.
|
||||
//
|
||||
// Otherwise
|
||||
// Pop the current node off the stack of open elements and acknowledge the token's
|
||||
// self-closing flag.
|
||||
Token::StartTag {
|
||||
tag_name,
|
||||
raw_tag_name,
|
||||
self_closing,
|
||||
attributes,
|
||||
} => {
|
||||
let is_self_closing = *self_closing;
|
||||
let is_script = tag_name == "script";
|
||||
let adjusted_current_node = self.get_adjusted_current_node();
|
||||
let namespace = match adjusted_current_node {
|
||||
Some(node) => match &node.data {
|
||||
Data::Element(element) => element.namespace,
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
let adjust_attributes = match namespace {
|
||||
Namespace::MATHML => Some(AdjustAttributes::MathML),
|
||||
Namespace::SVG => Some(AdjustAttributes::Svg),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if namespace == Namespace::SVG {
|
||||
let new_tag_name = match &**tag_name {
|
||||
"altglyph" => Some("altGlyph"),
|
||||
"altglyphdef" => Some("altGlyphDef"),
|
||||
"altglyphitem" => Some("altGlyphItem"),
|
||||
"animatecolor" => Some("animateColor"),
|
||||
"animatemotion" => Some("animateMotion"),
|
||||
"animatetransform" => Some("animateTransform"),
|
||||
"clippath" => Some("clipPath"),
|
||||
"feblend" => Some("feBlend"),
|
||||
"fecolormatrix" => Some("feColorMatrix"),
|
||||
"fecomponenttransfer" => Some("feComponentTransfer"),
|
||||
"fecomposite" => Some("feComposite"),
|
||||
"feconvolvematrix" => Some("feConvolveMatrix"),
|
||||
"fediffuselighting" => Some("feDiffuseLighting"),
|
||||
"fedisplacementmap" => Some("feDisplacementMap"),
|
||||
"fedistantlight" => Some("feDistantLight"),
|
||||
"fedropshadow" => Some("feDropShadow"),
|
||||
"feflood" => Some("feFlood"),
|
||||
"fefunca" => Some("feFuncA"),
|
||||
"fefuncb" => Some("feFuncB"),
|
||||
"fefuncg" => Some("feFuncG"),
|
||||
"fefuncr" => Some("feFuncR"),
|
||||
"fegaussianblur" => Some("feGaussianBlur"),
|
||||
"feimage" => Some("feImage"),
|
||||
"femerge" => Some("feMerge"),
|
||||
"femergenode" => Some("feMergeNode"),
|
||||
"femorphology" => Some("feMorphology"),
|
||||
"feoffset" => Some("feOffset"),
|
||||
"fepointlight" => Some("fePointLight"),
|
||||
"fespecularlighting" => Some("feSpecularLighting"),
|
||||
"fespotlight" => Some("feSpotLight"),
|
||||
"fetile" => Some("feTile"),
|
||||
"feturbulence" => Some("feTurbulence"),
|
||||
"foreignobject" => Some("foreignObject"),
|
||||
"glyphref" => Some("glyphRef"),
|
||||
"lineargradient" => Some("linearGradient"),
|
||||
"radialgradient" => Some("radialGradient"),
|
||||
"textpath" => Some("textPath"),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(new_tag_name) = new_tag_name {
|
||||
token_and_info.token = Token::StartTag {
|
||||
tag_name: new_tag_name.into(),
|
||||
raw_tag_name: raw_tag_name.clone(),
|
||||
self_closing: *self_closing,
|
||||
attributes: attributes.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.insert_foreign_element(token_and_info, namespace, adjust_attributes)?;
|
||||
|
||||
if is_self_closing {
|
||||
if is_script
|
||||
&& match self.open_elements_stack.items.last() {
|
||||
Some(node) => match &node.data {
|
||||
Data::Element(element) if element.namespace == Namespace::SVG => {
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
token_and_info.acknowledged = true;
|
||||
|
||||
self.open_elements_stack.pop();
|
||||
} else {
|
||||
self.open_elements_stack.pop();
|
||||
|
||||
token_and_info.acknowledged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// An end tag whose tag name is "script", if the current node is an SVG script element
|
||||
//
|
||||
// Pop the current node off the stack of open elements.
|
||||
//
|
||||
// Let the old insertion point have the same value as the current insertion point. Let
|
||||
// the insertion point be just before the next input character.
|
||||
//
|
||||
// Increment the parser's script nesting level by one. Set the parser pause flag to
|
||||
// true.
|
||||
//
|
||||
// If the active speculative HTML parser is null and the user agent supports SVG, then
|
||||
// Process the SVG script element according to the SVG rules. [SVG]
|
||||
//
|
||||
// Even if this causes new characters to be inserted into the tokenizer, the parser will
|
||||
// not be executed reentrantly, since the parser pause flag is true.
|
||||
//
|
||||
// Decrement the parser's script nesting level by one. If the parser's script nesting
|
||||
// level is zero, then set the parser pause flag to false.
|
||||
//
|
||||
// Let the insertion point have the value of the old insertion point. (In other words,
|
||||
// restore the insertion point to its previous value. This value might be the
|
||||
// "undefined" value.)
|
||||
Token::EndTag { tag_name, .. } if tag_name == "script" => {
|
||||
self.open_elements_stack.pop();
|
||||
|
||||
// No need to handle other steps
|
||||
}
|
||||
// Any other end tag
|
||||
//
|
||||
// Run these steps:
|
||||
//
|
||||
// Initialize node to be the current node (the bottommost node of the stack).
|
||||
//
|
||||
// If node's tag name, converted to ASCII lowercase, is not the same as the tag name of
|
||||
// the token, then this is a parse error.
|
||||
//
|
||||
// Loop: If node is the topmost element in the stack of open elements, then return.
|
||||
// (fragment case)
|
||||
//
|
||||
// If node's tag name, converted to ASCII lowercase, is the same as the tag name of the
|
||||
// token, pop elements from the stack of open elements until node has been popped from
|
||||
// the stack, and then return.
|
||||
//
|
||||
// Set node to the previous entry in the stack of open elements.
|
||||
//
|
||||
// If node is not an element in the HTML namespace, return to the step labeled loop.
|
||||
//
|
||||
// Otherwise, process the token according to the rules given in the section
|
||||
// corresponding to the current insertion mode in HTML content.
|
||||
Token::EndTag { tag_name, .. } => {
|
||||
let mut node = self.open_elements_stack.items.last();
|
||||
|
||||
if let Some(node) = node {
|
||||
match &node.data {
|
||||
Data::Element(element)
|
||||
if &*element.tag_name.to_ascii_lowercase() != tag_name =>
|
||||
{
|
||||
self.errors
|
||||
.push(Error::new(token_and_info.span, ErrorKind::UnexpectedToken));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut stack_idx = self.open_elements_stack.items.len() - 1;
|
||||
|
||||
loop {
|
||||
if stack_idx == 0 || node.is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
let inner_node = node.unwrap();
|
||||
let first = self.open_elements_stack.items.first();
|
||||
|
||||
if let Some(first) = first {
|
||||
if is_same_node(inner_node, first) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
match &inner_node.data {
|
||||
Data::Element(element)
|
||||
if &*element.tag_name.to_ascii_lowercase() == tag_name =>
|
||||
{
|
||||
let clone = inner_node.clone();
|
||||
|
||||
self.open_elements_stack.pop_until_node(&clone);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
stack_idx -= 1;
|
||||
node = self.open_elements_stack.items.get(stack_idx);
|
||||
|
||||
if let Some(node) = node {
|
||||
if matches!(&node.data, Data::Element(element) if element.namespace == Namespace::HTML)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.process_token(token_and_info, None)?;
|
||||
}
|
||||
// EOF token is not reachable here
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -3113,11 +3574,13 @@ where
|
||||
self.reconstruct_active_formatting_elements()?;
|
||||
self.insert_foreign_element(
|
||||
token_and_info,
|
||||
Namespace::SVG,
|
||||
Namespace::MATHML,
|
||||
Some(AdjustAttributes::MathML),
|
||||
)?;
|
||||
|
||||
if is_self_closing {
|
||||
self.open_elements_stack.pop();
|
||||
|
||||
token_and_info.acknowledged = true;
|
||||
}
|
||||
}
|
||||
@ -3150,6 +3613,8 @@ where
|
||||
)?;
|
||||
|
||||
if is_self_closing {
|
||||
self.open_elements_stack.pop();
|
||||
|
||||
token_and_info.acknowledged = true;
|
||||
}
|
||||
}
|
||||
@ -6733,6 +7198,117 @@ fn is_same_node(a: &RcNode, b: &RcNode) -> bool {
|
||||
Rc::ptr_eq(a, b)
|
||||
}
|
||||
|
||||
// The HTML namespace is "http://www.w3.org/1999/xhtml".
|
||||
fn is_element_in_html_namespace(node: Option<&RcNode>) -> bool {
|
||||
if let Some(node) = node {
|
||||
match &node.data {
|
||||
Data::Element(element) if element.namespace == Namespace::HTML => {
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// A node is a MathML text integration point if it is one of the following
|
||||
// elements:
|
||||
//
|
||||
// A MathML mi element
|
||||
// A MathML mo element
|
||||
// A MathML mn element
|
||||
// A MathML ms element
|
||||
// A MathML mtext element
|
||||
fn is_mathml_text_integration_point(node: Option<&RcNode>) -> bool {
|
||||
if let Some(node) = node {
|
||||
match &node.data {
|
||||
Data::Element(element)
|
||||
if element.namespace == Namespace::MATHML
|
||||
&& matches!(&*element.tag_name, "mi" | "mo" | "mn" | "ms" | "mtext") =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_mathml_annotation_xml(node: Option<&RcNode>) -> bool {
|
||||
if let Some(node) = node {
|
||||
match &node.data {
|
||||
Data::Element(element)
|
||||
if element.namespace == Namespace::MATHML
|
||||
&& &*element.tag_name == "annotation-xml" =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// A node is an HTML integration point if it is one of the following elements:
|
||||
//
|
||||
// A MathML annotation-xml element whose start tag token had an attribute with
|
||||
// the name "encoding" whose value was an ASCII case-insensitive match for the
|
||||
// string "text/html" A MathML annotation-xml element whose start tag token
|
||||
// had an attribute with the name "encoding" whose value was an ASCII
|
||||
// case-insensitive match for the string "application/xhtml+xml"
|
||||
// An SVG foreignObject element
|
||||
// An SVG desc element
|
||||
// An SVG title element
|
||||
fn is_html_integration_point(node: Option<&RcNode>) -> bool {
|
||||
if let Some(node) = node {
|
||||
match &node.data {
|
||||
Data::Element(Element {
|
||||
namespace,
|
||||
tag_name,
|
||||
attributes,
|
||||
..
|
||||
}) if *namespace == Namespace::MATHML && tag_name == "annotation-xml" => {
|
||||
for attribute in attributes {
|
||||
if &*attribute.name == "encoding"
|
||||
&& (attribute.value.is_some()
|
||||
&& matches!(
|
||||
&*attribute.value.as_ref().unwrap().to_ascii_lowercase(),
|
||||
"text/html" | "application/xhtml+xml"
|
||||
))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Data::Element(Element {
|
||||
namespace,
|
||||
tag_name,
|
||||
..
|
||||
}) if *namespace == Namespace::SVG
|
||||
&& matches!(&**tag_name, "foreignObject" | "desc" | "title") =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
impl<I> Parse<Document> for Parser<I>
|
||||
where
|
||||
I: ParserInput,
|
||||
|
@ -1,6 +1,8 @@
|
||||
use swc_html_ast::*;
|
||||
|
||||
use crate::parser::{is_same_node, RcNode};
|
||||
use crate::parser::{
|
||||
is_html_integration_point, is_mathml_text_integration_point, is_same_node, Data, RcNode,
|
||||
};
|
||||
|
||||
static IMPLICIT_END_TAG_REQUIRED: &[&str] = &[
|
||||
"dd", "dt", "li", "optgroup", "option", "p", "rb", "rp", "rt", "rtc",
|
||||
@ -371,10 +373,37 @@ impl OpenElementsStack {
|
||||
}
|
||||
|
||||
pub fn pop_until_tag_name_popped(&mut self, tag_name: &[&str]) {
|
||||
while let Some(node) = self.items.pop() {
|
||||
while let Some(node) = self.pop() {
|
||||
if tag_name.contains(&get_tag_name!(node)) && get_namespace!(node) == Namespace::HTML {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_until_node(&mut self, until_to_node: &RcNode) {
|
||||
while let Some(node) = &self.pop() {
|
||||
if is_same_node(node, until_to_node) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// While the current node is not a MathML text integration point, an HTML
|
||||
// integration point, or an element in the HTML namespace, pop elements from
|
||||
// the stack of open elements.
|
||||
pub fn pop_until_in_foreign(&mut self) {
|
||||
while let Some(node) = self.pop() {
|
||||
match &node.data {
|
||||
Data::Element(Element { namespace, .. }) if *namespace == Namespace::HTML => {
|
||||
break;
|
||||
}
|
||||
_ if is_mathml_text_integration_point(Some(&node))
|
||||
|| is_html_integration_point(Some(&node)) =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,152 @@
|
||||
</mrow>
|
||||
</math>
|
||||
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<matrix>
|
||||
<matrixrow>
|
||||
<cn> 0 </cn> <cn> 1 </cn> <cn> 0 </cn>
|
||||
</matrixrow>
|
||||
<matrixrow>
|
||||
<cn> 0 </cn> <cn> 0 </cn> <cn> 1 </cn>
|
||||
</matrixrow>
|
||||
<matrixrow>
|
||||
<cn> 1 </cn> <cn> 0 </cn> <cn> 0 </cn>
|
||||
</matrixrow>
|
||||
</matrix>
|
||||
</math>
|
||||
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<infinity/>
|
||||
</math>
|
||||
|
||||
<math>
|
||||
<semantics>
|
||||
|
||||
<!-- Presentation MathML -->
|
||||
<mrow>
|
||||
<msup>
|
||||
<mi>x</mi>
|
||||
<mn>2</mn>
|
||||
</msup>
|
||||
<mo>+</mo>
|
||||
<mi>y</mi>
|
||||
</mrow>
|
||||
|
||||
<!-- Content MathML -->
|
||||
<annotation-xml encoding="MathML-Content">
|
||||
<apply>
|
||||
<plus/>
|
||||
<apply>
|
||||
<power/>
|
||||
<ci>x</ci>
|
||||
<cn type="integer">2</cn>
|
||||
</apply>
|
||||
<ci>y</ci>
|
||||
</apply>
|
||||
</annotation-xml>
|
||||
|
||||
<!-- Content HTML -->
|
||||
<annotation-xml encoding="application/xhtml+xml">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head><title>INNER HTML TITLE</title></head>
|
||||
<body>
|
||||
<p>The base of the natural logarithms, approximately 2.71828.</p>
|
||||
</body>
|
||||
</html>
|
||||
</annotation-xml>
|
||||
|
||||
<!-- annotate an image -->
|
||||
<annotation encoding="image/png" src="some/path/formula.png"/>
|
||||
|
||||
<!-- annotate TeX -->
|
||||
<annotation encoding="application/x-tex">
|
||||
x^{2} + y
|
||||
</annotation>
|
||||
|
||||
</semantics>
|
||||
</math>
|
||||
|
||||
<math>
|
||||
<mtable>
|
||||
<mtr groupalign="{left}">
|
||||
<mtd>
|
||||
<mrow>
|
||||
<mn>2</mn>
|
||||
<mo>⁢</mo>
|
||||
<maligngroup/><mi>x</mi>
|
||||
<maligngroup/><mo>+</mo>
|
||||
<maligngroup/><mi>y</mi>
|
||||
<maligngroup/><mo>=</mo>
|
||||
<mo>-</mo>
|
||||
<maligngroup/><mn>5</mn>
|
||||
</mrow>
|
||||
</mtd>
|
||||
</mtr>
|
||||
<mtr>
|
||||
<mtd>
|
||||
<mrow>
|
||||
<maligngroup/><mi>x</mi>
|
||||
<maligngroup/><mo>-</mo>
|
||||
<mn>2</mn>
|
||||
<mo>⁢</mo>
|
||||
<maligngroup/><mi>y</mi>
|
||||
<maligngroup/><mo>=</mo>
|
||||
<maligngroup/><mn>1</mn>
|
||||
</mrow>
|
||||
</mtd>
|
||||
</mtr>
|
||||
</mtable>
|
||||
</math>
|
||||
|
||||
<math>
|
||||
|
||||
<mtext> Theorem of Pythagoras </mtext>
|
||||
|
||||
<mtext> /* comment here */ </mtext>
|
||||
|
||||
</math>
|
||||
|
||||
<math>
|
||||
<mtext>
|
||||
<div>test</div>
|
||||
</mtext>
|
||||
</math>
|
||||
|
||||
<math>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mi> x </mi>
|
||||
<malignmark edge="right"/>
|
||||
</mrow>
|
||||
<mn> 2 </mn>
|
||||
</msup>
|
||||
</math>
|
||||
|
||||
<math>
|
||||
<annotation-xml>
|
||||
<svg style="font-size: 20px" width="400px" height="220px" viewBox="0 0 200 110">
|
||||
<g transform="translate(10,80)">
|
||||
<path d="M 0 0 L 150 0 A 75 75 0 0 0 0 0
|
||||
M 30 0 L 30 -60 M 30 -10 L 40 -10 L 40 0"
|
||||
fill="none" stroke="black"></path>
|
||||
<text transform="translate(10,20)">1</text>
|
||||
<switch transform="translate(35,-40)">
|
||||
<foreignObject width="200" height="50" requiredExtensions="http://www.w3.org/1998/Math/MathML">
|
||||
<math>
|
||||
<msqrt>
|
||||
<mn>2</mn>
|
||||
<mi>r</mi>
|
||||
<mo>−</mo>
|
||||
<mn>1</mn>
|
||||
</msqrt>
|
||||
</math>
|
||||
</foreignObject>
|
||||
<text>\sqrt{2r - 1}</text>
|
||||
</switch>
|
||||
</g>
|
||||
</svg>
|
||||
</annotation-xml>
|
||||
</math>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -72,5 +72,56 @@
|
||||
</a>
|
||||
</svg>
|
||||
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
div {
|
||||
color: white;
|
||||
font: 18px serif;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<polygon points="5,5 195,10 185,185 10,195" />
|
||||
|
||||
<!-- Common use case: embed HTML text into SVG -->
|
||||
<foreignObject x="20" y="20" width="160" height="160">
|
||||
<!--
|
||||
In the context of SVG embedded in an HTML document, the XHTML
|
||||
namespace could be omitted, but it is mandatory in the
|
||||
context of an SVG document
|
||||
-->
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Sed mollis mollis mi ut ultricies. Nullam magna ipsum,
|
||||
porta vel dui convallis, rutrum imperdiet eros. Aliquam
|
||||
erat volutpat.
|
||||
</div>
|
||||
</foreignObject>
|
||||
</svg>
|
||||
|
||||
<svg style="font-size: 20px" width="400px" height="220px" viewBox="0 0 200 110">
|
||||
<g transform="translate(10,80)">
|
||||
<path d="M 0 0 L 150 0 A 75 75 0 0 0 0 0
|
||||
M 30 0 L 30 -60 M 30 -10 L 40 -10 L 40 0"
|
||||
fill="none" stroke="black"></path>
|
||||
<text transform="translate(10,20)">1</text>
|
||||
<switch transform="translate(35,-40)">
|
||||
<foreignObject width="200" height="50"
|
||||
requiredExtensions="http://www.w3.org/1998/Math/MathML">
|
||||
<math>
|
||||
<msqrt>
|
||||
<mn>2</mn>
|
||||
<mi>r</mi>
|
||||
<mo>−</mo>
|
||||
<mn>1</mn>
|
||||
</msqrt>
|
||||
</math>
|
||||
</foreignObject>
|
||||
<text>\sqrt{2r - 1}</text>
|
||||
</switch>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user