1use std::env;
6use std::fs::{File, create_dir_all};
7use std::io::{Error, Read, Seek, Write};
8use std::path::{Path, PathBuf};
9use std::process::Command;
10use std::rc::Rc;
11
12use script_bindings::domstring::BytesView;
13use servo_url::ServoUrl;
14use tempfile::NamedTempFile;
15use uuid::Uuid;
16
17use crate::dom::bindings::str::DOMString;
18
19pub(crate) trait ScriptSource {
20 fn unminified_dir(&self) -> Option<String>;
21 fn extract_bytes(&self) -> BytesView<'_>;
22 fn rewrite_source(&mut self, source: Rc<DOMString>);
23 fn url(&self) -> ServoUrl;
24 fn is_external(&self) -> bool;
25}
26
27pub(crate) fn create_temp_files() -> Option<(NamedTempFile, File)> {
28 let (input, output) = (NamedTempFile::new(), tempfile::tempfile());
34 if let (Ok(input), Ok(output)) = (input, output) {
35 Some((input, output))
36 } else {
37 log::warn!("Error creating input and output temp files");
38 None
39 }
40}
41
42#[derive(Debug)]
43pub(crate) enum BeautifyFileType {
44 Css,
45 Js,
46}
47
48pub(crate) fn execute_js_beautify(input: &Path, output: File, file_type: BeautifyFileType) -> bool {
49 let mut cmd = Command::new("js-beautify");
50 match file_type {
51 BeautifyFileType::Js => (),
52 BeautifyFileType::Css => {
53 cmd.arg("--type").arg("css");
54 },
55 }
56 match cmd.arg(input).stdout(output).status() {
57 Ok(status) => status.success(),
58 _ => {
59 log::warn!(
60 "Failed to execute js-beautify --type {:?}, Will store unmodified script",
61 file_type
62 );
63 false
64 },
65 }
66}
67
68pub fn create_output_file(
69 unminified_dir: String,
70 url: &ServoUrl,
71 external: Option<bool>,
72) -> Result<File, Error> {
73 let path = PathBuf::from(unminified_dir);
74
75 let url_path = &url[url::Position::BeforeHost..url::Position::AfterPath];
79
80 let (base, has_name) = match url.as_str().ends_with('/') {
81 true => (path.join(url_path).as_path().to_owned(), false),
82 false => (path.join(url_path).parent().unwrap().to_owned(), true),
83 };
84
85 create_dir_all(&base)?;
86
87 let path = if external.unwrap_or(true) && has_name {
88 path.join(url_path)
90 } else {
91 base.join(Uuid::new_v4().to_string())
93 };
94
95 debug!("Unminified files will be stored in {:?}", path);
96
97 File::create(path)
98}
99
100pub(crate) fn unminify_js(script: &mut dyn ScriptSource) {
101 let Some(unminified_dir) = script.unminified_dir() else {
102 return;
103 };
104
105 if let Some((mut input, mut output)) = create_temp_files() {
106 input.write_all(&script.extract_bytes()).unwrap();
107
108 if execute_js_beautify(
109 input.path(),
110 output.try_clone().unwrap(),
111 BeautifyFileType::Js,
112 ) {
113 let mut script_content = String::new();
114 output.seek(std::io::SeekFrom::Start(0)).unwrap();
115 output.read_to_string(&mut script_content).unwrap();
116 script.rewrite_source(Rc::new(DOMString::from(script_content)));
117 }
118 }
119
120 match create_output_file(unminified_dir, &script.url(), Some(script.is_external())) {
121 Ok(mut file) => file.write_all(&script.extract_bytes()).unwrap(),
122 Err(why) => warn!("Could not store script {:?}", why),
123 }
124}
125
126pub(crate) fn unminified_path(dir: &str) -> String {
127 let mut path = env::current_dir().unwrap();
128 path.push(dir);
129 path.into_os_string().into_string().unwrap()
130}