From d49689bf096b6b90dcf969c26637a7ab02599f00 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 25 Apr 2025 22:01:08 +0800 Subject: [PATCH] Support `isSecureContext` for custom protocols Signed-off-by: Tony --- components/constellation/constellation.rs | 23 +++++++++++++++- components/constellation/pipeline.rs | 8 +++++- components/net/protocols/mod.rs | 4 +++ .../script/dom/dissimilaroriginwindow.rs | 1 + components/script/dom/globalscope.rs | 16 ++++++++++-- components/script/dom/window.rs | 4 ++- components/script/dom/workerglobalscope.rs | 3 +++ components/script/dom/workletglobalscope.rs | 5 +++- components/script/script_thread.rs | 9 ++++++- components/servo/lib.rs | 5 +++- .../constellation/from_script_message.rs | 5 +++- components/shared/net/lib.rs | 26 +++++++++++++++++++ components/shared/script/lib.rs | 4 ++- 13 files changed, 103 insertions(+), 10 deletions(-) diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 05081fe0ba7..021cfc08a7a 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -142,10 +142,11 @@ use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::{Key, KeyState, KeyboardEvent, Modifiers}; use log::{debug, error, info, trace, warn}; use media::WindowGLContext; +use net::protocols::ProtocolRegistry; use net_traits::pub_domains::reg_host; use net_traits::request::Referrer; use net_traits::storage_thread::{StorageThreadMsg, StorageType}; -use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads}; +use net_traits::{self, IpcSend, Protocol, Protocols, ReferrerPolicy, ResourceThreads}; use profile_traits::{mem, time}; use script_layout_interface::{LayoutFactory, ScriptThreadFactory}; use script_traits::{ @@ -474,6 +475,9 @@ pub struct Constellation { /// The process manager. process_manager: ProcessManager, + + /// Registered custom protocols + pub protocols: Arc, } /// State needed to construct a constellation. @@ -526,6 +530,9 @@ pub struct InitialConstellationState { /// User content manager pub user_content_manager: UserContentManager, + + /// Registered custom protocols + pub protocols: Arc, } /// Data needed for webdriver @@ -741,6 +748,7 @@ where rippy_data, user_content_manager: state.user_content_manager, process_manager: ProcessManager::new(state.mem_profiler_chan), + protocols: state.protocols, }; constellation.run(); @@ -980,6 +988,19 @@ where player_context: WindowGLContext::get(), rippy_data: self.rippy_data.clone(), user_content_manager: self.user_content_manager.clone(), + protocols: Protocols::new( + self.protocols + .iter() + .map(|(protocol, handler)| { + ( + protocol.to_string(), + Protocol { + is_secure: handler.is_secure(), + }, + ) + }) + .collect(), + ), }); let pipeline = match result { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 2e139578ffe..93f03568153 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -33,8 +33,8 @@ use ipc_channel::router::ROUTER; use log::{debug, error, warn}; use media::WindowGLContext; use net::image_cache::ImageCacheImpl; -use net_traits::ResourceThreads; use net_traits::image_cache::ImageCache; +use net_traits::{Protocols, ResourceThreads}; use profile::system_reporter; use profile_traits::mem::{ProfilerMsg, Reporter}; use profile_traits::{mem as profile_mem, time}; @@ -197,6 +197,9 @@ pub struct InitialPipelineState { /// User content manager pub user_content_manager: UserContentManager, + + /// Registered custom protocols + pub protocols: Protocols, } pub struct NewPipeline { @@ -294,6 +297,7 @@ impl Pipeline { rippy_data: state.rippy_data, user_content_manager: state.user_content_manager, lifeline_sender: None, + protocols: state.protocols, }; // Spawn the child process. @@ -503,6 +507,7 @@ pub struct UnprivilegedPipelineContent { player_context: WindowGLContext, rippy_data: Vec, user_content_manager: UserContentManager, + protocols: Protocols, lifeline_sender: Option>, } @@ -549,6 +554,7 @@ impl UnprivilegedPipelineContent { player_context: self.player_context.clone(), inherited_secure_context: self.load_data.inherited_secure_context, user_content_manager: self.user_content_manager, + protocols: self.protocols.clone(), }, layout_factory, Arc::new(self.system_font_service.to_proxy()), diff --git a/components/net/protocols/mod.rs b/components/net/protocols/mod.rs index 6dc58ceab64..5ce62059401 100644 --- a/components/net/protocols/mod.rs +++ b/components/net/protocols/mod.rs @@ -110,6 +110,10 @@ impl ProtocolRegistry { self.handlers.get(scheme).map(|e| e.as_ref()) } + pub fn iter(&self) -> std::collections::hash_map::Iter<'_, String, Box> { + self.handlers.iter() + } + pub fn merge(&mut self, mut other: ProtocolRegistry) { for (scheme, handler) in other.handlers.drain() { if FORBIDDEN_SCHEMES.contains(&scheme.as_str()) { diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index b7fbe0855fe..bf71cae611a 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -68,6 +68,7 @@ impl DissimilarOriginWindow { global_to_clone_from.wgpu_id_hub(), Some(global_to_clone_from.is_secure_context()), false, + global_to_clone_from.registered_protocols().clone(), ), 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 77d1ee37c03..5e917da8355 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -57,7 +57,7 @@ use net_traits::policy_container::PolicyContainer; use net_traits::request::{InsecureRequestsPolicy, Referrer, RequestBuilder}; use net_traits::response::HttpsState; use net_traits::{ - CoreResourceMsg, CoreResourceThread, FetchResponseListener, IpcSend, ReferrerPolicy, + CoreResourceMsg, CoreResourceThread, FetchResponseListener, IpcSend, Protocols, ReferrerPolicy, ResourceThreads, fetch_async, }; use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time}; @@ -376,6 +376,11 @@ pub(crate) struct GlobalScope { #[ignore_malloc_size_of = "Rc is hard"] notification_permission_request_callback_map: DomRefCell>>, + + /// Registered custom protocols + #[no_trace] + #[ignore_malloc_size_of = "Arc"] + protocols: Arc, } /// A wrapper for glue-code between the ipc router and the event-loop. @@ -731,6 +736,7 @@ impl GlobalScope { #[cfg(feature = "webgpu")] gpu_id_hub: Arc, inherited_secure_context: Option, unminify_js: bool, + protocols: Arc, ) -> Self { Self { task_manager: Default::default(), @@ -775,6 +781,7 @@ impl GlobalScope { byte_length_queuing_strategy_size_function: OnceCell::new(), count_queuing_strategy_size_function: OnceCell::new(), notification_permission_request_callback_map: Default::default(), + protocols, } } @@ -2383,6 +2390,11 @@ impl GlobalScope { &self.creation_url } + /// Get registered custom protocols + pub(crate) fn registered_protocols(&self) -> &Arc { + &self.protocols + } + pub(crate) fn image_cache(&self) -> Arc { if let Some(window) = self.downcast::() { return window.image_cache(); @@ -3098,7 +3110,7 @@ impl GlobalScope { if creation_url.scheme() == "blob" && Some(true) == self.inherited_secure_context { return true; } - return creation_url.is_potentially_trustworthy(); + return self.protocols.is_url_potentially_trustworthy(creation_url); } false } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9053f7f7e86..cfd54d09e34 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -52,11 +52,11 @@ use js::rust::{ }; use malloc_size_of::MallocSizeOf; use media::WindowGLContext; -use net_traits::ResourceThreads; use net_traits::image_cache::{ ImageCache, ImageResponder, ImageResponse, PendingImageId, PendingImageResponse, }; use net_traits::storage_thread::StorageType; +use net_traits::{Protocols, ResourceThreads}; use num_traits::ToPrimitive; use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; @@ -2834,6 +2834,7 @@ impl Window { player_context: WindowGLContext, #[cfg(feature = "webgpu")] gpu_id_hub: Arc, inherited_secure_context: Option, + protocols: Arc, ) -> DomRoot { let error_reporter = CSSErrorReporter { pipelineid: pipeline_id, @@ -2861,6 +2862,7 @@ impl Window { gpu_id_hub, inherited_secure_context, unminify_js, + protocols, ), script_chan, layout: RefCell::new(layout), diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 695119715e4..baa00d1e7d1 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -83,6 +83,7 @@ pub(crate) fn prepare_workerscope_init( origin: global.origin().immutable().clone(), creation_url: global.creation_url().clone(), inherited_secure_context: Some(global.is_secure_context()), + protocols: global.registered_protocols().clone(), }; init @@ -155,6 +156,7 @@ impl WorkerGlobalScope { Some(..) => Some(devtools_receiver), None => None, }; + let protocols = init.protocols; Self { globalscope: GlobalScope::new_inherited( @@ -171,6 +173,7 @@ impl WorkerGlobalScope { gpu_id_hub, init.inherited_secure_context, false, + protocols, ), worker_id: init.worker_id, worker_name, diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 6e81220731c..dc04fe3dfa1 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -12,8 +12,8 @@ use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; use js::jsval::UndefinedValue; use js::rust::Runtime; -use net_traits::ResourceThreads; use net_traits::image_cache::ImageCache; +use net_traits::{Protocols, ResourceThreads}; use profile_traits::{mem, time}; use script_bindings::realms::InRealm; use script_traits::Painter; @@ -108,6 +108,7 @@ impl WorkletGlobalScope { init.gpu_id_hub.clone(), init.inherited_secure_context, false, + init.protocols.clone(), ), base_url, to_script_thread_sender: init.to_script_thread_sender.clone(), @@ -197,6 +198,8 @@ pub(crate) struct WorkletGlobalScopeInit { pub(crate) gpu_id_hub: Arc, /// Is considered secure pub(crate) inherited_secure_context: Option, + /// Registered custom protocols + pub(crate) protocols: Arc, } /// diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index f78b5bf281b..f27f883cf66 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -75,7 +75,7 @@ use net_traits::request::{Referrer, RequestId}; use net_traits::response::ResponseInit; use net_traits::storage_thread::StorageType; use net_traits::{ - FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError, + FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError, Protocols, ResourceFetchTiming, ResourceThreads, ResourceTimingType, }; use percent_encoding::percent_decode; @@ -335,6 +335,10 @@ pub struct ScriptThread { // In future, this shall be mouse_down_point for primary button #[no_trace] relative_mouse_down_point: Cell>, + + /// Registered custom protocols + #[no_trace] + protocols: Arc, } struct BHMExitSignal { @@ -744,6 +748,7 @@ impl ScriptThread { #[cfg(feature = "webgpu")] gpu_id_hub: script_thread.gpu_id_hub.clone(), inherited_secure_context: script_thread.inherited_secure_context, + protocols: script_thread.protocols.clone(), }; Rc::new(WorkletThreadPool::spawn(init)) }) @@ -949,6 +954,7 @@ impl ScriptThread { inherited_secure_context: state.inherited_secure_context, layout_factory, relative_mouse_down_point: Cell::new(Point2D::zero()), + protocols: Arc::new(state.protocols), } } @@ -3125,6 +3131,7 @@ impl ScriptThread { #[cfg(feature = "webgpu")] self.gpu_id_hub.clone(), incomplete.load_data.inherited_secure_context, + self.protocols.clone(), ); let _realm = enter_realm(&*window); diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 7fb990527ec..cd9c94ceffd 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -1032,6 +1032,8 @@ fn create_constellation( let bluetooth_thread: IpcSender = BluetoothThreadFactory::new(embedder_proxy.clone()); + let protocols = Arc::new(protocols); + let (public_resource_threads, private_resource_threads) = new_resource_threads( devtools_sender.clone(), time_profiler_chan.clone(), @@ -1040,7 +1042,7 @@ fn create_constellation( config_dir, opts.certificate_path.clone(), opts.ignore_certificate_errors, - Arc::new(protocols), + protocols.clone(), ); let system_font_service = Arc::new( @@ -1075,6 +1077,7 @@ fn create_constellation( #[cfg(feature = "webgpu")] wgpu_image_map, user_content_manager, + protocols, }; let layout_factory = Arc::new(LayoutFactoryImpl()); diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index 8346551fd15..a1d6b1b939b 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, VecDeque}; use std::fmt; +use std::sync::Arc; use base::Epoch; use base::id::{ @@ -23,7 +24,7 @@ use ipc_channel::Error as IpcError; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use net_traits::request::{InsecureRequestsPolicy, Referrer, RequestBody}; use net_traits::storage_thread::StorageType; -use net_traits::{CoreResourceMsg, ReferrerPolicy, ResourceThreads}; +use net_traits::{CoreResourceMsg, Protocols, ReferrerPolicy, ResourceThreads}; use profile_traits::mem::MemoryReportResult; use profile_traits::{mem, time as profile_time}; use serde::{Deserialize, Serialize}; @@ -433,6 +434,8 @@ pub struct WorkerGlobalScopeInit { pub creation_url: Option, /// True if secure context pub inherited_secure_context: Option, + /// Registered custom protocols + pub protocols: Arc, } /// Common entities representing a network load origin diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 05ad102f42f..251a1ca2ddd 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -1013,3 +1013,29 @@ pub fn set_default_accept_language(headers: &mut HeaderMap) { pub static PRIVILEGED_SECRET: LazyLock = LazyLock::new(|| servo_rand::ServoRng::default().next_u32()); + +/// Registered custom protocols +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Protocols(HashMap); + +impl Protocols { + /// Construct from a HashMap of string and protocols + pub fn new(protocols: HashMap) -> Self { + Self(protocols) + } + + /// Test if the URL is potentially trustworthy or the custom protocol is registered as secure + pub fn is_url_potentially_trustworthy(&self, url: &ServoUrl) -> bool { + url.is_potentially_trustworthy() || + self.0 + .get(url.scheme()) + .is_some_and(|protocol| protocol.is_secure) + } +} + +/// A custom protocol +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Protocol { + /// If this custom protocol is considered secure context + pub is_secure: bool, +} diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 7323907cba3..d65b6a4e80e 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -35,9 +35,9 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender}; use keyboard_types::Modifiers; use malloc_size_of_derive::MallocSizeOf; use media::WindowGLContext; -use net_traits::ResourceThreads; use net_traits::image_cache::ImageCache; use net_traits::storage_thread::StorageType; +use net_traits::{Protocols, ResourceThreads}; use pixels::PixelFormat; use profile_traits::mem; use serde::{Deserialize, Serialize}; @@ -324,6 +324,8 @@ pub struct InitialScriptState { pub player_context: WindowGLContext, /// User content manager pub user_content_manager: UserContentManager, + /// Registered custom protocols + pub protocols: Protocols, } /// Errors from executing a paint worklet