aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/constellation/constellation.rs70
-rw-r--r--components/constellation/lib.rs3
-rw-r--r--components/constellation/pipeline.rs150
-rw-r--r--components/constellation/sandboxing.rs202
-rw-r--r--components/constellation/serviceworker.rs56
-rw-r--r--components/script/init.rs7
-rw-r--r--components/script/lib.rs4
-rw-r--r--components/script/serviceworker_manager.rs64
-rw-r--r--components/script_traits/lib.rs7
-rw-r--r--components/script_traits/script_msg.rs15
-rw-r--r--components/servo/lib.rs72
11 files changed, 405 insertions, 245 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 25ba57b04df..265db68e7ae 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -96,6 +96,7 @@ use crate::browsingcontext::{
use crate::event_loop::EventLoop;
use crate::network_listener::NetworkListener;
use crate::pipeline::{InitialPipelineState, Pipeline};
+use crate::serviceworker::ServiceWorkerUnprivilegedContent;
use crate::session_history::{
JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
};
@@ -151,10 +152,15 @@ use script_traits::{HistoryEntryReplacement, IFrameSizeMsg, WindowSizeData, Wind
use script_traits::{
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
};
-use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
+use script_traits::{
+ LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
+ ServiceWorkerManagerFactory,
+};
use script_traits::{MediaSessionActionType, MouseEventType};
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
-use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
+use script_traits::{
+ SWManagerMsg, SWManagerSenders, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg,
+};
use serde::{Deserialize, Serialize};
use servo_config::{opts, pref};
use servo_rand::{random, Rng, ServoRng, SliceRandom};
@@ -259,7 +265,7 @@ struct BrowsingContextGroup {
/// `LayoutThread` in the `layout` crate, and `ScriptThread` in
/// the `script` crate). Script and layout communicate using a `Message`
/// type.
-pub struct Constellation<Message, LTF, STF> {
+pub struct Constellation<Message, LTF, STF, SWF> {
/// An ipc-sender/threaded-receiver pair
/// to facilitate installing pipeline namespaces in threads
/// via a per-process installer.
@@ -348,9 +354,8 @@ pub struct Constellation<Message, LTF, STF> {
/// bluetooth thread.
bluetooth_thread: IpcSender<BluetoothRequest>,
- /// An IPC channel for the constellation to send messages to the
- /// Service Worker Manager thread.
- swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
+ /// A map of origin to sender to a Service worker manager.
+ sw_managers: HashMap<ImmutableOrigin, IpcSender<ServiceWorkerMsg>>,
/// An IPC channel for Service Worker Manager threads to send
/// messages to the constellation. This is the SW Manager thread's
@@ -453,7 +458,7 @@ pub struct Constellation<Message, LTF, STF> {
random_pipeline_closure: Option<(ServoRng, f32)>,
/// Phantom data that keeps the Rust type system happy.
- phantom: PhantomData<(Message, LTF, STF)>,
+ phantom: PhantomData<(Message, LTF, STF, SWF)>,
/// Entry point to create and get channels to a WebGLThread.
webgl_threads: Option<WebGLThreads>,
@@ -813,10 +818,11 @@ fn handle_webrender_message(
}
}
-impl<Message, LTF, STF> Constellation<Message, LTF, STF>
+impl<Message, LTF, STF, SWF> Constellation<Message, LTF, STF, SWF>
where
LTF: LayoutThreadFactory<Message = Message>,
STF: ScriptThreadFactory<Message = Message>,
+ SWF: ServiceWorkerManagerFactory,
{
/// Create a new constellation thread.
pub fn start(
@@ -829,12 +835,11 @@ where
enable_canvas_antialiasing: bool,
canvas_chan: Sender<ConstellationCanvasMsg>,
ipc_canvas_chan: IpcSender<CanvasMsg>,
- ) -> (Sender<FromCompositorMsg>, IpcSender<SWManagerMsg>) {
+ ) -> Sender<FromCompositorMsg> {
let (compositor_sender, compositor_receiver) = unbounded();
// service worker manager to communicate with constellation
let (swmanager_sender, swmanager_receiver) = ipc::channel().expect("ipc channel failure");
- let sw_mgr_clone = swmanager_sender.clone();
thread::Builder::new()
.name("Constellation".to_owned())
@@ -937,7 +942,7 @@ where
}),
);
- let mut constellation: Constellation<Message, LTF, STF> = Constellation {
+ let mut constellation: Constellation<Message, LTF, STF, SWF> = Constellation {
namespace_receiver,
namespace_sender,
script_sender: ipc_script_sender,
@@ -961,9 +966,9 @@ where
public_resource_threads: state.public_resource_threads,
private_resource_threads: state.private_resource_threads,
font_cache_thread: state.font_cache_thread,
- swmanager_chan: None,
+ sw_managers: Default::default(),
swmanager_receiver: swmanager_receiver,
- swmanager_sender: sw_mgr_clone,
+ swmanager_sender,
browsing_context_group_set: Default::default(),
browsing_context_group_next_id: Default::default(),
message_ports: HashMap::new(),
@@ -1022,7 +1027,7 @@ where
})
.expect("Thread spawning failed");
- (compositor_sender, swmanager_sender)
+ compositor_sender
}
/// The main event loop for the constellation.
@@ -1530,9 +1535,9 @@ where
fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
match message {
- SWManagerMsg::OwnSender(sw_sender) => {
- // store service worker manager for communicating with it.
- self.swmanager_chan = Some(sw_sender);
+ SWManagerMsg::PostMessageToClient => {
+ // TODO: implement posting a message to a SW client.
+ // https://github.com/servo/servo/issues/24660
},
}
}
@@ -1965,7 +1970,7 @@ where
self.handle_register_serviceworker(scope_things, scope);
},
FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
- if let Some(ref mgr) = self.swmanager_chan {
+ if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
} else {
warn!("Unable to forward DOMMessage for postMessage call");
@@ -2621,11 +2626,32 @@ where
}
}
- fn handle_register_serviceworker(&self, scope_things: ScopeThings, scope: ServoUrl) {
- if let Some(ref mgr) = self.swmanager_chan {
+ fn handle_register_serviceworker(&mut self, scope_things: ScopeThings, scope: ServoUrl) {
+ let origin = scope.origin();
+
+ if let Some(mgr) = self.sw_managers.get(&origin) {
let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
} else {
- warn!("sending scope info to service worker manager failed");
+ let (own_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
+
+ let sw_senders = SWManagerSenders {
+ swmanager_sender: self.swmanager_sender.clone(),
+ resource_sender: self.public_resource_threads.sender(),
+ own_sender: own_sender.clone(),
+ receiver,
+ };
+ let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin.clone());
+
+ if opts::multiprocess() {
+ if content.spawn_multiprocess().is_err() {
+ return warn!("Failed to spawn process for SW manager.");
+ }
+ } else {
+ content.start::<SWF>();
+ }
+
+ let _ = own_sender.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
+ self.sw_managers.insert(origin, own_sender);
}
}
@@ -2763,7 +2789,7 @@ where
}
debug!("Exiting service worker manager thread.");
- if let Some(mgr) = self.swmanager_chan.as_ref() {
+ for (_, mgr) in self.sw_managers.drain() {
if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) {
warn!("Exit service worker manager failed ({})", e);
}
diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs
index b102a0bc2b5..0e28b880cce 100644
--- a/components/constellation/lib.rs
+++ b/components/constellation/lib.rs
@@ -24,6 +24,7 @@ mod pipeline;
not(target_arch = "aarch64")
))]
mod sandboxing;
+mod serviceworker;
mod session_history;
mod timer_scheduler;
@@ -38,4 +39,4 @@ pub use crate::pipeline::UnprivilegedPipelineContent;
not(target_arch = "arm"),
not(target_arch = "aarch64")
))]
-pub use crate::sandboxing::content_process_sandbox_profile;
+pub use crate::sandboxing::{content_process_sandbox_profile, UnprivilegedContent};
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 665ea177e79..16cb67ab9a2 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::event_loop::EventLoop;
+use crate::sandboxing::{spawn_multiprocess, UnprivilegedContent};
use background_hang_monitor::HangMonitorRegister;
use bluetooth_traits::BluetoothRequest;
use canvas_traits::webgl::WebGLPipeline;
@@ -27,7 +28,7 @@ use msg::constellation_msg::{
};
use net::image_cache::ImageCacheImpl;
use net_traits::image_cache::ImageCache;
-use net_traits::{IpcSend, ResourceThreads};
+use net_traits::ResourceThreads;
use profile_traits::mem as profile_mem;
use profile_traits::time;
use script_traits::{
@@ -35,16 +36,12 @@ use script_traits::{
};
use script_traits::{DocumentActivity, InitialScriptState};
use script_traits::{LayoutControlMsg, LayoutMsg, LoadData};
-use script_traits::{NewLayoutInfo, SWManagerMsg, SWManagerSenders};
+use script_traits::{NewLayoutInfo, SWManagerMsg};
use script_traits::{ScriptThreadFactory, TimerSchedulerMsg, WindowSizeData};
use servo_config::opts::{self, Opts};
use servo_config::{prefs, prefs::PrefValue};
use servo_url::ServoUrl;
use std::collections::{HashMap, HashSet};
-#[cfg(not(windows))]
-use std::env;
-use std::ffi::OsStr;
-use std::process;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
@@ -632,109 +629,8 @@ impl UnprivilegedPipelineContent {
}
}
- #[cfg(any(
- target_os = "android",
- target_arch = "arm",
- all(target_arch = "aarch64", not(target_os = "windows"))
- ))]
pub fn spawn_multiprocess(self) -> Result<(), Error> {
- use ipc_channel::ipc::IpcOneShotServer;
- // Note that this function can panic, due to process creation,
- // avoiding this panic would require a mechanism for dealing
- // with low-resource scenarios.
- let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new()
- .expect("Failed to create IPC one-shot server.");
-
- let path_to_self = env::current_exe().expect("Failed to get current executor.");
- let mut child_process = process::Command::new(path_to_self);
- self.setup_common(&mut child_process, token);
- let _ = child_process
- .spawn()
- .expect("Failed to start unsandboxed child process!");
-
- let (_receiver, sender) = server.accept().expect("Server failed to accept.");
- sender.send(self)?;
-
- Ok(())
- }
-
- #[cfg(all(
- not(target_os = "windows"),
- not(target_os = "ios"),
- not(target_os = "android"),
- not(target_arch = "arm"),
- not(target_arch = "aarch64")
- ))]
- pub fn spawn_multiprocess(self) -> Result<(), Error> {
- use crate::sandboxing::content_process_sandbox_profile;
- use gaol::sandbox::{self, Sandbox, SandboxMethods};
- use ipc_channel::ipc::IpcOneShotServer;
-
- impl CommandMethods for sandbox::Command {
- fn arg<T>(&mut self, arg: T)
- where
- T: AsRef<OsStr>,
- {
- self.arg(arg);
- }
-
- fn env<T, U>(&mut self, key: T, val: U)
- where
- T: AsRef<OsStr>,
- U: AsRef<OsStr>,
- {
- self.env(key, val);
- }
- }
-
- // Note that this function can panic, due to process creation,
- // avoiding this panic would require a mechanism for dealing
- // with low-resource scenarios.
- let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new()
- .expect("Failed to create IPC one-shot server.");
-
- // If there is a sandbox, use the `gaol` API to create the child process.
- if self.opts.sandbox {
- let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
- self.setup_common(&mut command, token);
-
- let profile = content_process_sandbox_profile();
- let _ = Sandbox::new(profile)
- .start(&mut command)
- .expect("Failed to start sandboxed child process!");
- } else {
- let path_to_self = env::current_exe().expect("Failed to get current executor.");
- let mut child_process = process::Command::new(path_to_self);
- self.setup_common(&mut child_process, token);
- let _ = child_process
- .spawn()
- .expect("Failed to start unsandboxed child process!");
- }
-
- let (_receiver, sender) = server.accept().expect("Server failed to accept.");
- sender.send(self)?;
-
- Ok(())
- }
-
- #[cfg(any(target_os = "windows", target_os = "ios"))]
- pub fn spawn_multiprocess(self) -> Result<(), Error> {
- error!("Multiprocess is not supported on Windows or iOS.");
- process::exit(1);
- }
-
- #[cfg(not(windows))]
- fn setup_common<C: CommandMethods>(&self, command: &mut C, token: String) {
- C::arg(command, "--content-process");
- C::arg(command, token);
-
- if let Ok(value) = env::var("RUST_BACKTRACE") {
- C::env(command, "RUST_BACKTRACE", value);
- }
-
- if let Ok(value) = env::var("RUST_LOG") {
- C::env(command, "RUST_LOG", value);
- }
+ spawn_multiprocess(UnprivilegedContent::Pipeline(self))
}
pub fn register_with_background_hang_monitor(
@@ -763,42 +659,4 @@ impl UnprivilegedPipelineContent {
pub fn prefs(&self) -> HashMap<String, PrefValue> {
self.prefs.clone()
}
-
- pub fn swmanager_senders(&self) -> SWManagerSenders {
- SWManagerSenders {
- swmanager_sender: self.swmanager_thread.clone(),
- resource_sender: self.resource_threads.sender(),
- }
- }
-}
-
-/// A trait to unify commands launched as multiprocess with or without a sandbox.
-trait CommandMethods {
- /// A command line argument.
- fn arg<T>(&mut self, arg: T)
- where
- T: AsRef<OsStr>;
-
- /// An environment variable.
- fn env<T, U>(&mut self, key: T, val: U)
- where
- T: AsRef<OsStr>,
- U: AsRef<OsStr>;
-}
-
-impl CommandMethods for process::Command {
- fn arg<T>(&mut self, arg: T)
- where
- T: AsRef<OsStr>,
- {
- self.arg(arg);
- }
-
- fn env<T, U>(&mut self, key: T, val: U)
- where
- T: AsRef<OsStr>,
- U: AsRef<OsStr>,
- {
- self.env(key, val);
- }
}
diff --git a/components/constellation/sandboxing.rs b/components/constellation/sandboxing.rs
index 4b6df8326fc..9905253965b 100644
--- a/components/constellation/sandboxing.rs
+++ b/components/constellation/sandboxing.rs
@@ -2,14 +2,53 @@
* 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 embedder_traits::resources;
+use crate::pipeline::UnprivilegedPipelineContent;
+use crate::serviceworker::ServiceWorkerUnprivilegedContent;
+#[cfg(all(
+ not(target_os = "windows"),
+ not(target_os = "ios"),
+ not(target_os = "android"),
+ not(target_arch = "arm"),
+ not(target_arch = "aarch64")
+))]
use gaol::profile::{Operation, PathPattern, Profile};
-use std::path::PathBuf;
+use ipc_channel::Error;
+use servo_config::opts::Opts;
+use servo_config::prefs::PrefValue;
+use std::collections::HashMap;
+#[cfg(not(windows))]
+use std::env;
+use std::ffi::OsStr;
+use std::process;
+
+#[derive(Deserialize, Serialize)]
+pub enum UnprivilegedContent {
+ Pipeline(UnprivilegedPipelineContent),
+ ServiceWorker(ServiceWorkerUnprivilegedContent),
+}
+
+impl UnprivilegedContent {
+ pub fn opts(&self) -> Opts {
+ match self {
+ UnprivilegedContent::Pipeline(content) => content.opts(),
+ UnprivilegedContent::ServiceWorker(content) => content.opts(),
+ }
+ }
+
+ pub fn prefs(&self) -> HashMap<String, PrefValue> {
+ match self {
+ UnprivilegedContent::Pipeline(content) => content.prefs(),
+ UnprivilegedContent::ServiceWorker(content) => content.prefs(),
+ }
+ }
+}
/// Our content process sandbox profile on Mac. As restrictive as possible.
#[cfg(target_os = "macos")]
pub fn content_process_sandbox_profile() -> Profile {
+ use embedder_traits::resources;
use gaol::platform;
+ use std::path::PathBuf;
let mut operations = vec![
Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/dev/urandom"))),
@@ -46,8 +85,18 @@ pub fn content_process_sandbox_profile() -> Profile {
}
/// Our content process sandbox profile on Linux. As restrictive as possible.
-#[cfg(not(target_os = "macos"))]
+#[cfg(all(
+ not(target_os = "macos"),
+ not(target_os = "windows"),
+ not(target_os = "ios"),
+ not(target_os = "android"),
+ not(target_arch = "arm"),
+ not(target_arch = "aarch64")
+))]
pub fn content_process_sandbox_profile() -> Profile {
+ use embedder_traits::resources;
+ use std::path::PathBuf;
+
let mut operations = vec![Operation::FileReadAll(PathPattern::Literal(PathBuf::from(
"/dev/urandom",
)))];
@@ -65,3 +114,150 @@ pub fn content_process_sandbox_profile() -> Profile {
Profile::new(operations).expect("Failed to create sandbox profile!")
}
+
+#[cfg(any(
+ target_os = "windows",
+ target_os = "ios",
+ target_os = "android",
+ target_arch = "arm",
+ target_arch = "aarch64",
+))]
+pub fn content_process_sandbox_profile() {
+ error!("Sandboxed multiprocess is not supported on this platform.");
+ process::exit(1);
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_arch = "arm",
+ all(target_arch = "aarch64", not(target_os = "windows"))
+))]
+pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
+ use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
+ // Note that this function can panic, due to process creation,
+ // avoiding this panic would require a mechanism for dealing
+ // with low-resource scenarios.
+ let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
+ .expect("Failed to create IPC one-shot server.");
+
+ let path_to_self = env::current_exe().expect("Failed to get current executor.");
+ let mut child_process = process::Command::new(path_to_self);
+ setup_common(&mut child_process, token);
+ let _ = child_process
+ .spawn()
+ .expect("Failed to start unsandboxed child process!");
+
+ let (_receiver, sender) = server.accept().expect("Server failed to accept.");
+ sender.send(content)?;
+
+ Ok(())
+}
+
+#[cfg(all(
+ not(target_os = "windows"),
+ not(target_os = "ios"),
+ not(target_os = "android"),
+ not(target_arch = "arm"),
+ not(target_arch = "aarch64")
+))]
+pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
+ use gaol::sandbox::{self, Sandbox, SandboxMethods};
+ use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
+
+ impl CommandMethods for sandbox::Command {
+ fn arg<T>(&mut self, arg: T)
+ where
+ T: AsRef<OsStr>,
+ {
+ self.arg(arg);
+ }
+
+ fn env<T, U>(&mut self, key: T, val: U)
+ where
+ T: AsRef<OsStr>,
+ U: AsRef<OsStr>,
+ {
+ self.env(key, val);
+ }
+ }
+
+ // Note that this function can panic, due to process creation,
+ // avoiding this panic would require a mechanism for dealing
+ // with low-resource scenarios.
+ let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
+ .expect("Failed to create IPC one-shot server.");
+
+ // If there is a sandbox, use the `gaol` API to create the child process.
+ if content.opts().sandbox {
+ let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
+ setup_common(&mut command, token);
+
+ let profile = content_process_sandbox_profile();
+ let _ = Sandbox::new(profile)
+ .start(&mut command)
+ .expect("Failed to start sandboxed child process!");
+ } else {
+ let path_to_self = env::current_exe().expect("Failed to get current executor.");
+ let mut child_process = process::Command::new(path_to_self);
+ setup_common(&mut child_process, token);
+ let _ = child_process
+ .spawn()
+ .expect("Failed to start unsandboxed child process!");
+ }
+
+ let (_receiver, sender) = server.accept().expect("Server failed to accept.");
+ sender.send(content)?;
+
+ Ok(())
+}
+
+#[cfg(any(target_os = "windows", target_os = "ios"))]
+pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<(), Error> {
+ error!("Multiprocess is not supported on Windows or iOS.");
+ process::exit(1);
+}
+
+#[cfg(not(windows))]
+fn setup_common<C: CommandMethods>(command: &mut C, token: String) {
+ C::arg(command, "--content-process");
+ C::arg(command, token);
+
+ if let Ok(value) = env::var("RUST_BACKTRACE") {
+ C::env(command, "RUST_BACKTRACE", value);
+ }
+
+ if let Ok(value) = env::var("RUST_LOG") {
+ C::env(command, "RUST_LOG", value);
+ }
+}
+
+/// A trait to unify commands launched as multiprocess with or without a sandbox.
+trait CommandMethods {
+ /// A command line argument.
+ fn arg<T>(&mut self, arg: T)
+ where
+ T: AsRef<OsStr>;
+
+ /// An environment variable.
+ fn env<T, U>(&mut self, key: T, val: U)
+ where
+ T: AsRef<OsStr>,
+ U: AsRef<OsStr>;
+}
+
+impl CommandMethods for process::Command {
+ fn arg<T>(&mut self, arg: T)
+ where
+ T: AsRef<OsStr>,
+ {
+ self.arg(arg);
+ }
+
+ fn env<T, U>(&mut self, key: T, val: U)
+ where
+ T: AsRef<OsStr>,
+ U: AsRef<OsStr>,
+ {
+ self.env(key, val);
+ }
+}
diff --git a/components/constellation/serviceworker.rs b/components/constellation/serviceworker.rs
new file mode 100644
index 00000000000..95bb8571b3a
--- /dev/null
+++ b/components/constellation/serviceworker.rs
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 crate::sandboxing::{spawn_multiprocess, UnprivilegedContent};
+use ipc_channel::Error;
+use script_traits::{SWManagerSenders, ServiceWorkerManagerFactory};
+use servo_config::opts::{self, Opts};
+use servo_config::prefs::{self, PrefValue};
+use servo_url::ImmutableOrigin;
+use std::collections::HashMap;
+
+/// Conceptually, this is glue to start an agent-cluster for a service worker agent.
+/// <https://html.spec.whatwg.org/multipage/#obtain-a-service-worker-agent>
+#[derive(Deserialize, Serialize)]
+pub struct ServiceWorkerUnprivilegedContent {
+ opts: Opts,
+ prefs: HashMap<String, PrefValue>,
+ senders: SWManagerSenders,
+ origin: ImmutableOrigin,
+}
+
+impl ServiceWorkerUnprivilegedContent {
+ pub fn new(
+ senders: SWManagerSenders,
+ origin: ImmutableOrigin,
+ ) -> ServiceWorkerUnprivilegedContent {
+ ServiceWorkerUnprivilegedContent {
+ opts: (*opts::get()).clone(),
+ prefs: prefs::pref_map().iter().collect(),
+ senders,
+ origin,
+ }
+ }
+
+ /// Start the agent-cluster.
+ pub fn start<SWF>(self)
+ where
+ SWF: ServiceWorkerManagerFactory,
+ {
+ SWF::create(self.senders, self.origin);
+ }
+
+ /// Start the agent-cluster in it's own process.
+ pub fn spawn_multiprocess(self) -> Result<(), Error> {
+ spawn_multiprocess(UnprivilegedContent::ServiceWorker(self))
+ }
+
+ pub fn opts(&self) -> Opts {
+ self.opts.clone()
+ }
+
+ pub fn prefs(&self) -> HashMap<String, PrefValue> {
+ self.prefs.clone()
+ }
+}
diff --git a/components/script/init.rs b/components/script/init.rs
index 30e01dda74e..823f045fad4 100644
--- a/components/script/init.rs
+++ b/components/script/init.rs
@@ -5,8 +5,6 @@
use crate::dom::bindings::codegen::RegisterBindings;
use crate::dom::bindings::proxyhandler;
use crate::script_runtime::JSEngineSetup;
-use crate::serviceworker_manager::ServiceWorkerManager;
-use script_traits::SWManagerSenders;
#[cfg(target_os = "linux")]
#[allow(unsafe_code)]
@@ -51,11 +49,6 @@ fn perform_platform_specific_initialization() {
#[cfg(not(target_os = "linux"))]
fn perform_platform_specific_initialization() {}
-pub fn init_service_workers(sw_senders: SWManagerSenders) {
- // Spawn the service worker manager passing the constellation sender
- ServiceWorkerManager::spawn_manager(sw_senders);
-}
-
#[allow(unsafe_code)]
pub fn init() -> JSEngineSetup {
unsafe {
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 13791748931..07090f4716b 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -91,7 +91,7 @@ pub mod script_runtime;
#[allow(unsafe_code)]
pub mod script_thread;
#[warn(deprecated)]
-mod serviceworker_manager;
+pub mod serviceworker_manager;
#[warn(deprecated)]
mod serviceworkerjob;
#[warn(deprecated)]
@@ -115,7 +115,7 @@ mod unpremultiplytable;
#[warn(deprecated)]
mod webdriver_handlers;
-pub use init::{init, init_service_workers};
+pub use init::init;
pub use script_runtime::JSEngineSetup;
/// A module with everything layout can use from script.
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 109628ae94e..66d4d995d32 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -15,8 +15,12 @@ use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use net_traits::{CoreResourceMsg, CustomResponseMediator};
-use script_traits::{DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg};
+use script_traits::{
+ DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerManagerFactory,
+ ServiceWorkerMsg,
+};
use servo_config::pref;
+use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;
use std::collections::HashMap;
use std::thread;
@@ -31,6 +35,9 @@ pub struct ServiceWorkerManager {
registered_workers: HashMap<ServoUrl, ScopeThings>,
// map of active service worker descriptors
active_workers: HashMap<ServoUrl, Sender<ServiceWorkerScriptMsg>>,
+ // Will be useful to implement posting a message to a client.
+ // See https://github.com/servo/servo/issues/24660
+ _constellation_sender: IpcSender<SWManagerMsg>,
// own sender to send messages here
own_sender: IpcSender<ServiceWorkerMsg>,
// receiver to receive messages from constellation
@@ -44,6 +51,7 @@ impl ServiceWorkerManager {
own_sender: IpcSender<ServiceWorkerMsg>,
from_constellation_receiver: Receiver<ServiceWorkerMsg>,
resource_port: Receiver<CustomResponseMediator>,
+ constellation_sender: IpcSender<SWManagerMsg>,
) -> ServiceWorkerManager {
ServiceWorkerManager {
registered_workers: HashMap::new(),
@@ -51,30 +59,10 @@ impl ServiceWorkerManager {
own_sender: own_sender,
own_port: from_constellation_receiver,
resource_receiver: resource_port,
+ _constellation_sender: constellation_sender,
}
}
- pub fn spawn_manager(sw_senders: SWManagerSenders) {
- let (own_sender, from_constellation_receiver) = ipc::channel().unwrap();
- let (resource_chan, resource_port) = ipc::channel().unwrap();
- let from_constellation =
- ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(from_constellation_receiver);
- let resource_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(resource_port);
- let _ = sw_senders
- .resource_sender
- .send(CoreResourceMsg::NetworkMediator(resource_chan));
- let _ = sw_senders
- .swmanager_sender
- .send(SWManagerMsg::OwnSender(own_sender.clone()));
- thread::Builder::new()
- .name("ServiceWorkerManager".to_owned())
- .spawn(move || {
- ServiceWorkerManager::new(own_sender, from_constellation, resource_port)
- .handle_message();
- })
- .expect("Thread spawning failed");
- }
-
pub fn get_matching_scope(&self, load_url: &ServoUrl) -> Option<ServoUrl> {
for scope in self.registered_workers.keys() {
if longest_prefix_match(&scope, load_url) {
@@ -203,6 +191,38 @@ impl ServiceWorkerManager {
}
}
+impl ServiceWorkerManagerFactory for ServiceWorkerManager {
+ fn create(sw_senders: SWManagerSenders, _origin: ImmutableOrigin) {
+ let (resource_chan, resource_port) = ipc::channel().unwrap();
+
+ let SWManagerSenders {
+ resource_sender,
+ own_sender,
+ receiver,
+ swmanager_sender: constellation_sender,
+ } = sw_senders;
+
+ let from_constellation = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(receiver);
+ let resource_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(resource_port);
+ let _ = resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan));
+ if thread::Builder::new()
+ .name("ServiceWorkerManager".to_owned())
+ .spawn(move || {
+ ServiceWorkerManager::new(
+ own_sender,
+ from_constellation,
+ resource_port,
+ constellation_sender,
+ )
+ .handle_message();
+ })
+ .is_err()
+ {
+ warn!("ServiceWorkerManager thread spawning failed");
+ }
+ }
+}
+
pub fn serviceworker_enabled() -> bool {
pref!(dom.serviceworker.enabled)
}
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 9b4588a3cb0..b41cc44d678 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -706,6 +706,13 @@ pub trait ScriptThreadFactory {
) -> (Sender<Self::Message>, Receiver<Self::Message>);
}
+/// This trait allows creating a `ServiceWorkerManager` without depending on the `script`
+/// crate.
+pub trait ServiceWorkerManagerFactory {
+ /// Create a `ServiceWorkerManager`.
+ fn create(sw_senders: SWManagerSenders, origin: ImmutableOrigin);
+}
+
/// Whether the sandbox attribute is present for an iframe element
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum IFrameSandboxState {
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index 918b4f4ff78..245a02ec6d2 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -370,11 +370,16 @@ pub struct DOMMessage {
}
/// Channels to allow service worker manager to communicate with constellation and resource thread
+#[derive(Deserialize, Serialize)]
pub struct SWManagerSenders {
- /// sender for communicating with constellation
+ /// Sender of messages to the constellation.
pub swmanager_sender: IpcSender<SWManagerMsg>,
- /// sender for communicating with resource thread
+ /// Sender for communicating with resource thread.
pub resource_sender: IpcSender<CoreResourceMsg>,
+ /// Sender of messages to the manager.
+ pub own_sender: IpcSender<ServiceWorkerMsg>,
+ /// Receiver of messages from the constellation.
+ pub receiver: IpcReceiver<ServiceWorkerMsg>,
}
/// Messages sent to Service Worker Manager thread
@@ -393,6 +398,8 @@ pub enum ServiceWorkerMsg {
/// Messages outgoing from the Service Worker Manager thread to constellation
#[derive(Debug, Deserialize, Serialize)]
pub enum SWManagerMsg {
- /// Provide the constellation with a means of communicating with the Service Worker Manager
- OwnSender(IpcSender<ServiceWorkerMsg>),
+ /// Placeholder to keep the enum,
+ /// as it will be needed when implementing
+ /// https://github.com/servo/servo/issues/24660
+ PostMessageToClient,
}
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index 5c54f654881..dff8c842e4f 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -80,7 +80,7 @@ use compositing::{CompositingReason, ConstellationMsg, IOCompositor, ShutdownSta
not(target_arch = "aarch64")
))]
use constellation::content_process_sandbox_profile;
-use constellation::{Constellation, InitialConstellationState, UnprivilegedPipelineContent};
+use constellation::{Constellation, InitialConstellationState, UnprivilegedContent};
use constellation::{FromCompositorLogger, FromScriptLogger};
use crossbeam_channel::{unbounded, Sender};
use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker};
@@ -105,8 +105,9 @@ use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
+use script::serviceworker_manager::ServiceWorkerManager;
use script::JSEngineSetup;
-use script_traits::{SWManagerSenders, ScriptToConstellationChan, WindowSizeData};
+use script_traits::{ScriptToConstellationChan, WindowSizeData};
use servo_config::opts;
use servo_config::{pref, prefs};
use servo_media::player::context::GlContext;
@@ -519,7 +520,7 @@ where
// Create the constellation, which maintains the engine
// pipelines, including the script and layout threads, as well
// as the navigation context.
- let (constellation_chan, sw_senders) = create_constellation(
+ let constellation_chan = create_constellation(
opts.user_agent.clone(),
opts.config_dir.clone(),
embedder_proxy,
@@ -541,9 +542,6 @@ where
pending_wr_frame.clone(),
);
- // Send the constellation's swmanager sender to service worker manager thread
- script::init_service_workers(sw_senders);
-
if cfg!(feature = "webdriver") {
if let Some(port) = opts.webdriver_port {
webdriver(port, constellation_chan.clone());
@@ -879,7 +877,7 @@ fn create_constellation(
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
initial_window_size: WindowSizeData,
pending_wr_frame: Arc<AtomicBool>,
-) -> (Sender<ConstellationMsg>, SWManagerSenders) {
+) -> Sender<ConstellationMsg> {
// Global configuration options, parsed from the command line.
let opts = opts::get();
@@ -900,8 +898,6 @@ fn create_constellation(
webrender_api_sender.create_api(),
);
- let resource_sender = public_resource_threads.sender();
-
let initial_state = InitialConstellationState {
compositor_proxy,
embedder_proxy,
@@ -926,10 +922,11 @@ fn create_constellation(
let (canvas_chan, ipc_canvas_chan) = canvas::canvas_paint_thread::CanvasPaintThread::start();
- let (constellation_chan, from_swmanager_sender) = Constellation::<
+ let constellation_chan = Constellation::<
script_layout_interface::message::Msg,
layout_thread::LayoutThread,
script::script_thread::ScriptThread,
+ script::serviceworker_manager::ServiceWorkerManager,
>::start(
initial_state,
initial_window_size,
@@ -949,13 +946,7 @@ fn create_constellation(
.unwrap();
}
- // channels to communicate with Service Worker Manager
- let sw_senders = SWManagerSenders {
- swmanager_sender: from_swmanager_sender,
- resource_sender: resource_sender,
- };
-
- (constellation_chan, sw_senders)
+ constellation_chan
}
// A logger that logs to two downstream loggers.
@@ -997,45 +988,50 @@ pub fn set_logger(script_to_constellation_chan: ScriptToConstellationChan) {
/// Content process entry point.
pub fn run_content_process(token: String) {
let (unprivileged_content_sender, unprivileged_content_receiver) =
- ipc::channel::<UnprivilegedPipelineContent>().unwrap();
- let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
+ ipc::channel::<UnprivilegedContent>().unwrap();
+ let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
IpcSender::connect(token).unwrap();
connection_bootstrap
.send(unprivileged_content_sender)
.unwrap();
- let mut unprivileged_content = unprivileged_content_receiver.recv().unwrap();
+ let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
opts::set_options(unprivileged_content.opts());
prefs::pref_map()
.set_all(unprivileged_content.prefs())
.expect("Failed to set preferences");
- set_logger(unprivileged_content.script_to_constellation_chan().clone());
// Enter the sandbox if necessary.
if opts::get().sandbox {
create_sandbox();
}
- let background_hang_monitor_register = if opts::get().background_hang_monitor {
- unprivileged_content.register_with_background_hang_monitor()
- } else {
- None
- };
-
- // send the required channels to the service worker manager
- let sw_senders = unprivileged_content.swmanager_senders();
let _js_engine_setup = script::init();
- script::init_service_workers(sw_senders);
- media_platform::init();
+ match unprivileged_content {
+ UnprivilegedContent::Pipeline(mut content) => {
+ media_platform::init();
+
+ set_logger(content.script_to_constellation_chan().clone());
- unprivileged_content.start_all::<script_layout_interface::message::Msg,
- layout_thread::LayoutThread,
- script::script_thread::ScriptThread>(
- true,
- background_hang_monitor_register,
- None,
- );
+ let background_hang_monitor_register = if opts::get().background_hang_monitor {
+ content.register_with_background_hang_monitor()
+ } else {
+ None
+ };
+
+ content.start_all::<script_layout_interface::message::Msg,
+ layout_thread::LayoutThread,
+ script::script_thread::ScriptThread>(
+ true,
+ background_hang_monitor_register,
+ None,
+ );
+ },
+ UnprivilegedContent::ServiceWorker(content) => {
+ content.start::<ServiceWorkerManager>();
+ },
+ }
}
#[cfg(all(