Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/parser/lexer/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl<S: LexemeSink> StateMachineActions for Lexer<S> {
self.set_last_text_type(TextType::Data);

if let Some(feedback) = feedback {
self.handle_tree_builder_feedback(context, feedback, &lexeme);
self.handle_tree_builder_feedback(context, feedback, &lexeme)?;
}

if let StartTag {
Expand Down
8 changes: 6 additions & 2 deletions src/parser/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,21 @@ impl<S: LexemeSink> Lexer<S> {
context: &mut ParserContext<S>,
feedback: TreeBuilderFeedback,
lexeme: &TagLexeme<'_>,
) {
) -> Result<(), ParsingAmbiguityError> {
match feedback {
TreeBuilderFeedback::SwitchTextType(text_type) => self.set_last_text_type(text_type),
TreeBuilderFeedback::SetAllowCdata(cdata_allowed) => self.cdata_allowed = cdata_allowed,
TreeBuilderFeedback::RequestLexeme(mut callback) => {
let feedback = callback(&mut context.tree_builder_simulator, lexeme);

self.handle_tree_builder_feedback(context, feedback, lexeme);
self.handle_tree_builder_feedback(context, feedback, lexeme)?;
}
TreeBuilderFeedback::DepthExceeded => {
return Err(ParsingAmbiguityError::depth_exceeded());
}
TreeBuilderFeedback::None => (),
}
Ok(())
}

#[inline]
Expand Down
3 changes: 3 additions & 0 deletions src/parser/tag_scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ impl<S: TagHintSink> TagScanner<S> {
}
TreeBuilderFeedback::RequestLexeme(_) => Some(feedback),
TreeBuilderFeedback::None => None,
TreeBuilderFeedback::DepthExceeded => {
return Err(ParsingAmbiguityError::depth_exceeded());
}
})
}

Expand Down
69 changes: 53 additions & 16 deletions src/parser/tree_builder_simulator/ambiguity_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,64 @@ use thiserror::Error;
/// correct parsing context.
///
/// [`strict`]: ../struct.Settings.html#structfield.strict
#[derive(Error, Debug, Eq, PartialEq)]
#[derive(Error, Eq, PartialEq)]
pub struct ParsingAmbiguityError {
on_tag_name: Box<str>,
cause: Cause,
}

impl Display for ParsingAmbiguityError {
impl ParsingAmbiguityError {
pub(crate) fn depth_exceeded() -> Self {
Self {
cause: Cause::DepthExceeded,
}
}
}

#[derive(Eq, PartialEq)]
pub(crate) enum Cause {
AmbiguousTextTypeSwitch(Box<str>),
DepthExceeded,
}

impl Display for Cause {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
concat!(
"The parser has encountered a text content tag (`<{}>`) in the context where it is ",
"ambiguous whether this tag should be ignored or not. And, thus, it is unclear whether ",
"consequent content should be parsed as raw text or HTML markup.",
"\n\n",
"This error occurs due to the limited capabilities of the streaming parsing. However, ",
"almost all of the cases of this error are caused by a non-conforming markup (e.g. a ",
"`<script>` element in `<select>` element)."
match self {
Self::AmbiguousTextTypeSwitch(on_tag_name) => write!(
f,
concat!(
"The parser has encountered a text content tag (`<{}>`) in the context where it is ",
"ambiguous whether this tag should be ignored or not. And, thus, it is unclear whether ",
"consequent content should be parsed as raw text or HTML markup.",
"\n\n",
"This error occurs due to the limited capabilities of the streaming parsing. However, ",
"almost all of the cases of this error are caused by a non-conforming markup (e.g. a ",
"`<script>` element in `<select>` element)."
),
on_tag_name
),
self.on_tag_name
)
Self::DepthExceeded => {
f.write_str("Too much HTML/XML nesting while simulating the tree builder.")
}
}
}
}

impl Display for ParsingAmbiguityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.cause, f)
}
}

impl fmt::Debug for ParsingAmbiguityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.cause, f)
}
}

impl fmt::Debug for Cause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}

Expand All @@ -112,7 +149,7 @@ macro_rules! create_assert_for_tags {
) -> Result<(), ParsingAmbiguityError> {
if tag_is_one_of!(tag_name, [ $($tag),+ ]) {
Err(ParsingAmbiguityError {
on_tag_name: tag_hash_to_string(tag_name)
cause: Cause::AmbiguousTextTypeSwitch(tag_hash_to_string(tag_name))
})
} else {
Ok(())
Expand Down
10 changes: 8 additions & 2 deletions src/parser/tree_builder_simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ use TagTokenOutline::{EndTag, StartTag};

pub use self::ambiguity_guard::ParsingAmbiguityError;

const DEFAULT_NS_STACK_CAPACITY: usize = 256;
/// <svg><html><svg><html>... nesting depth
const MAX_NS_STACK_DEPTH: usize = 256;

#[must_use]
pub(crate) enum TreeBuilderFeedback {
SwitchTextType(TextType),
SetAllowCdata(bool),
#[allow(clippy::type_complexity)]
RequestLexeme(Box<dyn FnMut(&mut TreeBuilderSimulator, &TagLexeme<'_>) -> Self + Send>),
DepthExceeded,
None,
}

Expand Down Expand Up @@ -112,7 +114,7 @@ impl TreeBuilderSimulator {
#[must_use]
pub fn new(strict: bool) -> Self {
let mut simulator = Self {
ns_stack: Vec::with_capacity(DEFAULT_NS_STACK_CAPACITY),
ns_stack: Vec::with_capacity(MAX_NS_STACK_DEPTH),
current_ns: Namespace::Html,
ambiguity_guard: AmbiguityGuard::default(),
strict,
Expand Down Expand Up @@ -179,6 +181,10 @@ impl TreeBuilderSimulator {

#[inline]
fn enter_ns(&mut self, ns: Namespace) -> TreeBuilderFeedback {
if self.ns_stack.len() >= self.ns_stack.capacity() {
return TreeBuilderFeedback::DepthExceeded;
}

self.ns_stack.push(ns);
self.current_ns = ns;
TreeBuilderFeedback::SetAllowCdata(ns != Namespace::Html)
Expand Down
23 changes: 23 additions & 0 deletions src/rewriter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,29 @@ mod tests {
}
}

#[test]
fn extra_svg_nesting() {
let nested = "<svg><foreign-content>".repeat(32);
let res = rewrite_str(
&nested,
RewriteStrSettings {
element_content_handlers: vec![element!("svg", |_| Ok(()))],
..RewriteStrSettings::new()
},
);
assert_eq!(nested, res.unwrap());

let extra_nested = nested.repeat(32);
let res = rewrite_str(
&extra_nested,
RewriteStrSettings {
element_content_handlers: vec![element!("svg", |_| Ok(()))],
..RewriteStrSettings::new()
},
);
assert!(matches!(res, Err(RewritingError::ParsingAmbiguity(_))));
}

#[test]
fn handler_invocation_order() {
let handlers_executed = Arc::new(Mutex::new(Vec::default()));
Expand Down
Loading