Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ServiceWorker in multiprocess #26087

Merged
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

allow for a service worker manager per origin

  • Loading branch information
gterzian committed Apr 5, 2020
commit db217d557513030836898f840206d5ac9ed87b79
@@ -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);
}
@@ -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};
@@ -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,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;
@@ -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);
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.