From 946193f9e3904914db7c44553e20bce97de0cdb7 Mon Sep 17 00:00:00 2001 From: EdJoPaTo Date: Mon, 2 Nov 2026 18:02:59 +0100 Subject: [PATCH] Use as much defaults for parsing/serializing as possible This consistently uses scripting_enabled: true from the html5ever default and thereby ensures that `scripting_enabled` is the same for parsing and serializing. If its not (like before this commit) it escapes the content which results in broken noscript contents. --- scraper/src/element_ref/mod.rs | 3 +-- scraper/src/html/mod.rs | 41 +++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/scraper/src/element_ref/mod.rs b/scraper/src/element_ref/mod.rs index b307d073..b00dd8f2 100644 --- a/scraper/src/element_ref/mod.rs +++ b/scraper/src/element_ref/mod.rs @@ -55,9 +55,8 @@ impl<'a> ElementRef<'a> { fn serialize(&self, traversal_scope: TraversalScope) -> String { let opts = SerializeOpts { - scripting_enabled: false, // It's not clear what this does. traversal_scope, - create_missing_parent: false, + ..Default::default() }; let mut buf = Vec::new(); serialize(&mut buf, self, opts).unwrap(); diff --git a/scraper/src/html/mod.rs b/scraper/src/html/mod.rs index 31873ee2..0722c693 100644 --- a/scraper/src/html/mod.rs +++ b/scraper/src/html/mod.rs @@ -10,7 +10,7 @@ use ego_tree::iter::Nodes; use html5ever::serialize::SerializeOpts; use html5ever::tendril::TendrilSink; use html5ever::tree_builder::QuirksMode; -use html5ever::{QualName, driver, serialize}; +use html5ever::{ParseOpts, QualName, driver, serialize}; use selectors::matching::SelectorCaches; use crate::selector::Selector; @@ -83,12 +83,14 @@ impl Html { /// Parses a string of HTML as a fragment. pub fn parse_fragment(fragment: &str) -> Self { + let opts = ParseOpts::default(); + let context_element_allows_scripting = opts.tree_builder.scripting_enabled; let parser = driver::parse_fragment( HtmlTreeSink::new(Self::new_fragment()), - Default::default(), + opts, QualName::new(None, ns!(html), local_name!("body")), Vec::new(), - false, + context_element_allows_scripting, ); parser.one(fragment) } @@ -116,9 +118,8 @@ impl Html { /// Serialize entire document into HTML. pub fn html(&self) -> String { let opts = SerializeOpts { - scripting_enabled: false, // It's not clear what this does. traversal_scope: serialize::TraversalScope::IncludeNode, - create_missing_parent: false, + ..Default::default() }; let mut buf = Vec::new(); serialize(&mut buf, self, opts).unwrap(); @@ -258,6 +259,36 @@ mod tests { assert_eq!(upper, Some(10)); } + #[test] + fn noscript_is_just_text() { + let html = Html::parse_document( + "", + ); + + let selector = Selector::parse("h1").unwrap(); + let result = html.select(&selector).collect::>(); + assert_eq!(result, []); + + let selector = Selector::parse("noscript").unwrap(); + let mut result = html.select(&selector).collect::>(); + assert_eq!(result.len(), 1); + let element = result.pop().unwrap(); + let texts = element.text().collect::>(); + assert_eq!(texts, ["

Hello

world

"]); + } + + #[test] + fn noscript_parse_roundtrip_stays_the_same() { + let input = + ""; + let mut output = input.to_owned(); + // Parsing and serializing this many times should stay the same and not escape the already escaped escape sequences + for _ in 0..3 { + output = Html::parse_document(&output).html(); + } + assert_eq!(output, input); + } + #[cfg(feature = "atomic")] #[test] fn html_is_send() {