Skip to content

Commit 36520f5

Browse files
authored
Auto merge of #26087 - gterzian:allow_service_workers_in_multiprocess, r=<try>
[WIP] Fix ServiceWorker in multiprocess <!-- Please describe your changes on the following line: --> FIX #15217 FIX #26100 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
2 parents f7d3d4a + ad1f274 commit 36520f5

13 files changed

Lines changed: 377 additions & 248 deletions

File tree

components/constellation/constellation.rs

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ use crate::browsingcontext::{
9696
use crate::event_loop::EventLoop;
9797
use crate::network_listener::NetworkListener;
9898
use crate::pipeline::{InitialPipelineState, Pipeline};
99+
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
99100
use crate::session_history::{
100101
JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
101102
};
@@ -151,10 +152,15 @@ use script_traits::{HistoryEntryReplacement, IFrameSizeMsg, WindowSizeData, Wind
151152
use script_traits::{
152153
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
153154
};
154-
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
155+
use script_traits::{
156+
LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
157+
ServiceWorkerManagerFactory,
158+
};
155159
use script_traits::{MediaSessionActionType, MouseEventType};
156160
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
157-
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg};
161+
use script_traits::{
162+
SWManagerMsg, SWManagerSenders, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg,
163+
};
158164
use serde::{Deserialize, Serialize};
159165
use servo_config::{opts, pref};
160166
use servo_rand::{random, Rng, ServoRng, SliceRandom};
@@ -259,7 +265,7 @@ struct BrowsingContextGroup {
259265
/// `LayoutThread` in the `layout` crate, and `ScriptThread` in
260266
/// the `script` crate). Script and layout communicate using a `Message`
261267
/// type.
262-
pub struct Constellation<Message, LTF, STF> {
268+
pub struct Constellation<Message, LTF, STF, SWF> {
263269
/// An ipc-sender/threaded-receiver pair
264270
/// to facilitate installing pipeline namespaces in threads
265271
/// via a per-process installer.
@@ -348,9 +354,8 @@ pub struct Constellation<Message, LTF, STF> {
348354
/// bluetooth thread.
349355
bluetooth_thread: IpcSender<BluetoothRequest>,
350356

351-
/// An IPC channel for the constellation to send messages to the
352-
/// Service Worker Manager thread.
353-
swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
357+
/// A map of origin to sender to a Service worker manager.
358+
sw_managers: HashMap<ImmutableOrigin, IpcSender<ServiceWorkerMsg>>,
354359

355360
/// An IPC channel for Service Worker Manager threads to send
356361
/// messages to the constellation. This is the SW Manager thread's
@@ -453,7 +458,7 @@ pub struct Constellation<Message, LTF, STF> {
453458
random_pipeline_closure: Option<(ServoRng, f32)>,
454459

455460
/// Phantom data that keeps the Rust type system happy.
456-
phantom: PhantomData<(Message, LTF, STF)>,
461+
phantom: PhantomData<(Message, LTF, STF, SWF)>,
457462

458463
/// Entry point to create and get channels to a WebGLThread.
459464
webgl_threads: Option<WebGLThreads>,
@@ -813,10 +818,11 @@ fn handle_webrender_message(
813818
}
814819
}
815820

816-
impl<Message, LTF, STF> Constellation<Message, LTF, STF>
821+
impl<Message, LTF, STF, SWF> Constellation<Message, LTF, STF, SWF>
817822
where
818823
LTF: LayoutThreadFactory<Message = Message>,
819824
STF: ScriptThreadFactory<Message = Message>,
825+
SWF: ServiceWorkerManagerFactory,
820826
{
821827
/// Create a new constellation thread.
822828
pub fn start(
@@ -829,12 +835,11 @@ where
829835
enable_canvas_antialiasing: bool,
830836
canvas_chan: Sender<ConstellationCanvasMsg>,
831837
ipc_canvas_chan: IpcSender<CanvasMsg>,
832-
) -> (Sender<FromCompositorMsg>, IpcSender<SWManagerMsg>) {
838+
) -> Sender<FromCompositorMsg> {
833839
let (compositor_sender, compositor_receiver) = unbounded();
834840

835841
// service worker manager to communicate with constellation
836842
let (swmanager_sender, swmanager_receiver) = ipc::channel().expect("ipc channel failure");
837-
let sw_mgr_clone = swmanager_sender.clone();
838843

839844
thread::Builder::new()
840845
.name("Constellation".to_owned())
@@ -937,7 +942,7 @@ where
937942
}),
938943
);
939944

940-
let mut constellation: Constellation<Message, LTF, STF> = Constellation {
945+
let mut constellation: Constellation<Message, LTF, STF, SWF> = Constellation {
941946
namespace_receiver,
942947
namespace_sender,
943948
script_sender: ipc_script_sender,
@@ -961,9 +966,9 @@ where
961966
public_resource_threads: state.public_resource_threads,
962967
private_resource_threads: state.private_resource_threads,
963968
font_cache_thread: state.font_cache_thread,
964-
swmanager_chan: None,
969+
sw_managers: Default::default(),
965970
swmanager_receiver: swmanager_receiver,
966-
swmanager_sender: sw_mgr_clone,
971+
swmanager_sender,
967972
browsing_context_group_set: Default::default(),
968973
browsing_context_group_next_id: Default::default(),
969974
message_ports: HashMap::new(),
@@ -1022,7 +1027,7 @@ where
10221027
})
10231028
.expect("Thread spawning failed");
10241029

1025-
(compositor_sender, swmanager_sender)
1030+
compositor_sender
10261031
}
10271032

10281033
/// The main event loop for the constellation.
@@ -1530,9 +1535,8 @@ where
15301535

15311536
fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
15321537
match message {
1533-
SWManagerMsg::OwnSender(sw_sender) => {
1534-
// store service worker manager for communicating with it.
1535-
self.swmanager_chan = Some(sw_sender);
1538+
SWManagerMsg::PostMessageToClient => {
1539+
// TODO: implement posting a message to a SW client.
15361540
},
15371541
}
15381542
}
@@ -1965,7 +1969,7 @@ where
19651969
self.handle_register_serviceworker(scope_things, scope);
19661970
},
19671971
FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
1968-
if let Some(ref mgr) = self.swmanager_chan {
1972+
if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
19691973
let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
19701974
} else {
19711975
warn!("Unable to forward DOMMessage for postMessage call");
@@ -2621,11 +2625,32 @@ where
26212625
}
26222626
}
26232627

2624-
fn handle_register_serviceworker(&self, scope_things: ScopeThings, scope: ServoUrl) {
2625-
if let Some(ref mgr) = self.swmanager_chan {
2628+
fn handle_register_serviceworker(&mut self, scope_things: ScopeThings, scope: ServoUrl) {
2629+
let origin = scope.origin();
2630+
2631+
if let Some(mgr) = self.sw_managers.get(&origin) {
26262632
let _ = mgr.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
26272633
} else {
2628-
warn!("sending scope info to service worker manager failed");
2634+
let (own_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
2635+
2636+
let sw_senders = SWManagerSenders {
2637+
swmanager_sender: self.swmanager_sender.clone(),
2638+
resource_sender: self.public_resource_threads.sender(),
2639+
own_sender: own_sender.clone(),
2640+
receiver,
2641+
};
2642+
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin.clone());
2643+
2644+
if opts::multiprocess() {
2645+
if content.spawn_multiprocess().is_err() {
2646+
return warn!("Failed to spawn process for SW manager.");
2647+
}
2648+
} else {
2649+
content.start::<SWF>();
2650+
}
2651+
2652+
let _ = own_sender.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
2653+
self.sw_managers.insert(origin, own_sender);
26292654
}
26302655
}
26312656

@@ -2763,7 +2788,7 @@ where
27632788
}
27642789

27652790
debug!("Exiting service worker manager thread.");
2766-
if let Some(mgr) = self.swmanager_chan.as_ref() {
2791+
for (_, mgr) in self.sw_managers.drain() {
27672792
if let Err(e) = mgr.send(ServiceWorkerMsg::Exit) {
27682793
warn!("Exit service worker manager failed ({})", e);
27692794
}

components/constellation/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod pipeline;
2424
not(target_arch = "aarch64")
2525
))]
2626
mod sandboxing;
27+
mod serviceworker;
2728
mod session_history;
2829
mod timer_scheduler;
2930

@@ -38,4 +39,4 @@ pub use crate::pipeline::UnprivilegedPipelineContent;
3839
not(target_arch = "arm"),
3940
not(target_arch = "aarch64")
4041
))]
41-
pub use crate::sandboxing::content_process_sandbox_profile;
42+
pub use crate::sandboxing::{content_process_sandbox_profile, UnprivilegedContent};

components/constellation/pipeline.rs

Lines changed: 4 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

55
use crate::event_loop::EventLoop;
6+
use crate::sandboxing::{spawn_multiprocess, UnprivilegedContent};
67
use background_hang_monitor::HangMonitorRegister;
78
use bluetooth_traits::BluetoothRequest;
89
use canvas_traits::webgl::WebGLPipeline;
@@ -27,24 +28,20 @@ use msg::constellation_msg::{
2728
};
2829
use net::image_cache::ImageCacheImpl;
2930
use net_traits::image_cache::ImageCache;
30-
use net_traits::{IpcSend, ResourceThreads};
31+
use net_traits::ResourceThreads;
3132
use profile_traits::mem as profile_mem;
3233
use profile_traits::time;
3334
use script_traits::{
3435
AnimationState, ConstellationControlMsg, DiscardBrowsingContext, ScriptToConstellationChan,
3536
};
3637
use script_traits::{DocumentActivity, InitialScriptState};
3738
use script_traits::{LayoutControlMsg, LayoutMsg, LoadData};
38-
use script_traits::{NewLayoutInfo, SWManagerMsg, SWManagerSenders};
39+
use script_traits::{NewLayoutInfo, SWManagerMsg};
3940
use script_traits::{ScriptThreadFactory, TimerSchedulerMsg, WindowSizeData};
4041
use servo_config::opts::{self, Opts};
4142
use servo_config::{prefs, prefs::PrefValue};
4243
use servo_url::ServoUrl;
4344
use std::collections::{HashMap, HashSet};
44-
#[cfg(not(windows))]
45-
use std::env;
46-
use std::ffi::OsStr;
47-
use std::process;
4845
use std::rc::Rc;
4946
use std::sync::atomic::AtomicBool;
5047
use std::sync::Arc;
@@ -632,109 +629,8 @@ impl UnprivilegedPipelineContent {
632629
}
633630
}
634631

635-
#[cfg(any(
636-
target_os = "android",
637-
target_arch = "arm",
638-
all(target_arch = "aarch64", not(target_os = "windows"))
639-
))]
640632
pub fn spawn_multiprocess(self) -> Result<(), Error> {
641-
use ipc_channel::ipc::IpcOneShotServer;
642-
// Note that this function can panic, due to process creation,
643-
// avoiding this panic would require a mechanism for dealing
644-
// with low-resource scenarios.
645-
let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new()
646-
.expect("Failed to create IPC one-shot server.");
647-
648-
let path_to_self = env::current_exe().expect("Failed to get current executor.");
649-
let mut child_process = process::Command::new(path_to_self);
650-
self.setup_common(&mut child_process, token);
651-
let _ = child_process
652-
.spawn()
653-
.expect("Failed to start unsandboxed child process!");
654-
655-
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
656-
sender.send(self)?;
657-
658-
Ok(())
659-
}
660-
661-
#[cfg(all(
662-
not(target_os = "windows"),
663-
not(target_os = "ios"),
664-
not(target_os = "android"),
665-
not(target_arch = "arm"),
666-
not(target_arch = "aarch64")
667-
))]
668-
pub fn spawn_multiprocess(self) -> Result<(), Error> {
669-
use crate::sandboxing::content_process_sandbox_profile;
670-
use gaol::sandbox::{self, Sandbox, SandboxMethods};
671-
use ipc_channel::ipc::IpcOneShotServer;
672-
673-
impl CommandMethods for sandbox::Command {
674-
fn arg<T>(&mut self, arg: T)
675-
where
676-
T: AsRef<OsStr>,
677-
{
678-
self.arg(arg);
679-
}
680-
681-
fn env<T, U>(&mut self, key: T, val: U)
682-
where
683-
T: AsRef<OsStr>,
684-
U: AsRef<OsStr>,
685-
{
686-
self.env(key, val);
687-
}
688-
}
689-
690-
// Note that this function can panic, due to process creation,
691-
// avoiding this panic would require a mechanism for dealing
692-
// with low-resource scenarios.
693-
let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedPipelineContent>>::new()
694-
.expect("Failed to create IPC one-shot server.");
695-
696-
// If there is a sandbox, use the `gaol` API to create the child process.
697-
if self.opts.sandbox {
698-
let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
699-
self.setup_common(&mut command, token);
700-
701-
let profile = content_process_sandbox_profile();
702-
let _ = Sandbox::new(profile)
703-
.start(&mut command)
704-
.expect("Failed to start sandboxed child process!");
705-
} else {
706-
let path_to_self = env::current_exe().expect("Failed to get current executor.");
707-
let mut child_process = process::Command::new(path_to_self);
708-
self.setup_common(&mut child_process, token);
709-
let _ = child_process
710-
.spawn()
711-
.expect("Failed to start unsandboxed child process!");
712-
}
713-
714-
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
715-
sender.send(self)?;
716-
717-
Ok(())
718-
}
719-
720-
#[cfg(any(target_os = "windows", target_os = "ios"))]
721-
pub fn spawn_multiprocess(self) -> Result<(), Error> {
722-
error!("Multiprocess is not supported on Windows or iOS.");
723-
process::exit(1);
724-
}
725-
726-
#[cfg(not(windows))]
727-
fn setup_common<C: CommandMethods>(&self, command: &mut C, token: String) {
728-
C::arg(command, "--content-process");
729-
C::arg(command, token);
730-
731-
if let Ok(value) = env::var("RUST_BACKTRACE") {
732-
C::env(command, "RUST_BACKTRACE", value);
733-
}
734-
735-
if let Ok(value) = env::var("RUST_LOG") {
736-
C::env(command, "RUST_LOG", value);
737-
}
633+
spawn_multiprocess(UnprivilegedContent::Pipeline(self))
738634
}
739635

740636
pub fn register_with_background_hang_monitor(
@@ -763,42 +659,4 @@ impl UnprivilegedPipelineContent {
763659
pub fn prefs(&self) -> HashMap<String, PrefValue> {
764660
self.prefs.clone()
765661
}
766-
767-
pub fn swmanager_senders(&self) -> SWManagerSenders {
768-
SWManagerSenders {
769-
swmanager_sender: self.swmanager_thread.clone(),
770-
resource_sender: self.resource_threads.sender(),
771-
}
772-
}
773-
}
774-
775-
/// A trait to unify commands launched as multiprocess with or without a sandbox.
776-
trait CommandMethods {
777-
/// A command line argument.
778-
fn arg<T>(&mut self, arg: T)
779-
where
780-
T: AsRef<OsStr>;
781-
782-
/// An environment variable.
783-
fn env<T, U>(&mut self, key: T, val: U)
784-
where
785-
T: AsRef<OsStr>,
786-
U: AsRef<OsStr>;
787-
}
788-
789-
impl CommandMethods for process::Command {
790-
fn arg<T>(&mut self, arg: T)
791-
where
792-
T: AsRef<OsStr>,
793-
{
794-
self.arg(arg);
795-
}
796-
797-
fn env<T, U>(&mut self, key: T, val: U)
798-
where
799-
T: AsRef<OsStr>,
800-
U: AsRef<OsStr>,
801-
{
802-
self.env(key, val);
803-
}
804662
}

0 commit comments

Comments
 (0)