Sfoglia il codice sorgente

fix(codegen): serialize template tags, closes #4410 (#5247)

Lucas Fernandes Nogueira 2 anni fa
parent
commit
aec5537de0

+ 6 - 0
.changes/fix-html-template.md

@@ -0,0 +1,6 @@
+---
+"tauri-utils": patch
+"tauri-codegen": patch
+---
+
+Properly serialize HTML template tags.

+ 7 - 5
core/tauri-codegen/src/context.rs

@@ -11,7 +11,9 @@ use sha2::{Digest, Sha256};
 
 use tauri_utils::assets::AssetKey;
 use tauri_utils::config::{AppUrl, Config, PatternKind, WindowUrl};
-use tauri_utils::html::{inject_nonce_token, parse as parse_html};
+use tauri_utils::html::{
+  inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node,
+};
 
 #[cfg(feature = "shell-scope")]
 use tauri_utils::config::{ShellAllowedArg, ShellAllowedArgs, ShellAllowlistScope};
@@ -37,10 +39,10 @@ fn map_core_assets(
     options.dangerous_disable_asset_csp_modification.clone();
   move |key, path, input, csp_hashes| {
     if path.extension() == Some(OsStr::new("html")) {
-      let mut document = parse_html(String::from_utf8_lossy(input).into_owned());
-
       #[allow(clippy::collapsible_if)]
       if csp {
+        let mut document = parse_html(String::from_utf8_lossy(input).into_owned());
+
         if target == Target::Linux {
           ::tauri_utils::html::inject_csp_token(&mut document);
         }
@@ -77,9 +79,9 @@ fn map_core_assets(
               .push(format!("'sha256-{}'", base64::encode(&hash)));
           }
         }
-      }
 
-      *input = document.to_string().as_bytes().to_vec();
+        *input = serialize_html_node(&document);
+      }
     }
     Ok(())
   }

+ 92 - 2
core/tauri-utils/src/html.rs

@@ -6,9 +6,15 @@
 
 use std::path::{Path, PathBuf};
 
-use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName};
+use html5ever::{
+  interface::QualName,
+  namespace_url, ns,
+  serialize::{HtmlSerializer, SerializeOpts, Serializer, TraversalScope},
+  tendril::TendrilSink,
+  LocalName,
+};
 pub use kuchiki::NodeRef;
-use kuchiki::{Attribute, ExpandedName};
+use kuchiki::{Attribute, ExpandedName, NodeData};
 use serde::Serialize;
 #[cfg(feature = "isolation")]
 use serialize_to_javascript::DefaultTemplate;
@@ -24,6 +30,90 @@ pub const SCRIPT_NONCE_TOKEN: &str = "__TAURI_SCRIPT_NONCE__";
 /// The token used for style nonces.
 pub const STYLE_NONCE_TOKEN: &str = "__TAURI_STYLE_NONCE__";
 
+// taken from https://github.com/kuchiki-rs/kuchiki/blob/57ee6920d835315a498e748ba4b07a851ae5e498/src/serializer.rs#L12
+fn serialize_node_ref_internal<S: Serializer>(
+  node: &NodeRef,
+  serializer: &mut S,
+  traversal_scope: TraversalScope,
+) -> crate::Result<()> {
+  match (traversal_scope, node.data()) {
+    (ref scope, &NodeData::Element(ref element)) => {
+      if *scope == TraversalScope::IncludeNode {
+        let attrs = element.attributes.borrow();
+
+        // Unfortunately we need to allocate something to hold these &'a QualName
+        let attrs = attrs
+          .map
+          .iter()
+          .map(|(name, attr)| {
+            (
+              QualName::new(attr.prefix.clone(), name.ns.clone(), name.local.clone()),
+              &attr.value,
+            )
+          })
+          .collect::<Vec<_>>();
+
+        serializer.start_elem(
+          element.name.clone(),
+          attrs.iter().map(|&(ref name, value)| (name, &**value)),
+        )?
+      }
+
+      let children = match element.template_contents.as_ref() {
+        Some(template_root) => template_root.children(),
+        None => node.children(),
+      };
+      for child in children {
+        serialize_node_ref_internal(&child, serializer, TraversalScope::IncludeNode)?
+      }
+
+      if *scope == TraversalScope::IncludeNode {
+        serializer.end_elem(element.name.clone())?
+      }
+      Ok(())
+    }
+
+    (_, &NodeData::DocumentFragment) | (_, &NodeData::Document(_)) => {
+      for child in node.children() {
+        serialize_node_ref_internal(&child, serializer, TraversalScope::IncludeNode)?
+      }
+      Ok(())
+    }
+
+    (TraversalScope::ChildrenOnly(_), _) => Ok(()),
+
+    (TraversalScope::IncludeNode, &NodeData::Doctype(ref doctype)) => {
+      serializer.write_doctype(&doctype.name).map_err(Into::into)
+    }
+    (TraversalScope::IncludeNode, &NodeData::Text(ref text)) => {
+      serializer.write_text(&text.borrow()).map_err(Into::into)
+    }
+    (TraversalScope::IncludeNode, &NodeData::Comment(ref text)) => {
+      serializer.write_comment(&text.borrow()).map_err(Into::into)
+    }
+    (TraversalScope::IncludeNode, &NodeData::ProcessingInstruction(ref contents)) => {
+      let contents = contents.borrow();
+      serializer
+        .write_processing_instruction(&contents.0, &contents.1)
+        .map_err(Into::into)
+    }
+  }
+}
+
+/// Serializes the node to HTML.
+pub fn serialize_node(node: &NodeRef) -> Vec<u8> {
+  let mut u8_vec = Vec::new();
+  let mut ser = HtmlSerializer::new(
+    &mut u8_vec,
+    SerializeOpts {
+      traversal_scope: TraversalScope::IncludeNode,
+      ..Default::default()
+    },
+  );
+  serialize_node_ref_internal(node, &mut ser, TraversalScope::IncludeNode).unwrap();
+  u8_vec
+}
+
 /// Parses the given HTML string.
 pub fn parse(html: String) -> NodeRef {
   kuchiki::parse_html().one(html)