Skip to content

Commit

Permalink
allow for a service worker manager per origin
Browse files Browse the repository at this point in the history
  • Loading branch information
gterzian committed Apr 5, 2020
1 parent 9972aee commit db217d5
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 245 deletions.
70 changes: 48 additions & 22 deletions components/constellation/constellation.rs
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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(
Expand All @@ -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())
Expand Down Expand Up @@ -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,
Expand All @@ -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(),
Expand Down Expand Up @@ -1022,7 +1027,7 @@ where
})
.expect("Thread spawning failed");

(compositor_sender, swmanager_sender)
compositor_sender
}

/// The main event loop for the constellation.
Expand Down Expand Up @@ -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
},
}
}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion components/constellation/lib.rs
Expand Up @@ -24,6 +24,7 @@ mod pipeline;
not(target_arch = "aarch64")
))]
mod sandboxing;
mod serviceworker;
mod session_history;
mod timer_scheduler;

Expand All @@ -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};
150 changes: 4 additions & 146 deletions components/constellation/pipeline.rs
Expand Up @@ -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;
Expand All @@ -27,24 +28,20 @@ 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::{
AnimationState, ConstellationControlMsg, DiscardBrowsingContext, ScriptToConstellationChan,
};
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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
}
}

0 comments on commit db217d5

Please sign in to comment.