use std::env;
use std::fs::{create_dir_all, File};
use std::io::{Error, Read, Seek, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::rc::Rc;
use servo_url::ServoUrl;
use tempfile::NamedTempFile;
use uuid::Uuid;
use crate::dom::bindings::str::DOMString;
pub(crate) trait ScriptSource {
fn unminified_dir(&self) -> Option<String>;
fn extract_bytes(&self) -> &[u8];
fn rewrite_source(&mut self, source: Rc<DOMString>);
fn url(&self) -> ServoUrl;
fn is_external(&self) -> bool;
}
pub fn create_temp_files() -> Option<(NamedTempFile, File)> {
let (input, output) = (NamedTempFile::new(), tempfile::tempfile());
if let (Ok(input), Ok(output)) = (input, output) {
Some((input, output))
} else {
log::warn!("Error creating input and output temp files");
None
}
}
#[derive(Debug)]
pub enum BeautifyFileType {
Css,
Js,
}
pub fn execute_js_beautify(input: &Path, output: File, file_type: BeautifyFileType) -> bool {
let mut cmd = Command::new("js-beautify");
match file_type {
BeautifyFileType::Js => (),
BeautifyFileType::Css => {
cmd.arg("--type").arg("css");
},
}
match cmd.arg(input).stdout(output).status() {
Ok(status) => status.success(),
_ => {
log::warn!(
"Failed to execute js-beautify --type {:?}, Will store unmodified script",
file_type
);
false
},
}
}
pub fn create_output_file(
unminified_dir: String,
url: &ServoUrl,
external: Option<bool>,
) -> Result<File, Error> {
let path = PathBuf::from(unminified_dir);
let (base, has_name) = match url.as_str().ends_with('/') {
true => (
path.join(&url[url::Position::BeforeHost..])
.as_path()
.to_owned(),
false,
),
false => (
path.join(&url[url::Position::BeforeHost..])
.parent()
.unwrap()
.to_owned(),
true,
),
};
create_dir_all(&base)?;
let path = if external.unwrap_or(true) && has_name {
path.join(&url[url::Position::BeforeHost..])
} else {
base.join(Uuid::new_v4().to_string())
};
debug!("Unminified files will be stored in {:?}", path);
File::create(path)
}
pub(crate) fn unminify_js(script: &mut dyn ScriptSource) {
let Some(unminified_dir) = script.unminified_dir() else {
return;
};
if let Some((mut input, mut output)) = create_temp_files() {
input.write_all(script.extract_bytes()).unwrap();
if execute_js_beautify(
input.path(),
output.try_clone().unwrap(),
BeautifyFileType::Js,
) {
let mut script_content = String::new();
output.seek(std::io::SeekFrom::Start(0)).unwrap();
output.read_to_string(&mut script_content).unwrap();
script.rewrite_source(Rc::new(DOMString::from(script_content)));
}
}
match create_output_file(unminified_dir, &script.url(), Some(script.is_external())) {
Ok(mut file) => file.write_all(script.extract_bytes()).unwrap(),
Err(why) => warn!("Could not store script {:?}", why),
}
}
pub(crate) fn unminified_path(dir: &str) -> String {
let mut path = env::current_dir().unwrap();
path.push(dir);
path.into_os_string().into_string().unwrap()
}