diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 65a8039e261..6085de8fe8f 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -66,6 +66,7 @@ impl DissimilarOriginWindow { global_to_clone_from.get_user_agent(), global_to_clone_from.wgpu_id_hub(), Some(global_to_clone_from.is_secure_context()), + false, ), window_proxy: Dom::from_ref(window_proxy), location: Default::default(), diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 47ace9c8455..811e9f03eaa 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -143,6 +143,7 @@ use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::{ IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback, }; +use crate::unminify::unminified_path; #[derive(JSTraceable)] pub struct AutoCloseWorker { @@ -356,6 +357,10 @@ pub struct GlobalScope { /// Is considered in a secure context inherited_secure_context: Option, + + /// Directory to store unminified scripts for this window if unminify-js + /// opt is enabled. + unminified_js_dir: Option, } /// A wrapper for glue-code between the ipc router and the event-loop. @@ -764,6 +769,7 @@ impl GlobalScope { user_agent: Cow<'static, str>, gpu_id_hub: Arc, inherited_secure_context: Option, + unminify_js: bool, ) -> Self { Self { message_port_state: DomRefCell::new(MessagePortState::UnManaged), @@ -805,6 +811,7 @@ impl GlobalScope { console_count_map: Default::default(), dynamic_modules: DomRefCell::new(DynamicModuleList::new()), inherited_secure_context, + unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")), } } @@ -3439,6 +3446,10 @@ impl GlobalScope { network_listener.into_callback(), ); } + + pub fn unminified_js_dir(&self) -> Option { + self.unminified_js_dir.clone() + } } /// Returns the Rust global scope from a JS global object. diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 34239b0d836..6b55cf0f220 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -4,8 +4,7 @@ #![allow(unused_imports)] use core::ffi::c_void; use std::cell::Cell; -use std::fs::{create_dir_all, read_to_string, File}; -use std::io::{Read, Seek, Write}; +use std::fs::read_to_string; use std::path::PathBuf; use std::process::Command; use std::ptr; @@ -70,9 +69,34 @@ use crate::script_runtime::CanGc; use crate::task::TaskCanceller; use crate::task_source::dom_manipulation::DOMManipulationTaskSource; use crate::task_source::{TaskSource, TaskSourceName}; -use crate::unminify::{ - create_output_file, create_temp_files, execute_js_beautify, BeautifyFileType, -}; +use crate::unminify::{unminify_js, ScriptSource}; + +impl ScriptSource for ScriptOrigin { + fn unminified_dir(&self) -> Option { + self.unminified_dir.clone() + } + + fn extract_bytes(&self) -> &[u8] { + match &self.code { + SourceCode::Text(text) => text.as_bytes(), + SourceCode::Compiled(compiled_source_code) => { + compiled_source_code.original_text.as_bytes() + }, + } + } + + fn rewrite_source(&mut self, source: Rc) { + self.code = SourceCode::Text(source); + } + + fn url(&self) -> ServoUrl { + self.url.clone() + } + + fn is_external(&self) -> bool { + self.external + } +} // TODO Implement offthread compilation in mozjs /*pub struct OffThreadCompilationContext { @@ -263,6 +287,7 @@ pub struct ScriptOrigin { external: bool, fetch_options: ScriptFetchOptions, type_: ScriptType, + unminified_dir: Option, } impl ScriptOrigin { @@ -271,6 +296,7 @@ impl ScriptOrigin { url: ServoUrl, fetch_options: ScriptFetchOptions, type_: ScriptType, + unminified_dir: Option, ) -> ScriptOrigin { ScriptOrigin { code: SourceCode::Text(text), @@ -278,6 +304,7 @@ impl ScriptOrigin { external: false, fetch_options, type_, + unminified_dir, } } @@ -286,6 +313,7 @@ impl ScriptOrigin { url: ServoUrl, fetch_options: ScriptFetchOptions, type_: ScriptType, + unminified_dir: Option, ) -> ScriptOrigin { ScriptOrigin { code: SourceCode::Text(text), @@ -293,6 +321,7 @@ impl ScriptOrigin { external: true, fetch_options, type_, + unminified_dir, } } @@ -471,6 +500,7 @@ impl FetchResponseListener for ClassicContext { final_url.clone(), self.fetch_options.clone(), ScriptType::Classic, + elem.parser_document.global().unminified_js_dir(), ); finish_fetching_a_classic_script( &elem, @@ -813,6 +843,7 @@ impl HTMLScriptElement { base_url.clone(), options.clone(), script_type, + self.global().unminified_js_dir(), )); // Step 27-2. @@ -855,51 +886,6 @@ impl HTMLScriptElement { } } - fn unminify_js(&self, script: &mut ScriptOrigin) { - if self.parser_document.window().unminified_js_dir().is_none() { - return; - } - - if let Some((mut input, mut output)) = create_temp_files() { - match &script.code { - SourceCode::Text(text) => { - input.write_all(text.as_bytes()).unwrap(); - }, - SourceCode::Compiled(compiled_source_code) => { - input - .write_all(compiled_source_code.original_text.as_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.code = SourceCode::Text(Rc::new(DOMString::from(script_content))); - } - } - - match create_output_file( - window_from_node(self).unminified_js_dir(), - &script.url, - Some(script.external), - ) { - Ok(mut file) => match &script.code { - SourceCode::Text(text) => file.write_all(text.as_bytes()).unwrap(), - SourceCode::Compiled(compiled_source_code) => { - file.write_all(compiled_source_code.original_text.as_bytes()) - .unwrap(); - }, - }, - Err(why) => warn!("Could not store script {:?}", why), - } - } - fn substitute_with_local_script(&self, script: &mut ScriptOrigin) { if self .parser_document @@ -948,7 +934,7 @@ impl HTMLScriptElement { }; if script.type_ == ScriptType::Classic { - self.unminify_js(&mut script); + unminify_js(&mut script); self.substitute_with_local_script(&mut script); } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9121fa75fc0..d6f1dae848f 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -4,6 +4,7 @@ use std::borrow::{Cow, ToOwned}; use std::cell::{Cell, RefCell, RefMut}; +use std::cmp; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::default::Default; @@ -13,7 +14,6 @@ use std::rc::Rc; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use std::time::Duration; -use std::{cmp, env}; use app_units::Au; use backtrace::Backtrace; @@ -154,6 +154,7 @@ use crate::script_thread::{ use crate::task_manager::TaskManager; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::{IsInterval, TimerCallback}; +use crate::unminify::unminified_path; use crate::webdriver_handlers::jsval_to_webdriver; use crate::{fetch, window_named_properties}; @@ -294,10 +295,6 @@ pub struct Window { /// available at some point in the future. pending_layout_images: DomRefCell>>>, - /// Directory to store unminified scripts for this window if unminify-js - /// opt is enabled. - unminified_js_dir: DomRefCell>, - /// Directory to store unminified css for this window if unminify-css /// opt is enabled. unminified_css_dir: DomRefCell>, @@ -332,9 +329,6 @@ pub struct Window { /// True if it is safe to write to the image. prepare_for_screenshot: bool, - /// Unminify Javascript. - unminify_js: bool, - /// Unminify Css. unminify_css: bool, @@ -2259,13 +2253,9 @@ impl Window { assert!(document.window() == self); self.document.set(Some(document)); - set_unminified_path(self.unminify_js, &self.unminified_js_dir, "unminified-js"); - - set_unminified_path( - self.unminify_css, - &self.unminified_css_dir, - "unminified-css", - ); + if self.unminify_css { + *self.unminified_css_dir.borrow_mut() = Some(unminified_path("unminified-css")); + } } /// Commence a new URL load which will either replace this window or scroll to a fragment. @@ -2528,10 +2518,6 @@ impl Window { self.throttled.get() } - pub fn unminified_js_dir(&self) -> Option { - self.unminified_js_dir.borrow().clone() - } - pub fn unminified_css_dir(&self) -> Option { self.unminified_css_dir.borrow().clone() } @@ -2636,6 +2622,7 @@ impl Window { user_agent, gpu_id_hub, inherited_secure_context, + unminify_js, ), script_chan, task_manager, @@ -2676,7 +2663,6 @@ impl Window { webgl_chan, webxr_registry, pending_layout_images: Default::default(), - unminified_js_dir: Default::default(), unminified_css_dir: Default::default(), local_script_source, test_worklet: Default::default(), @@ -2687,7 +2673,6 @@ impl Window { has_sent_idle_message: Cell::new(false), relayout_event, prepare_for_screenshot, - unminify_js, unminify_css, userscripts_path, replace_surrogates, @@ -2920,12 +2905,3 @@ fn is_named_element_with_name_attribute(elem: &Element) -> bool { fn is_named_element_with_id_attribute(elem: &Element) -> bool { elem.is_html_element() } - -fn set_unminified_path(option: bool, dir_ref: &DomRefCell>, folder_name: &str) { - if option { - // Set a path for the document host to store unminified files. - let mut path = env::current_dir().unwrap(); - path.push(folder_name); - *dir_ref.borrow_mut() = Some(path.into_os_string().into_string().unwrap()); - } -} diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 2d317c8a131..258d58e6cf6 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -166,6 +166,7 @@ impl WorkerGlobalScope { init.user_agent, gpu_id_hub, init.inherited_secure_context, + false, ), worker_id: init.worker_id, worker_name, diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 307bcab46c8..4956e6e3358 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -101,6 +101,7 @@ impl WorkletGlobalScope { init.user_agent.clone(), init.gpu_id_hub.clone(), init.inherited_secure_context, + false, ), base_url, to_script_thread_sender: init.to_script_thread_sender.clone(), diff --git a/components/script/script_module.rs b/components/script/script_module.rs index 39e37542aaa..86b7134adc7 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -418,8 +418,37 @@ pub enum ModuleStatus { Finished, } +struct ModuleSource { + source: Rc, + unminified_dir: Option, + external: bool, + url: ServoUrl, +} + +impl crate::unminify::ScriptSource for ModuleSource { + fn unminified_dir(&self) -> Option { + self.unminified_dir.clone() + } + + fn extract_bytes(&self) -> &[u8] { + self.source.as_bytes() + } + + fn rewrite_source(&mut self, source: Rc) { + self.source = source; + } + + fn url(&self) -> ServoUrl { + self.url.clone() + } + + fn is_external(&self) -> bool { + self.external + } +} + impl ModuleTree { - #[allow(unsafe_code)] + #[allow(unsafe_code, clippy::too_many_arguments)] /// /// Step 7-11. fn compile_module_script( @@ -430,17 +459,25 @@ impl ModuleTree { url: &ServoUrl, options: ScriptFetchOptions, mut module_script: RustMutableHandleObject, + inline: bool, ) -> Result<(), RethrowError> { let cx = GlobalScope::get_cx(); let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject()); let compile_options = unsafe { CompileOptionsWrapper::new(*cx, url.as_str(), 1) }; + let mut module_source = ModuleSource { + source: module_script_text, + unminified_dir: global.unminified_js_dir(), + external: !inline, + url: url.clone(), + }; + crate::unminify::unminify_js(&mut module_source); unsafe { module_script.set(CompileModule1( *cx, compile_options.ptr, - &mut transform_str_to_source_text(&module_script_text), + &mut transform_str_to_source_text(&module_source.source), )); if module_script.is_null() { @@ -931,12 +968,14 @@ impl ModuleOwner { script_src.clone(), fetch_options, ScriptType::Module, + global.unminified_js_dir(), )), ModuleIdentity::ScriptId(_) => Ok(ScriptOrigin::internal( Rc::clone(&module_tree.get_text().borrow()), document.base_url().clone(), fetch_options, ScriptType::Module, + global.unminified_js_dir(), )), }, } @@ -1149,6 +1188,7 @@ impl FetchResponseListener for ModuleContext { meta.final_url, self.options.clone(), ScriptType::Module, + global.unminified_js_dir(), )) }); @@ -1178,6 +1218,7 @@ impl FetchResponseListener for ModuleContext { &self.url, self.options.clone(), compiled_module.handle_mut(), + false, ); match compiled_module_result { @@ -1748,6 +1789,7 @@ pub(crate) fn fetch_inline_module_script( &url, options.clone(), compiled_module.handle_mut(), + true, ); match compiled_module_result { diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index d076d1e7f06..590918606b3 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -94,9 +94,9 @@ pub struct StylesheetContext { impl StylesheetContext { fn unminify_css(&self, data: Vec, file_url: ServoUrl) -> Vec { - if self.document.root().window().unminified_css_dir().is_none() { + let Some(unminified_dir) = self.document.root().window().unminified_css_dir() else { return data; - } + }; let mut style_content = data; @@ -110,11 +110,7 @@ impl StylesheetContext { output.read_to_end(&mut style_content).unwrap(); } } - match create_output_file( - self.document.root().window().unminified_css_dir(), - &file_url, - None, - ) { + match create_output_file(unminified_dir, &file_url, None) { Ok(mut file) => { file.write_all(&style_content).unwrap(); }, diff --git a/components/script/unminify.rs b/components/script/unminify.rs index 5292e68fedb..81bb5beeac9 100644 --- a/components/script/unminify.rs +++ b/components/script/unminify.rs @@ -2,15 +2,27 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::env; use std::fs::{create_dir_all, File}; -use std::io::{Error, ErrorKind}; +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; + fn extract_bytes(&self) -> &[u8]; + fn rewrite_source(&mut self, source: Rc); + fn url(&self) -> ServoUrl; + fn is_external(&self) -> bool; +} + pub fn create_temp_files() -> Option<(NamedTempFile, File)> { // Write the minified code to a temporary file and pass its path as an argument // to js-beautify to read from. Meanwhile, redirect the process' stdout into @@ -53,20 +65,11 @@ pub fn execute_js_beautify(input: &Path, output: File, file_type: BeautifyFileTy } pub fn create_output_file( - unminified_dir: Option, + unminified_dir: String, url: &ServoUrl, external: Option, ) -> Result { - let path = match unminified_dir { - Some(unminified_dir) => PathBuf::from(unminified_dir), - None => { - warn!("Unminified file directory not found"); - return Err(Error::new( - ErrorKind::NotFound, - "Unminified file directory not found", - )); - }, - }; + let path = PathBuf::from(unminified_dir); let (base, has_name) = match url.as_str().ends_with('/') { true => ( @@ -98,3 +101,35 @@ pub fn create_output_file( 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() +}