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