From d7ddda3f2182a689fdc68e9ad5002d9276de35a7 Mon Sep 17 00:00:00 2001
From: Wilson Lin
Date: Thu, 30 Jul 2020 20:17:55 +1000
Subject: [PATCH] Do not allow unmatched closing tag at root
---
src/err.rs | 6 +++++-
src/lib.rs | 5 +++++
src/proc/mod.rs | 6 ++----
src/tests/mod.rs | 29 +++++++++++++++++++++++++++++
src/unit/content.rs | 2 +-
5 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/src/err.rs b/src/err.rs
index 0976a81..ca300d9 100644
--- a/src/err.rs
+++ b/src/err.rs
@@ -1,9 +1,10 @@
// Implement debug to allow .unwrap().
-#[derive(Debug)]
+#[derive(Debug, Eq, PartialEq)]
pub enum ErrorType {
ClosingTagMismatch { expected: String, got: String },
NotFound(&'static str),
UnexpectedEnd,
+ UnexpectedClosingTag,
}
impl ErrorType {
@@ -18,6 +19,9 @@ impl ErrorType {
ErrorType::UnexpectedEnd => {
format!("Unexpected end of source code.")
}
+ ErrorType::UnexpectedClosingTag => {
+ format!("Unexpected closing tag.")
+ }
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 9241cd2..74b9ad7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,11 @@ mod unit;
pub fn in_place(code: &mut [u8], cfg: &Cfg) -> Result {
let mut proc = Processor::new(code);
process_content(&mut proc, cfg, Namespace::Html, None)
+ .and_then(|_| if !proc.at_end() {
+ Err(ErrorType::UnexpectedClosingTag)
+ } else {
+ Ok(())
+ })
.map_err(|error_type| Error {
error_type,
position: proc.read_len(),
diff --git a/src/proc/mod.rs b/src/proc/mod.rs
index 8bba8c5..36b0321 100644
--- a/src/proc/mod.rs
+++ b/src/proc/mod.rs
@@ -362,8 +362,7 @@ impl<'d> Processor<'d> {
#[cfg(not(feature = "js-esbuild"))]
#[inline(always)]
pub fn finish(self) -> Result {
- // NOTE: Do not assert that we are at the end, as invalid HTML can end prematurely e.g.
- // `hellooutside`.
+ debug_assert!(self.at_end());
Ok(self.write_next)
}
@@ -371,8 +370,7 @@ impl<'d> Processor<'d> {
#[cfg(feature = "js-esbuild")]
#[inline(always)]
pub fn finish(self) -> Result {
- // NOTE: Do not assert that we are at the end, as invalid HTML can end prematurely e.g.
- // `hellooutside`.
+ debug_assert!(self.at_end());
self.script_wg.wait();
let mut results = Arc::try_unwrap(self.script_results)
.unwrap_or_else(|_| panic!("failed to acquire script results"))
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 708e88a..c9efd95 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,3 +1,8 @@
+#[cfg(test)]
+use {
+ crate::ErrorType
+};
+
#[cfg(test)]
fn _eval(src: &'static [u8], expected: &'static [u8], cfg: &super::Cfg) -> () {
let mut code = src.to_vec();
@@ -13,6 +18,12 @@ fn _eval(src: &'static [u8], expected: &'static [u8], cfg: &super::Cfg) -> () {
};
}
+#[cfg(test)]
+fn _eval_error(src: &'static [u8], expected: ErrorType, cfg: &super::Cfg) -> () {
+ let mut code = src.to_vec();
+ assert_eq!(super::in_place(&mut code, cfg).unwrap_err().error_type, expected);
+}
+
#[cfg(test)]
fn eval(src: &'static [u8], expected: &'static [u8]) -> () {
_eval(src, expected, &super::Cfg {
@@ -20,6 +31,13 @@ fn eval(src: &'static [u8], expected: &'static [u8]) -> () {
});
}
+#[cfg(test)]
+fn eval_error(src: &'static [u8], expected: ErrorType) -> () {
+ _eval_error(src, expected, &super::Cfg {
+ minify_js: false,
+ });
+}
+
#[cfg(test)]
#[cfg(feature = "js-esbuild")]
fn eval_with_js_min(src: &'static [u8], expected: &'static [u8]) -> () {
@@ -79,10 +97,21 @@ fn test_parsing_with_omitted_tags() {
eval(b"
Goodbye", ErrorType::UnexpectedClosingTag);
+ eval_error(b"Hello
Goodbye", ErrorType::UnexpectedClosingTag);
+ eval_error(b"HelloGoodbye", ErrorType::ClosingTagMismatch { expected: "div".to_string(), got: "p".to_string() });
+ eval_error(b"
- a", ErrorType::ClosingTagMismatch { expected: "ul".to_string(), got: "p".to_string() });
+ eval_error(b"
- a", ErrorType::ClosingTagMismatch { expected: "ul".to_string(), got: "p".to_string() });
+ eval_error(b"
- a", ErrorType::ClosingTagMismatch { expected: "ul".to_string(), got: "p".to_string() });
+}
+
#[test]
fn test_removal_of_optional_tags() {
eval(b"", b"");
diff --git a/src/unit/content.rs b/src/unit/content.rs
index 4516f94..536ab68 100644
--- a/src/unit/content.rs
+++ b/src/unit/content.rs
@@ -47,7 +47,7 @@ impl ContentType {
}
pub struct ProcessedContent {
- pub(crate) closing_tag_omitted: bool,
+ pub closing_tag_omitted: bool,
}
pub fn process_content(proc: &mut Processor, cfg: &Cfg, ns: Namespace, parent: Option) -> ProcessingResult {