diff --git a/servo/components/compositing/Cargo.toml b/servo/components/compositing/Cargo.toml index 95ddbc9cfc78..ce452ad8b7d1 100644 --- a/servo/components/compositing/Cargo.toml +++ b/servo/components/compositing/Cargo.toml @@ -68,14 +68,20 @@ features = ["texture_surface"] version = "0.2" features = [ "serde_serialization" ] +[dependencies.gaol] +git = "https://github.com/pcwalton/gaol" + [dependencies] app_units = {version = "0.1", features = ["plugins"]} image = "0.4.0" +libc = "0.1" log = "0.3" num = "0.1.24" time = "0.1.17" gleam = "0.1" euclid = {version = "0.3", features = ["plugins"]} +serde = "0.6" +serde_macros = "0.6" [target.x86_64-apple-darwin.dependencies] core-graphics = "0.1" diff --git a/servo/components/compositing/compositor.rs b/servo/components/compositing/compositor.rs index c251426dc489..e91bc9510866 100644 --- a/servo/components/compositing/compositor.rs +++ b/servo/components/compositing/compositor.rs @@ -17,7 +17,7 @@ use gfx_traits::color; use gleam::gl; use gleam::gl::types::{GLint, GLsizei}; use image::{DynamicImage, ImageFormat, RgbImage}; -use ipc_channel::ipc::{self, IpcSharedMemory}; +use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; use ipc_channel::router::ROUTER; use layers::geometry::{DevicePixel, LayerPixel}; use layers::layers::{BufferRequest, Layer, LayerBuffer, LayerBufferSet}; @@ -30,7 +30,7 @@ use msg::compositor_msg::{Epoch, EventResult, FrameTreeId, LayerId, LayerKind}; use msg::compositor_msg::{LayerProperties, ScrollPolicy}; use msg::constellation_msg::CompositorMsg as ConstellationMsg; use msg::constellation_msg::{AnimationState, Image, PixelFormat}; -use msg::constellation_msg::{ConstellationChan, Key, KeyModifiers, KeyState, LoadData}; +use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData}; use pipeline::CompositionPipeline; use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest}; @@ -168,7 +168,7 @@ pub struct IOCompositor { frame_tree_id: FrameTreeId, /// The channel on which messages can be sent to the constellation. - constellation_chan: ConstellationChan, + constellation_chan: Sender, /// The channel on which messages can be sent to the time profiler. time_profiler_chan: time::ProfilerChan, @@ -385,8 +385,7 @@ impl IOCompositor { pub fn start_shutting_down(&mut self) { debug!("Compositor sending Exit message to Constellation"); - let ConstellationChan(ref constellation_channel) = self.constellation_chan; - constellation_channel.send(ConstellationMsg::Exit).unwrap(); + self.constellation_chan.send(ConstellationMsg::Exit).unwrap(); self.mem_profiler_chan.send(mem::ProfilerMsg::UnregisterReporter(reporter_name())); @@ -702,8 +701,8 @@ impl IOCompositor { fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree, - response_chan: Sender<()>, - new_constellation_chan: ConstellationChan) { + response_chan: IpcSender<()>, + new_constellation_chan: Sender) { response_chan.send(()).unwrap(); // There are now no more pending iframes. @@ -943,8 +942,7 @@ impl IOCompositor { let initial_viewport = self.window_size.as_f32() / dppx; let visible_viewport = initial_viewport / self.viewport_zoom; - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { + self.constellation_chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { device_pixel_ratio: dppx, initial_viewport: initial_viewport, visible_viewport: visible_viewport, @@ -959,9 +957,9 @@ impl IOCompositor { None => return, }; - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::FrameSize(*subpage_pipeline_id, - layer_properties.rect.size)).unwrap(); + self.constellation_chan.send(ConstellationMsg::FrameSize( + *subpage_pipeline_id, + layer_properties.rect.size)).unwrap(); } pub fn move_layer(&self, @@ -1168,8 +1166,7 @@ impl IOCompositor { None => ConstellationMsg::InitLoadUrl(url) }; - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(msg).unwrap() + self.constellation_chan.send(msg).unwrap() } fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) { @@ -1446,7 +1443,7 @@ impl IOCompositor { } fn tick_animations_for_pipeline(&self, pipeline_id: PipelineId) { - self.constellation_chan.0.send(ConstellationMsg::TickAnimation(pipeline_id)).unwrap() + self.constellation_chan.send(ConstellationMsg::TickAnimation(pipeline_id)).unwrap() } fn constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) { @@ -1538,13 +1535,11 @@ impl IOCompositor { windowing::WindowNavigateMsg::Forward => NavigationDirection::Forward, windowing::WindowNavigateMsg::Back => NavigationDirection::Back, }; - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::Navigate(None, direction)).unwrap() + self.constellation_chan.send(ConstellationMsg::Navigate(None, direction)).unwrap() } fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::KeyEvent(key, state, modifiers)).unwrap() + self.constellation_chan.send(ConstellationMsg::KeyEvent(key, state, modifiers)).unwrap() } fn fill_paint_request_with_cached_layer_buffers(&mut self, paint_request: &mut PaintRequest) { @@ -1744,8 +1739,7 @@ impl IOCompositor { // Pass the pipeline/epoch states to the constellation and check // if it's safe to output the image. - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::IsReadyToSaveImage(pipeline_epochs)).unwrap(); + self.constellation_chan.send(ConstellationMsg::IsReadyToSaveImage(pipeline_epochs)).unwrap(); self.ready_to_save_state = ReadyState::WaitingForConstellationReply; Err(NotReadyToPaint::JustNotifiedConstellation) } @@ -2167,8 +2161,7 @@ impl CompositorEventListener for IOCompositor where Window: Wind None => return, Some(ref root_pipeline) => root_pipeline.id, }; - let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ConstellationMsg::GetPipelineTitle(root_pipeline_id)).unwrap(); + self.constellation_chan.send(ConstellationMsg::GetPipelineTitle(root_pipeline_id)).unwrap(); } } diff --git a/servo/components/compositing/compositor_task.rs b/servo/components/compositing/compositor_task.rs index 70c59132b6d3..9e5c59d54679 100644 --- a/servo/components/compositing/compositor_task.rs +++ b/servo/components/compositing/compositor_task.rs @@ -5,15 +5,16 @@ //! Communication with the compositor task. use compositor; -use euclid::{Point2D, Size2D}; +use euclid::point::Point2D; +use euclid::size::Size2D; use headless; -use ipc_channel::ipc::{IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use layers::layers::{BufferRequest, LayerBufferSet}; use layers::platform::surface::{NativeDisplay, NativeSurface}; use msg::compositor_msg::{Epoch, EventResult, FrameTreeId, LayerId, LayerProperties}; use msg::compositor_msg::{PaintListener, ScriptToCompositorMsg}; use msg::constellation_msg::CompositorMsg as ConstellationMsg; -use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId}; +use msg::constellation_msg::{AnimationState, PipelineId}; use msg::constellation_msg::{Image, Key, KeyModifiers, KeyState}; use profile_traits::mem; use profile_traits::time; @@ -60,11 +61,11 @@ pub fn run_script_listener_thread(compositor_proxy: Box) { while let Ok(msg) = receiver.recv() { match msg { - ScriptToCompositorMsg::ScrollFragmentPoint(pipeline_id, layer_id, point, _smooth) => { + ScriptToCompositorMsg::ScrollFragmentPoint(pipeline_id, layer_id, point, smooth) => { compositor_proxy.send(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point, - _smooth)); + smooth)); } ScriptToCompositorMsg::GetClientWindow(send) => { @@ -80,7 +81,7 @@ pub fn run_script_listener_thread(compositor_proxy: Box { - let (chan, port) = channel(); + let (chan, port) = ipc::channel().unwrap(); compositor_proxy.send(Msg::Exit(chan)); port.recv().unwrap(); } @@ -152,7 +153,7 @@ impl PaintListener for Box { /// Messages from the painting task and the constellation task to the compositor task. pub enum Msg { /// Requests that the compositor shut down. - Exit(Sender<()>), + Exit(IpcSender<()>), /// Informs the compositor that the constellation has completed shutdown. /// Required because the constellation can have pending calls to make @@ -180,7 +181,7 @@ pub enum Msg { /// Alerts the compositor that the given pipeline has changed whether it is running animations. ChangeRunningAnimationsState(PipelineId, AnimationState), /// Replaces the current frame tree, typically called during main frame navigation. - SetFrameTree(SendableFrameTree, Sender<()>, ConstellationChan), + SetFrameTree(SendableFrameTree, IpcSender<()>, Sender), /// The load of a page has begun: (can go back, can go forward). LoadStart(bool, bool), /// The load of a page has completed: (can go back, can go forward). @@ -296,7 +297,7 @@ pub struct InitialCompositorState { /// A port on which messages inbound to the compositor can be received. pub receiver: Box, /// A channel to the constellation. - pub constellation_chan: ConstellationChan, + pub constellation_chan: Sender, /// A channel to the time profiler thread. pub time_profiler_chan: time::ProfilerChan, /// A channel to the memory profiler thread. diff --git a/servo/components/compositing/constellation.rs b/servo/components/compositing/constellation.rs index 8d8d24d050ad..2084ace97014 100644 --- a/servo/components/compositing/constellation.rs +++ b/servo/components/compositing/constellation.rs @@ -18,8 +18,11 @@ use compositor_task::Msg as ToCompositorMsg; use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg}; use euclid::scale_factor::ScaleFactor; use euclid::size::{Size2D, TypedSize2D}; +use gaol; +use gaol::sandbox::{self, Sandbox, SandboxMethods}; use gfx::font_cache_task::FontCacheTask; -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender}; +use ipc_channel::router::ROUTER; use layout_traits::{LayoutControlChan, LayoutTaskFactory}; use msg::compositor_msg::Epoch; use msg::constellation_msg::AnimationState; @@ -37,19 +40,21 @@ use net_traits::image_cache_task::ImageCacheTask; use net_traits::storage_task::{StorageTask, StorageTaskMsg}; use net_traits::{self, ResourceTask}; use offscreen_gl_context::GLContextAttributes; -use pipeline::{CompositionPipeline, InitialPipelineState, Pipeline}; +use pipeline::{CompositionPipeline, InitialPipelineState, Pipeline, UnprivilegedPipelineContent}; use profile_traits::mem; use profile_traits::time; +use sandboxing; use script_traits::{CompositorEvent, ConstellationControlMsg, LayoutControlMsg}; use script_traits::{ScriptState, ScriptTaskFactory}; use script_traits::{TimerEventRequest}; use std::borrow::ToOwned; use std::collections::HashMap; +use std::env; use std::io::{self, Write}; use std::marker::PhantomData; use std::mem::replace; use std::process; -use std::sync::mpsc::{Receiver, Sender, channel}; +use std::sync::mpsc::{Sender, channel, Receiver}; use style_traits::viewport::ViewportConstraints; use timer_scheduler::TimerScheduler; use url::Url; @@ -79,7 +84,7 @@ pub struct Constellation { pub script_sender: ConstellationChan, /// A channel through which compositor messages can be sent to this object. - pub compositor_sender: ConstellationChan, + pub compositor_sender: Sender, /// Receives messages from scripts. pub script_receiver: Receiver, @@ -156,6 +161,9 @@ pub struct Constellation { webgl_paint_tasks: Vec>, scheduler_chan: IpcSender, + + /// A list of child content processes. + child_processes: Vec, } /// State needed to construct a constellation. @@ -259,14 +267,21 @@ enum ExitPipelineMode { Force, } +enum ChildProcess { + Sandboxed(gaol::platform::process::Process), + Unsandboxed(process::Child), +} + impl Constellation { - pub fn start(state: InitialConstellationState) -> ConstellationChan { - let (script_receiver, script_sender) = ConstellationChan::::new(); - let (compositor_receiver, compositor_sender) = ConstellationChan::::new(); + pub fn start(state: InitialConstellationState) -> Sender { + let (ipc_script_receiver, ipc_script_sender) = ConstellationChan::::new(); + //let (script_receiver, script_sender) = channel(); + let script_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_script_receiver); + let (compositor_sender, compositor_receiver) = channel(); let compositor_sender_clone = compositor_sender.clone(); spawn_named("Constellation".to_owned(), move || { let mut constellation: Constellation = Constellation { - script_sender: script_sender, + script_sender: ipc_script_sender, compositor_sender: compositor_sender_clone, script_receiver: script_receiver, compositor_receiver: compositor_receiver, @@ -305,6 +320,7 @@ impl Constellation { canvas_paint_tasks: Vec::new(), webgl_paint_tasks: Vec::new(), scheduler_chan: TimerScheduler::start(), + child_processes: Vec::new(), }; let namespace_id = constellation.next_pipeline_namespace_id(); PipelineNamespace::install(namespace_id); @@ -333,10 +349,10 @@ impl Constellation { pipeline_id: PipelineId, parent_info: Option<(PipelineId, SubpageId)>, initial_window_size: Option>, - script_channel: Option>, + script_channel: Option>, load_data: LoadData) { let spawning_paint_only = script_channel.is_some(); - let (pipeline, mut pipeline_content) = + let (pipeline, unprivileged_pipeline_content, mut privileged_pipeline_content) = Pipeline::create::(InitialPipelineState { id: pipeline_id, parent_info: parent_info, @@ -357,12 +373,39 @@ impl Constellation { pipeline_namespace_id: self.next_pipeline_namespace_id(), }); - // TODO(pcwalton): In multiprocess mode, send that `PipelineContent` instance over to - // the content process and call this over there. if spawning_paint_only { - pipeline_content.start_paint_task(); + privileged_pipeline_content.start_paint_task(); } else { - pipeline_content.start_all::(); + privileged_pipeline_content.start_all(); + + // Spawn the child process. + // + // Yes, that's all there is to it! + if opts::multiprocess() { + let (server, token) = + IpcOneShotServer::>::new().unwrap(); + + // If there is a sandbox, use the `gaol` API to create the child process. + let child_process = if opts::get().sandbox { + let mut command = sandbox::Command::me().unwrap(); + command.arg("--content-process").arg(token); + let profile = sandboxing::content_process_sandbox_profile(); + ChildProcess::Sandboxed(Sandbox::new(profile).start(&mut command).expect( + "Failed to start sandboxed child process!")) + } else { + let path_to_self = env::current_exe().unwrap(); + let mut child_process = process::Command::new(path_to_self); + child_process.arg("--content-process"); + child_process.arg(token); + ChildProcess::Unsandboxed(child_process.spawn().unwrap()) + }; + self.child_processes.push(child_process); + + let (_receiver, sender) = server.accept().unwrap(); + sender.send(unprivileged_pipeline_content).unwrap(); + } else { + unprivileged_pipeline_content.start_all::(false); + } } assert!(!self.pipelines.contains_key(&pipeline_id)); @@ -1290,7 +1333,7 @@ impl Constellation { // Synchronously query the script task for this pipeline // to see if it is idle. - let (sender, receiver) = channel(); + let (sender, receiver) = ipc::channel().unwrap(); let msg = ConstellationControlMsg::GetCurrentState(sender, frame.current); pipeline.script_chan.send(msg).unwrap(); let result = receiver.recv().unwrap(); @@ -1445,7 +1488,7 @@ impl Constellation { if let Some(root_frame_id) = self.root_frame_id { let frame_tree = self.frame_to_sendable(root_frame_id); - let (chan, port) = channel(); + let (chan, port) = ipc::channel().unwrap(); self.compositor_proxy.send(ToCompositorMsg::SetFrameTree(frame_tree, chan, self.compositor_sender.clone())); diff --git a/servo/components/compositing/headless.rs b/servo/components/compositing/headless.rs index 52435af0cd7f..a31d1b3c5315 100644 --- a/servo/components/compositing/headless.rs +++ b/servo/components/compositing/headless.rs @@ -8,9 +8,10 @@ use euclid::scale_factor::ScaleFactor; use euclid::{Point2D, Size2D}; use msg::constellation_msg::AnimationState; use msg::constellation_msg::CompositorMsg as ConstellationMsg; -use msg::constellation_msg::{ConstellationChan, WindowSizeData}; +use msg::constellation_msg::WindowSizeData; use profile_traits::mem; use profile_traits::time; +use std::sync::mpsc::Sender; use util::opts; use windowing::WindowEvent; @@ -22,7 +23,7 @@ pub struct NullCompositor { /// The port on which we receive messages. pub port: Box, /// A channel to the constellation. - constellation_chan: ConstellationChan, + constellation_chan: Sender, /// A channel to the time profiler. time_profiler_chan: time::ProfilerChan, /// A channel to the memory profiler. @@ -44,8 +45,7 @@ impl NullCompositor { // Tell the constellation about the initial fake size. { - let ConstellationChan(ref chan) = compositor.constellation_chan; - chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { + compositor.constellation_chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { initial_viewport: Size2D::typed(800_f32, 600_f32), visible_viewport: Size2D::typed(800_f32, 600_f32), device_pixel_ratio: @@ -62,8 +62,7 @@ impl CompositorEventListener for NullCompositor { match self.port.recv_compositor_msg() { Msg::Exit(chan) => { debug!("shutting down the constellation"); - let ConstellationChan(ref con_chan) = self.constellation_chan; - con_chan.send(ConstellationMsg::Exit).unwrap(); + self.constellation_chan.send(ConstellationMsg::Exit).unwrap(); chan.send(()).unwrap(); } @@ -100,7 +99,7 @@ impl CompositorEventListener for NullCompositor { AnimationState::NoAnimationCallbacksPresent => {} AnimationState::AnimationCallbacksPresent => { let msg = ConstellationMsg::TickAnimation(pipeline_id); - self.constellation_chan.0.send(msg).unwrap() + self.constellation_chan.send(msg).unwrap() } } } diff --git a/servo/components/compositing/lib.rs b/servo/components/compositing/lib.rs index a38f1d8490fa..fa2ad24fd60c 100644 --- a/servo/components/compositing/lib.rs +++ b/servo/components/compositing/lib.rs @@ -3,7 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![feature(box_syntax)] +#![feature(custom_derive)] #![feature(iter_cmp)] +#![feature(plugin)] #![feature(slice_bytes)] #![feature(vec_push_all)] #![feature(mpsc_select)] @@ -11,6 +13,7 @@ #![plugin(plugins)] #![deny(unsafe_code)] +#![plugin(serde_macros)] extern crate app_units; #[macro_use] @@ -31,6 +34,7 @@ extern crate core_text; extern crate devtools_traits; extern crate euclid; +extern crate gaol; extern crate gfx; extern crate gfx_traits; extern crate gleam; @@ -43,7 +47,10 @@ extern crate net_traits; extern crate num; extern crate offscreen_gl_context; extern crate script_traits; +extern crate serde; extern crate style_traits; + +extern crate libc; extern crate time; extern crate url; @@ -59,4 +66,5 @@ mod timer_scheduler; pub mod compositor_task; pub mod constellation; pub mod pipeline; +pub mod sandboxing; pub mod windowing; diff --git a/servo/components/compositing/pipeline.rs b/servo/components/compositing/pipeline.rs index 8742898eaa44..2fbc5c6b0a64 100644 --- a/servo/components/compositing/pipeline.rs +++ b/servo/components/compositing/pipeline.rs @@ -14,6 +14,7 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layers::geometry::DevicePixel; use layout_traits::{LayoutControlChan, LayoutTaskFactory}; +use msg::compositor_msg::ScriptToCompositorMsg; use msg::constellation_msg::ScriptMsg as ConstellationMsg; use msg::constellation_msg::{ConstellationChan, Failure, FrameId, PipelineId, SubpageId}; use msg::constellation_msg::{LoadData, MozBrowserEvent, WindowSizeData}; @@ -26,7 +27,6 @@ use profile_traits::time; use script_traits::{ConstellationControlMsg, InitialScriptState}; use script_traits::{LayoutControlMsg, NewLayoutInfo, ScriptTaskFactory}; use script_traits::{TimerEventRequest}; -use std::any::Any; use std::mem; use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; @@ -34,20 +34,21 @@ use url::Url; use util; use util::geometry::{PagePx, ViewportPx}; use util::ipc::OptionalIpcSender; +use util::opts::{self, Opts}; use util::prefs; /// A uniquely-identifiable pipeline of script task, layout task, and paint task. pub struct Pipeline { pub id: PipelineId, pub parent_info: Option<(PipelineId, SubpageId)>, - pub script_chan: Sender, + pub script_chan: IpcSender, /// A channel to layout, for performing reflows and shutdown. pub layout_chan: LayoutControlChan, /// A channel to the compositor. pub compositor_proxy: Box, pub chrome_to_paint_chan: Sender, - pub layout_shutdown_port: Receiver<()>, - pub paint_shutdown_port: Receiver<()>, + pub layout_shutdown_port: IpcReceiver<()>, + pub paint_shutdown_port: IpcReceiver<()>, /// URL corresponding to the most recently-loaded page. pub url: Url, /// The title of the most recently-loaded page. @@ -63,7 +64,7 @@ pub struct Pipeline { #[derive(Clone)] pub struct CompositionPipeline { pub id: PipelineId, - pub script_chan: Sender, + pub script_chan: IpcSender, pub layout_chan: LayoutControlChan, pub chrome_to_paint_chan: Sender, } @@ -104,7 +105,7 @@ pub struct InitialPipelineState { pub device_pixel_ratio: ScaleFactor, /// A channel to the script thread, if applicable. If this is `Some`, /// then `parent_info` must also be `Some`. - pub script_chan: Option>, + pub script_chan: Option>, /// Information about the page to load. pub load_data: LoadData, /// The ID of the pipeline namespace for this script thread. @@ -115,13 +116,14 @@ impl Pipeline { /// Starts a paint task, layout task, and possibly a script task. /// Returns the channels wrapped in a struct. pub fn create(state: InitialPipelineState) - -> (Pipeline, PipelineContent) + -> (Pipeline, UnprivilegedPipelineContent, PrivilegedPipelineContent) where LTF: LayoutTaskFactory, STF: ScriptTaskFactory { let (layout_to_paint_chan, layout_to_paint_port) = util::ipc::optional_ipc_channel(); let (chrome_to_paint_chan, chrome_to_paint_port) = channel(); - let (paint_shutdown_chan, paint_shutdown_port) = channel(); - let (layout_shutdown_chan, layout_shutdown_port) = channel(); + let (paint_shutdown_chan, paint_shutdown_port) = ipc::channel().unwrap(); + let (layout_shutdown_chan, layout_shutdown_port) = ipc::channel().unwrap(); let (pipeline_chan, pipeline_port) = ipc::channel().unwrap(); + let (script_to_compositor_chan, script_to_compositor_port) = ipc::channel().unwrap(); let mut pipeline_port = Some(pipeline_port); let failure = Failure { @@ -148,6 +150,9 @@ impl Pipeline { script_to_devtools_chan }); + let (layout_content_process_shutdown_chan, layout_content_process_shutdown_port) = + ipc::channel().unwrap(); + let (script_chan, script_port) = match state.script_chan { Some(script_chan) => { let (containing_pipeline_id, subpage_id) = @@ -157,10 +162,11 @@ impl Pipeline { new_pipeline_id: state.id, subpage_id: subpage_id, load_data: state.load_data.clone(), - paint_chan: box layout_to_paint_chan.clone() as Box, + paint_chan: layout_to_paint_chan.clone().to_opaque(), failure: failure, pipeline_port: mem::replace(&mut pipeline_port, None).unwrap(), layout_shutdown_chan: layout_shutdown_chan.clone(), + content_process_shutdown_chan: layout_content_process_shutdown_chan.clone(), }; script_chan.send(ConstellationControlMsg::AttachLayout(new_layout_info)) @@ -168,11 +174,14 @@ impl Pipeline { (script_chan, None) } None => { - let (script_chan, script_port) = channel(); + let (script_chan, script_port) = ipc::channel().unwrap(); (script_chan, Some(script_port)) } }; + let (script_content_process_shutdown_chan, script_content_process_shutdown_port) = + ipc::channel().unwrap(); + let pipeline = Pipeline::new(state.id, state.parent_info, script_chan.clone(), @@ -184,45 +193,63 @@ impl Pipeline { state.load_data.url.clone(), state.window_size); - let pipeline_content = PipelineContent { + let unprivileged_pipeline_content = UnprivilegedPipelineContent { id: state.id, parent_info: state.parent_info, - constellation_chan: state.constellation_chan, + constellation_chan: state.constellation_chan.clone(), scheduler_chan: state.scheduler_chan, - compositor_proxy: state.compositor_proxy, devtools_chan: script_to_devtools_chan, image_cache_task: state.image_cache_task, - font_cache_task: state.font_cache_task, + font_cache_task: state.font_cache_task.clone(), resource_task: state.resource_task, storage_task: state.storage_task, - time_profiler_chan: state.time_profiler_chan, - mem_profiler_chan: state.mem_profiler_chan, + time_profiler_chan: state.time_profiler_chan.clone(), + mem_profiler_chan: state.mem_profiler_chan.clone(), window_size: window_size, script_chan: script_chan, - load_data: state.load_data, + load_data: state.load_data.clone(), failure: failure, script_port: script_port, + opts: (*opts::get()).clone(), layout_to_paint_chan: layout_to_paint_chan, - chrome_to_paint_chan: chrome_to_paint_chan, - layout_to_paint_port: Some(layout_to_paint_port), - chrome_to_paint_port: Some(chrome_to_paint_port), pipeline_port: pipeline_port, - paint_shutdown_chan: paint_shutdown_chan, layout_shutdown_chan: layout_shutdown_chan, + paint_shutdown_chan: paint_shutdown_chan.clone(), + script_to_compositor_chan: script_to_compositor_chan, pipeline_namespace_id: state.pipeline_namespace_id, + layout_content_process_shutdown_chan: layout_content_process_shutdown_chan, + layout_content_process_shutdown_port: layout_content_process_shutdown_port, + script_content_process_shutdown_chan: script_content_process_shutdown_chan, + script_content_process_shutdown_port: script_content_process_shutdown_port, }; - (pipeline, pipeline_content) + let privileged_pipeline_content = PrivilegedPipelineContent { + id: state.id, + constellation_chan: state.constellation_chan, + compositor_proxy: state.compositor_proxy, + font_cache_task: state.font_cache_task, + time_profiler_chan: state.time_profiler_chan, + mem_profiler_chan: state.mem_profiler_chan, + load_data: state.load_data, + failure: failure, + layout_to_paint_port: Some(layout_to_paint_port), + chrome_to_paint_chan: chrome_to_paint_chan, + chrome_to_paint_port: Some(chrome_to_paint_port), + paint_shutdown_chan: paint_shutdown_chan, + script_to_compositor_port: Some(script_to_compositor_port), + }; + + (pipeline, unprivileged_pipeline_content, privileged_pipeline_content) } pub fn new(id: PipelineId, parent_info: Option<(PipelineId, SubpageId)>, - script_chan: Sender, + script_chan: IpcSender, layout_chan: LayoutControlChan, compositor_proxy: Box, chrome_to_paint_chan: Sender, - layout_shutdown_port: Receiver<()>, - paint_shutdown_port: Receiver<()>, + layout_shutdown_port: IpcReceiver<()>, + paint_shutdown_port: IpcReceiver<()>, url: Url, size: Option>) -> Pipeline { @@ -315,13 +342,14 @@ impl Pipeline { } } -pub struct PipelineContent { +#[derive(Deserialize, Serialize)] +pub struct UnprivilegedPipelineContent { id: PipelineId, parent_info: Option<(PipelineId, SubpageId)>, constellation_chan: ConstellationChan, scheduler_chan: IpcSender, - compositor_proxy: Box, devtools_chan: Option>, + script_to_compositor_chan: IpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, resource_task: ResourceTask, @@ -329,39 +357,31 @@ pub struct PipelineContent { time_profiler_chan: time::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan, window_size: Option, - script_chan: Sender, + script_chan: IpcSender, load_data: LoadData, failure: Failure, - script_port: Option>, + script_port: Option>, layout_to_paint_chan: OptionalIpcSender, - chrome_to_paint_chan: Sender, - layout_to_paint_port: Option>, - chrome_to_paint_port: Option>, - paint_shutdown_chan: Sender<()>, + opts: Opts, + paint_shutdown_chan: IpcSender<()>, pipeline_port: Option>, - layout_shutdown_chan: Sender<()>, pipeline_namespace_id: PipelineNamespaceId, + layout_shutdown_chan: IpcSender<()>, + layout_content_process_shutdown_chan: IpcSender<()>, + layout_content_process_shutdown_port: IpcReceiver<()>, + script_content_process_shutdown_chan: IpcSender<()>, + script_content_process_shutdown_port: IpcReceiver<()>, } -impl PipelineContent { - pub fn start_all(mut self) where LTF: LayoutTaskFactory, STF: ScriptTaskFactory { +impl UnprivilegedPipelineContent { + pub fn start_all(mut self, wait_for_completion: bool) + where LTF: LayoutTaskFactory, STF: ScriptTaskFactory { let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>); - let (script_to_compositor_chan, script_to_compositor_port) = ipc::channel().unwrap(); - - self.start_paint_task(); - - let compositor_proxy_for_script_listener_thread = - self.compositor_proxy.clone_compositor_proxy(); - thread::spawn(move || { - compositor_task::run_script_listener_thread( - compositor_proxy_for_script_listener_thread, - script_to_compositor_port) - }); ScriptTaskFactory::create(None::<&mut STF>, InitialScriptState { id: self.id, parent_info: self.parent_info, - compositor: script_to_compositor_chan, + compositor: self.script_to_compositor_chan.clone(), control_chan: self.script_chan.clone(), control_port: mem::replace(&mut self.script_port, None).unwrap(), constellation_chan: self.constellation_chan.clone(), @@ -375,6 +395,7 @@ impl PipelineContent { devtools_chan: self.devtools_chan, window_size: self.window_size, pipeline_namespace_id: self.pipeline_namespace_id, + content_process_shutdown_chan: self.script_content_process_shutdown_chan.clone(), }, &layout_pair, self.load_data.clone()); LayoutTaskFactory::create(None::<&mut LTF>, @@ -391,7 +412,49 @@ impl PipelineContent { self.font_cache_task, self.time_profiler_chan, self.mem_profiler_chan, - self.layout_shutdown_chan); + self.layout_shutdown_chan, + self.layout_content_process_shutdown_chan.clone()); + + if wait_for_completion { + self.script_content_process_shutdown_port.recv().unwrap(); + self.layout_content_process_shutdown_port.recv().unwrap(); + } + } + + pub fn opts(&self) -> Opts { + self.opts.clone() + } +} + +pub struct PrivilegedPipelineContent { + id: PipelineId, + constellation_chan: ConstellationChan, + compositor_proxy: Box, + script_to_compositor_port: Option>, + font_cache_task: FontCacheTask, + time_profiler_chan: time::ProfilerChan, + mem_profiler_chan: profile_mem::ProfilerChan, + load_data: LoadData, + failure: Failure, + layout_to_paint_port: Option>, + chrome_to_paint_chan: Sender, + chrome_to_paint_port: Option>, + paint_shutdown_chan: IpcSender<()>, +} + +impl PrivilegedPipelineContent { + pub fn start_all(&mut self) { + self.start_paint_task(); + + let compositor_proxy_for_script_listener_thread = + self.compositor_proxy.clone_compositor_proxy(); + let script_to_compositor_port = + mem::replace(&mut self.script_to_compositor_port, None).unwrap(); + thread::spawn(move || { + compositor_task::run_script_listener_thread( + compositor_proxy_for_script_listener_thread, + script_to_compositor_port) + }); } pub fn start_paint_task(&mut self) { diff --git a/servo/components/compositing/sandboxing.rs b/servo/components/compositing/sandboxing.rs new file mode 100644 index 000000000000..dd02f878ae30 --- /dev/null +++ b/servo/components/compositing/sandboxing.rs @@ -0,0 +1,38 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use gaol::platform; +use gaol::profile::{Operation, PathPattern, Profile}; +use std::path::PathBuf; +use util::resource_files; + +/// Our content process sandbox profile on Mac. As restrictive as possible. +#[cfg(target_os = "macos")] +pub fn content_process_sandbox_profile() -> Profile { + Profile::new(vec![ + Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/dev/urandom"))), + Operation::FileReadAll(PathPattern::Subpath(resource_files::resources_dir_path())), + Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/Library/Fonts"))), + Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/System/Library/Fonts"))), + Operation::FileReadAll(PathPattern::Subpath(PathBuf::from( + "/System/Library/Frameworks/ApplicationServices.framework/"))), + Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/"))), + Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/Library"))), + Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/System"))), + Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/etc"))), + Operation::SystemInfoRead, + Operation::PlatformSpecific(platform::macos::Operation::MachLookup( + b"com.apple.FontServer".to_vec())), + ]).expect("Failed to create sandbox profile!") +} + +/// Our content process sandbox profile on Linux. As restrictive as possible. +#[cfg(not(target_os = "macos"))] +pub fn content_process_sandbox_profile() -> Profile { + Profile::new(vec![ + Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/dev/urandom"))), + Operation::FileReadAll(PathPattern::Subpath(resource_files::resources_dir_path())), + ]).expect("Failed to create sandbox profile!") +} + diff --git a/servo/components/gfx/font_cache_task.rs b/servo/components/gfx/font_cache_task.rs index 6122325b6022..578d3f9d9e79 100644 --- a/servo/components/gfx/font_cache_task.rs +++ b/servo/components/gfx/font_cache_task.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use font_template::{FontTemplate, FontTemplateDescriptor}; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::{AsyncResponseTarget, PendingAsyncLoad, ResourceTask, ResponseAction}; use platform::font_context::FontContextHandle; @@ -15,7 +15,7 @@ use platform::font_template::FontTemplateData; use std::borrow::ToOwned; use std::collections::HashMap; use std::mem; -use std::sync::mpsc::{Sender, Receiver, channel}; +use std::sync::mpsc::channel; use std::sync::{Arc, Mutex}; use string_cache::Atom; use style::font_face::Source; @@ -77,15 +77,17 @@ impl FontFamily { } /// Commands that the FontContext sends to the font cache task. +#[derive(Deserialize, Serialize)] pub enum Command { - GetFontTemplate(String, FontTemplateDescriptor, Sender), - GetLastResortFontTemplate(FontTemplateDescriptor, Sender), - AddWebFont(Atom, Source, Sender<()>), - AddDownloadedWebFont(LowercaseString, Url, Vec, Sender<()>), - Exit(Sender<()>), + GetFontTemplate(String, FontTemplateDescriptor, IpcSender), + GetLastResortFontTemplate(FontTemplateDescriptor, IpcSender), + AddWebFont(Atom, Source, IpcSender<()>), + AddDownloadedWebFont(LowercaseString, Url, Vec, IpcSender<()>), + Exit(IpcSender<()>), } /// Reply messages sent from the font cache task to the FontContext caller. +#[derive(Deserialize, Serialize)] pub enum Reply { GetFontTemplateReply(Option>), } @@ -93,8 +95,8 @@ pub enum Reply { /// The font cache task itself. It maintains a list of reference counted /// font templates that are currently in use. struct FontCache { - port: Receiver, - channel_to_self: Sender, + port: IpcReceiver, + channel_to_self: IpcSender, generic_fonts: HashMap, local_families: HashMap, web_families: HashMap, @@ -269,14 +271,14 @@ impl FontCache { /// The public interface to the font cache task, used exclusively by /// the per-thread/task FontContext structures. -#[derive(Clone)] +#[derive(Clone, Deserialize, Serialize)] pub struct FontCacheTask { - chan: Sender, + chan: IpcSender, } impl FontCacheTask { pub fn new(resource_task: ResourceTask) -> FontCacheTask { - let (chan, port) = channel(); + let (chan, port) = ipc::channel().unwrap(); let channel_to_self = chan.clone(); spawn_named("FontCacheTask".to_owned(), move || { @@ -310,7 +312,7 @@ impl FontCacheTask { pub fn find_font_template(&self, family: String, desc: FontTemplateDescriptor) -> Option> { - let (response_chan, response_port) = channel(); + let (response_chan, response_port) = ipc::channel().unwrap(); self.chan.send(Command::GetFontTemplate(family, desc, response_chan)).unwrap(); let reply = response_port.recv().unwrap(); @@ -325,7 +327,7 @@ impl FontCacheTask { pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor) -> Arc { - let (response_chan, response_port) = channel(); + let (response_chan, response_port) = ipc::channel().unwrap(); self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)).unwrap(); let reply = response_port.recv().unwrap(); @@ -337,12 +339,12 @@ impl FontCacheTask { } } - pub fn add_web_font(&self, family: Atom, src: Source, sender: Sender<()>) { + pub fn add_web_font(&self, family: Atom, src: Source, sender: IpcSender<()>) { self.chan.send(Command::AddWebFont(family, src, sender)).unwrap(); } pub fn exit(&self) { - let (response_chan, response_port) = channel(); + let (response_chan, response_port) = ipc::channel().unwrap(); self.chan.send(Command::Exit(response_chan)).unwrap(); response_port.recv().unwrap(); } diff --git a/servo/components/gfx/font_template.rs b/servo/components/gfx/font_template.rs index 843216a81bbf..45becf31d5c3 100644 --- a/servo/components/gfx/font_template.rs +++ b/servo/components/gfx/font_template.rs @@ -14,7 +14,7 @@ use style::computed_values::{font_stretch, font_weight}; /// to be expanded or refactored when we support more of the font styling parameters. /// /// NB: If you change this, you will need to update `style::properties::compute_font_hash()`. -#[derive(Clone, Copy, Eq, Hash)] +#[derive(Clone, Copy, Eq, Hash, Deserialize, Serialize)] pub struct FontTemplateDescriptor { pub weight: font_weight::T, pub stretch: font_stretch::T, diff --git a/servo/components/gfx/paint_task.rs b/servo/components/gfx/paint_task.rs index 82b0e75d284d..d3859e306517 100644 --- a/servo/components/gfx/paint_task.rs +++ b/servo/components/gfx/paint_task.rs @@ -36,8 +36,7 @@ use std::sync::mpsc::{Receiver, Select, Sender, channel}; use url::Url; use util::geometry::{ExpandToPixelBoundaries, ZERO_POINT}; use util::opts; -use util::task::spawn_named; -use util::task::spawn_named_with_send_on_failure; +use util::task; use util::task_state; #[derive(Clone, Deserialize, Serialize, HeapSizeOf)] @@ -255,9 +254,11 @@ impl PaintTask where C: PaintListener + Send + 'static { failure_msg: Failure, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, - shutdown_chan: Sender<()>) { + shutdown_chan: IpcSender<()>) { let ConstellationChan(c) = constellation_chan.clone(); - spawn_named_with_send_on_failure(format!("PaintTask {:?}", id), task_state::PAINT, move || { + task::spawn_named_with_send_on_failure(format!("PaintTask {:?}", id), + task_state::PAINT, + move || { { // Ensures that the paint task and graphics context are destroyed before the // shutdown message. @@ -572,7 +573,7 @@ impl WorkerThreadProxy { let (to_worker_sender, to_worker_receiver) = channel(); let font_cache_task = font_cache_task.clone(); let time_profiler_chan = time_profiler_chan.clone(); - spawn_named("PaintWorker".to_owned(), move || { + task::spawn_named("PaintWorker".to_owned(), move || { let mut worker_thread = WorkerThread::new(from_worker_sender, to_worker_receiver, native_display, diff --git a/servo/components/layout/layout_task.rs b/servo/components/layout/layout_task.rs index e4cfcefeaf5f..6eced0fd13e7 100644 --- a/servo/components/layout/layout_task.rs +++ b/servo/components/layout/layout_task.rs @@ -70,7 +70,7 @@ use util::ipc::OptionalIpcSender; use util::logical_geometry::LogicalPoint; use util::mem::HeapSizeOf; use util::opts; -use util::task::spawn_named_with_send_on_failure; +use util::task; use util::task_state; use util::workqueue::WorkQueue; use wrapper::{LayoutDocument, LayoutElement, LayoutNode, ServoLayoutNode}; @@ -137,13 +137,13 @@ pub struct LayoutTask { font_cache_receiver: Receiver<()>, /// The channel on which the font cache can send messages to us. - font_cache_sender: Sender<()>, + font_cache_sender: IpcSender<()>, /// The channel on which messages can be sent to the constellation. constellation_chan: ConstellationChan, /// The channel on which messages can be sent to the script task. - script_chan: Sender, + script_chan: IpcSender, /// The channel on which messages can be sent to the painting task. paint_chan: OptionalIpcSender, @@ -219,17 +219,18 @@ impl LayoutTaskFactory for LayoutTask { pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, failure_msg: Failure, - script_chan: Sender, + script_chan: IpcSender, paint_chan: OptionalIpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, - shutdown_chan: Sender<()>) { + shutdown_chan: IpcSender<()>, + content_process_shutdown_chan: IpcSender<()>) { let ConstellationChan(con_chan) = constellation_chan.clone(); - spawn_named_with_send_on_failure(format!("LayoutTask {:?}", id), - task_state::LAYOUT, - move || { + task::spawn_named_with_send_on_failure(format!("LayoutTask {:?}", id), + task_state::LAYOUT, + move || { { // Ensures layout task is destroyed before we send shutdown message let sender = chan.sender(); let layout = LayoutTask::new(id, @@ -250,7 +251,8 @@ impl LayoutTaskFactory for LayoutTask { layout.start(); }, reporter_name, sender, Msg::CollectReports); } - shutdown_chan.send(()).unwrap(); + let _ = shutdown_chan.send(()); + let _ = content_process_shutdown_chan.send(()); }, ConstellationMsg::Failure(failure_msg), con_chan); } } @@ -318,12 +320,12 @@ impl<'a, 'b: 'a> RwData<'a, 'b> { fn add_font_face_rules(stylesheet: &Stylesheet, device: &Device, font_cache_task: &FontCacheTask, - font_cache_sender: &Sender<()>, + font_cache_sender: &IpcSender<()>, outstanding_web_fonts_counter: &Arc) { for font_face in stylesheet.effective_rules(&device).font_face() { for source in &font_face.sources { if opts::get().load_webfonts_synchronously { - let (sender, receiver) = channel(); + let (sender, receiver) = ipc::channel().unwrap(); font_cache_task.add_web_font(font_face.family.clone(), (*source).clone(), sender); @@ -346,7 +348,7 @@ impl LayoutTask { port: Receiver, pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, - script_chan: Sender, + script_chan: IpcSender, paint_chan: OptionalIpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, @@ -375,7 +377,10 @@ impl LayoutTask { let image_cache_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_receiver); - let (font_cache_sender, font_cache_receiver) = channel(); + // Ask the router to proxy IPC messages from the font cache task to the layout thread. + let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap(); + let font_cache_receiver = + ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_font_cache_receiver); let stylist = box Stylist::new(device); let outstanding_web_fonts_counter = Arc::new(AtomicUsize::new(0)); @@ -383,7 +388,7 @@ impl LayoutTask { add_font_face_rules(stylesheet, &stylist.device, &font_cache_task, - &font_cache_sender, + &ipc_font_cache_sender, &outstanding_web_fonts_counter); } @@ -404,7 +409,7 @@ impl LayoutTask { image_cache_receiver: image_cache_receiver, image_cache_sender: ImageCacheChan(ipc_image_cache_sender), font_cache_receiver: font_cache_receiver, - font_cache_sender: font_cache_sender, + font_cache_sender: ipc_font_cache_sender, canvas_layers_receiver: canvas_layers_receiver, canvas_layers_sender: canvas_layers_sender, parallel_traversal: parallel_traversal, @@ -668,14 +673,13 @@ impl LayoutTask { info.constellation_chan, info.failure, info.script_chan.clone(), - *info.paint_chan - .downcast::>() - .unwrap(), + info.paint_chan.to::(), self.image_cache_task.clone(), self.font_cache_task.clone(), self.time_profiler_chan.clone(), self.mem_profiler_chan.clone(), - info.layout_shutdown_chan); + info.layout_shutdown_chan, + info.content_process_shutdown_chan); } /// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is diff --git a/servo/components/layout_traits/lib.rs b/servo/components/layout_traits/lib.rs index 9205d4205837..50024754ba45 100644 --- a/servo/components/layout_traits/lib.rs +++ b/servo/components/layout_traits/lib.rs @@ -29,7 +29,6 @@ use msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; use net_traits::image_cache_task::ImageCacheTask; use profile_traits::{mem, time}; use script_traits::{LayoutControlMsg, ConstellationControlMsg, OpaqueScriptLayoutChannel}; -use std::sync::mpsc::Sender; use url::Url; use util::ipc::OptionalIpcSender; @@ -49,11 +48,12 @@ pub trait LayoutTaskFactory { pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, failure_msg: Failure, - script_chan: Sender, + script_chan: IpcSender, layout_to_paint_chan: OptionalIpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, - shutdown_chan: Sender<()>); + shutdown_chan: IpcSender<()>, + content_process_shutdown_chan: IpcSender<()>); } diff --git a/servo/components/msg/constellation_msg.rs b/servo/components/msg/constellation_msg.rs index 59da1f175db9..e01ff1c93475 100644 --- a/servo/components/msg/constellation_msg.rs +++ b/servo/components/msg/constellation_msg.rs @@ -11,13 +11,14 @@ use euclid::scale_factor::ScaleFactor; use euclid::size::{Size2D, TypedSize2D}; use hyper::header::Headers; use hyper::method::Method; -use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory}; use layers::geometry::DevicePixel; use offscreen_gl_context::GLContextAttributes; +use serde::{Deserialize, Serialize}; use std::cell::Cell; use std::collections::HashMap; use std::fmt; -use std::sync::mpsc::{Receiver, Sender, channel}; +use std::sync::mpsc::channel; use style_traits::viewport::ViewportConstraints; use url::Url; use util::cursor::Cursor; @@ -25,16 +26,17 @@ use util::geometry::{PagePx, ViewportPx}; use util::mem::HeapSizeOf; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; -pub struct ConstellationChan(pub Sender); +#[derive(Deserialize, Serialize)] +pub struct ConstellationChan(pub IpcSender); -impl ConstellationChan { - pub fn new() -> (Receiver, ConstellationChan) { - let (chan, port) = channel(); +impl ConstellationChan { + pub fn new() -> (IpcReceiver, ConstellationChan) { + let (chan, port) = ipc::channel().unwrap(); (port, ConstellationChan(chan)) } } -impl Clone for ConstellationChan { +impl Clone for ConstellationChan { fn clone(&self) -> ConstellationChan { ConstellationChan(self.0.clone()) } diff --git a/servo/components/profile/time.rs b/servo/components/profile/time.rs index 0de493368afe..8092be811bde 100644 --- a/servo/components/profile/time.rs +++ b/servo/components/profile/time.rs @@ -100,6 +100,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element", ProfilerCategory::ScriptSetViewport => "Script Set Viewport", ProfilerCategory::ScriptTimerEvent => "Script Timer Event", + ProfilerCategory::ScriptStylesheetLoad => "Script Stylesheet Load", ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event", ProfilerCategory::ScriptWorkerEvent => "Script Worker Event", ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat", diff --git a/servo/components/profile_traits/mem.rs b/servo/components/profile_traits/mem.rs index caf7ba8a7934..9fc78febdd69 100644 --- a/servo/components/profile_traits/mem.rs +++ b/servo/components/profile_traits/mem.rs @@ -25,7 +25,7 @@ impl OpaqueSender for Sender { /// Front-end representation of the profiler used to communicate with the /// profiler. -#[derive(Clone)] +#[derive(Clone, Deserialize, Serialize)] pub struct ProfilerChan(pub IpcSender); impl ProfilerChan { diff --git a/servo/components/profile_traits/time.rs b/servo/components/profile_traits/time.rs index 5ebfcc18e460..c29bbe7993fa 100644 --- a/servo/components/profile_traits/time.rs +++ b/servo/components/profile_traits/time.rs @@ -60,15 +60,16 @@ pub enum ProfilerCategory { ScriptDevtoolsMsg, ScriptDocumentEvent, ScriptDomEvent, + ScriptEvent, ScriptFileRead, ScriptImageCacheMsg, ScriptInputEvent, ScriptNetworkEvent, ScriptResize, - ScriptEvent, - ScriptUpdateReplacedElement, ScriptSetViewport, ScriptTimerEvent, + ScriptStylesheetLoad, + ScriptUpdateReplacedElement, ScriptWebSocketEvent, ScriptWorkerEvent, ApplicationHeartbeat, diff --git a/servo/components/script/dom/bindings/trace.rs b/servo/components/script/dom/bindings/trace.rs index efb0952e3dcc..e893f284978e 100644 --- a/servo/components/script/dom/bindings/trace.rs +++ b/servo/components/script/dom/bindings/trace.rs @@ -53,7 +53,7 @@ use js::jsval::JSVal; use js::rust::Runtime; use layout_interface::{LayoutChan, LayoutRPC}; use libc; -use msg::constellation_msg::ConstellationChan; +use msg::constellation_msg::{ConstellationChan, ScriptMsg}; use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData, WorkerId}; use net_traits::Metadata; use net_traits::image::base::Image; @@ -275,7 +275,6 @@ no_jsmanaged_fields!(WorkerId); no_jsmanaged_fields!(QuirksMode); no_jsmanaged_fields!(Runtime); no_jsmanaged_fields!(Headers, Method); -no_jsmanaged_fields!(ConstellationChan); no_jsmanaged_fields!(LayoutChan); no_jsmanaged_fields!(WindowProxyHandler); no_jsmanaged_fields!(UntrustedNodeAddress); @@ -299,6 +298,13 @@ no_jsmanaged_fields!(AttrIdentifier); no_jsmanaged_fields!(AttrValue); no_jsmanaged_fields!(ElementSnapshot); +impl JSTraceable for ConstellationChan { + #[inline] + fn trace(&self, _trc: *mut JSTracer) { + // Do nothing + } +} + impl JSTraceable for Box { #[inline] fn trace(&self, _trc: *mut JSTracer) { diff --git a/servo/components/script/layout_interface.rs b/servo/components/script/layout_interface.rs index cf9bae13e3b0..4a78e23e23c5 100644 --- a/servo/components/script/layout_interface.rs +++ b/servo/components/script/layout_interface.rs @@ -29,6 +29,8 @@ use string_cache::Atom; use style::animation::PropertyAnimation; use style::stylesheets::Stylesheet; use url::Url; +use util::ipc::OptionalOpaqueIpcSender; + pub use dom::node::TrustedNodeAddress; /// Asynchronous messages that script can send to layout. @@ -254,8 +256,9 @@ pub struct NewLayoutTaskInfo { pub pipeline_port: IpcReceiver, pub constellation_chan: ConstellationChan, pub failure: Failure, - pub script_chan: Sender, + pub script_chan: IpcSender, pub image_cache_task: ImageCacheTask, - pub paint_chan: Box, - pub layout_shutdown_chan: Sender<()>, + pub paint_chan: OptionalOpaqueIpcSender, + pub layout_shutdown_chan: IpcSender<()>, + pub content_process_shutdown_chan: IpcSender<()>, } diff --git a/servo/components/script/script_task.rs b/servo/components/script/script_task.rs index 5f881601bd76..043c0574be13 100644 --- a/servo/components/script/script_task.rs +++ b/servo/components/script/script_task.rs @@ -104,7 +104,7 @@ use time::{Tm, now}; use url::{Url, UrlParser}; use util::opts; use util::str::DOMString; -use util::task::spawn_named_with_send_on_failure; +use util::task; use util::task_state; use webdriver_handlers; @@ -232,8 +232,9 @@ pub enum ScriptTaskEventCategory { Resize, ScriptEvent, TimerEvent, - UpdateReplacedElement, SetViewport, + StylesheetLoad, + UpdateReplacedElement, WebSocketEvent, WorkerEvent, } @@ -396,7 +397,7 @@ pub struct ScriptTask { chan: MainThreadScriptChan, /// A channel to hand out to tasks that need to respond to a message from the script task. - control_chan: Sender, + control_chan: IpcSender, /// The port on which the constellation and layout tasks can communicate with the /// script task. @@ -438,6 +439,8 @@ pub struct ScriptTask { scheduler_chan: IpcSender, timer_event_chan: Sender, timer_event_port: Receiver, + + content_process_shutdown_chan: IpcSender<()>, } /// In the event of task failure, all data on the stack runs its destructor. However, there @@ -484,7 +487,8 @@ impl ScriptTaskFactory for ScriptTask { ScriptLayoutChan::new(chan, port) } - fn clone_layout_channel(_phantom: Option<&mut ScriptTask>, pair: &OpaqueScriptLayoutChannel) -> Box { + fn clone_layout_channel(_phantom: Option<&mut ScriptTask>, pair: &OpaqueScriptLayoutChannel) + -> Box { box pair.sender() as Box } @@ -496,9 +500,10 @@ impl ScriptTaskFactory for ScriptTask { let (script_chan, script_port) = channel(); let layout_chan = LayoutChan(layout_chan.sender()); let failure_info = state.failure_info; - spawn_named_with_send_on_failure(format!("ScriptTask {:?}", state.id), task_state::SCRIPT, move || { + task::spawn_named_with_send_on_failure(format!("ScriptTask {:?}", state.id), + task_state::SCRIPT, + move || { PipelineNamespace::install(state.pipeline_namespace_id); - let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); let chan = MainThreadScriptChan(script_chan); @@ -524,6 +529,7 @@ impl ScriptTaskFactory for ScriptTask { let reporter_name = format!("script-reporter-{}", id); mem_profiler_chan.run_with_memory_reporting(|| { script_task.start(); + let _ = script_task.content_process_shutdown_chan.send(()); }, reporter_name, channel_for_reporter, CommonScriptMsg::CollectReports); // This must always be the very last operation performed before the task completes @@ -636,6 +642,9 @@ impl ScriptTask { let (timer_event_chan, timer_event_port) = channel(); + // Ask the router to proxy IPC messages from the control port to us. + let control_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(state.control_port); + ScriptTask { page: DOMRefCell::new(None), incomplete_loads: DOMRefCell::new(vec!()), @@ -650,7 +659,7 @@ impl ScriptTask { port: port, chan: chan, control_chan: state.control_chan, - control_port: state.control_port, + control_port: control_port, constellation_chan: state.constellation_chan, compositor: DOMRefCell::new(state.compositor), time_profiler_chan: state.time_profiler_chan, @@ -667,6 +676,8 @@ impl ScriptTask { scheduler_chan: state.scheduler_chan, timer_event_chan: timer_event_chan, timer_event_port: timer_event_port, + + content_process_shutdown_chan: state.content_process_shutdown_chan, } } @@ -940,7 +951,10 @@ impl ScriptTask { ScriptTaskEventCategory::NetworkEvent => ProfilerCategory::ScriptNetworkEvent, ScriptTaskEventCategory::Resize => ProfilerCategory::ScriptResize, ScriptTaskEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent, - ScriptTaskEventCategory::UpdateReplacedElement => ProfilerCategory::ScriptUpdateReplacedElement, + ScriptTaskEventCategory::UpdateReplacedElement => { + ProfilerCategory::ScriptUpdateReplacedElement + } + ScriptTaskEventCategory::StylesheetLoad => ProfilerCategory::ScriptStylesheetLoad, ScriptTaskEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport, ScriptTaskEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent, ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent, @@ -1185,6 +1199,7 @@ impl ScriptTask { failure, pipeline_port, layout_shutdown_chan, + content_process_shutdown_chan, } = new_layout_info; let layout_pair = ScriptTask::create_layout_channel(None::<&mut ScriptTask>); @@ -1204,6 +1219,7 @@ impl ScriptTask { script_chan: self.control_chan.clone(), image_cache_task: self.image_cache_task.clone(), layout_shutdown_chan: layout_shutdown_chan, + content_process_shutdown_chan: content_process_shutdown_chan, }; let page = self.root_page(); diff --git a/servo/components/script_traits/lib.rs b/servo/components/script_traits/lib.rs index 337f07fda056..995045e6e17f 100644 --- a/servo/components/script_traits/lib.rs +++ b/servo/components/script_traits/lib.rs @@ -41,7 +41,7 @@ use net_traits::image_cache_task::ImageCacheTask; use net_traits::storage_task::StorageTask; use profile_traits::mem; use std::any::Any; -use std::sync::mpsc::{Receiver, Sender}; +use util::ipc::OptionalOpaqueIpcSender; use util::mem::HeapSizeOf; /// The address of a node. Layout sends these back. They must be validated via @@ -68,6 +68,7 @@ pub enum LayoutControlMsg { } /// The initial data associated with a newly-created framed pipeline. +#[derive(Deserialize, Serialize)] pub struct NewLayoutInfo { /// Id of the parent of this new pipeline. pub containing_pipeline_id: PipelineId, @@ -77,21 +78,21 @@ pub struct NewLayoutInfo { pub subpage_id: SubpageId, /// Network request data which will be initiated by the script task. pub load_data: LoadData, - /// The paint channel, cast to `Box`. - /// - /// TODO(pcwalton): When we convert this to use IPC, this will need to become an - /// `IpcAnySender`. - pub paint_chan: Box, + /// The paint channel, cast to `OptionalOpaqueIpcSender`. This is really an + /// `Sender`. + pub paint_chan: OptionalOpaqueIpcSender, /// Information on what to do on task failure. pub failure: Failure, /// A port on which layout can receive messages from the pipeline. pub pipeline_port: IpcReceiver, /// A shutdown channel so that layout can notify others when it's done. - pub layout_shutdown_chan: Sender<()>, + pub layout_shutdown_chan: IpcSender<()>, + /// A shutdown channel so that layout can tell the content process to shut down when it's done. + pub content_process_shutdown_chan: IpcSender<()>, } /// Used to determine if a script has any pending asynchronous activity. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum ScriptState { /// The document has been loaded. DocumentLoaded, @@ -100,6 +101,7 @@ pub enum ScriptState { } /// Messages sent from the constellation or layout to the script task. +#[derive(Deserialize, Serialize)] pub enum ConstellationControlMsg { /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent AttachLayout(NewLayoutInfo), @@ -135,11 +137,11 @@ pub enum ConstellationControlMsg { /// reflowed. WebFontLoaded(PipelineId), /// Get the current state of the script task for a given pipeline. - GetCurrentState(Sender, PipelineId), + GetCurrentState(IpcSender, PipelineId), } /// The mouse button involved in the event. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] pub enum MouseButton { /// The left mouse button. Left, @@ -150,7 +152,7 @@ pub enum MouseButton { } /// The type of input represented by a multi-touch event. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] pub enum TouchEventType { /// A new touch point came in contact with the screen. Down, @@ -165,10 +167,11 @@ pub enum TouchEventType { /// An opaque identifier for a touch point. /// /// http://w3c.github.io/touch-events/#widl-Touch-identifier -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct TouchId(pub i32); /// Events from the compositor that the script task needs to know about +#[derive(Deserialize, Serialize)] pub enum CompositorEvent { /// The window was resized. ResizeEvent(WindowSizeData), @@ -250,9 +253,9 @@ pub struct InitialScriptState { /// The compositor. pub compositor: IpcSender, /// A channel with which messages can be sent to us (the script task). - pub control_chan: Sender, + pub control_chan: IpcSender, /// A port on which messages sent by the constellation to script can be received. - pub control_port: Receiver, + pub control_port: IpcReceiver, /// A channel on which messages can be sent to the constellation from script. pub constellation_chan: ConstellationChan, /// A channel to schedule timer events. @@ -275,8 +278,14 @@ pub struct InitialScriptState { pub window_size: Option, /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, + /// A ping will be sent on this channel once the script thread shuts down. + pub content_process_shutdown_chan: IpcSender<()>, } +/// Encapsulates external communication with the script task. +#[derive(Clone, Deserialize, Serialize)] +pub struct ScriptControlChan(pub IpcSender); + /// This trait allows creating a `ScriptTask` without depending on the `script` /// crate. pub trait ScriptTaskFactory { diff --git a/servo/components/servo/Cargo.lock b/servo/components/servo/Cargo.lock index 598fcd0bff82..92b083fdf50f 100644 --- a/servo/components/servo/Cargo.lock +++ b/servo/components/servo/Cargo.lock @@ -11,11 +11,13 @@ dependencies = [ "devtools_traits 0.0.1", "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gfx_tests 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_app 0.0.1", "image 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout 0.0.1", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -261,6 +263,7 @@ dependencies = [ "core-text 0.1.0 (git+https://github.com/servo/core-text-rs)", "devtools_traits 0.0.1", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gfx_traits 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -268,6 +271,7 @@ dependencies = [ "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout_traits 0.0.1", + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -276,6 +280,8 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", + "serde 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_macros 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "style_traits 0.0.1", "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -600,6 +606,16 @@ dependencies = [ "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gaol" +version = "0.0.1" +source = "git+https://github.com/pcwalton/gaol#71865ff8a1824cbc1cbee4d388d56c5ba1b5ffc2" +dependencies = [ + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.16" diff --git a/servo/components/servo/Cargo.toml b/servo/components/servo/Cargo.toml index 6a8bb74972b4..b17a137a16c5 100644 --- a/servo/components/servo/Cargo.toml +++ b/servo/components/servo/Cargo.toml @@ -129,6 +129,12 @@ features = [ "serde_serialization" ] version = "0.3" features = ["plugins"] +[dependencies.gaol] +git = "https://github.com/pcwalton/gaol" + +[dependencies.ipc-channel] +git = "https://github.com/pcwalton/ipc-channel" + [dependencies.layers] git = "https://github.com/servo/rust-layers" features = ["plugins"] diff --git a/servo/components/servo/lib.rs b/servo/components/servo/lib.rs index c43a22f2b216..4e5af217b5ac 100644 --- a/servo/components/servo/lib.rs +++ b/servo/components/servo/lib.rs @@ -17,6 +17,8 @@ // The `Browser` is fed events from a generic type that implements the // `WindowMethods` trait. +extern crate gaol; + #[macro_use] extern crate util as _util; @@ -29,6 +31,7 @@ mod export { extern crate euclid; extern crate gfx; extern crate gleam; + extern crate ipc_channel; extern crate layers; extern crate layout; extern crate msg; @@ -48,22 +51,25 @@ extern crate libc; extern crate webdriver_server; #[cfg(feature = "webdriver")] -fn webdriver(port: u16, constellation: msg::constellation_msg::ConstellationChan) { - webdriver_server::start_server(port, constellation.clone()); +fn webdriver(port: u16, constellation: Sender) { + webdriver_server::start_server(port, constellation); } #[cfg(not(feature = "webdriver"))] -fn webdriver(_port: u16, _constellation: msg::constellation_msg::ConstellationChan) { } +fn webdriver(_port: u16, _constellation: Sender) { } use compositing::CompositorEventListener; use compositing::compositor_task::InitialCompositorState; use compositing::constellation::InitialConstellationState; +use compositing::pipeline::UnprivilegedPipelineContent; +use compositing::sandboxing; use compositing::windowing::WindowEvent; use compositing::windowing::WindowMethods; use compositing::{CompositorProxy, CompositorTask, Constellation}; +use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; use gfx::font_cache_task::FontCacheTask; +use ipc_channel::ipc::{self, IpcSender}; use msg::constellation_msg::CompositorMsg as ConstellationMsg; -use msg::constellation_msg::ConstellationChan; use net::image_cache_task::new_image_cache_task; use net::resource_task::new_resource_task; use net::storage_task::StorageTaskFactory; @@ -86,6 +92,7 @@ pub use export::devtools_traits; pub use export::euclid; pub use export::gfx; pub use export::gleam::gl; +pub use export::ipc_channel; pub use export::layers; pub use export::layout; pub use export::msg; @@ -193,7 +200,7 @@ fn create_constellation(opts: opts::Opts, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option>, - supports_clipboard: bool) -> ConstellationChan { + supports_clipboard: bool) -> Sender { let resource_task = new_resource_task(opts.user_agent.clone(), devtools_chan.clone()); let image_cache_task = new_image_cache_task(resource_task.clone()); @@ -218,11 +225,33 @@ fn create_constellation(opts: opts::Opts, // Send the URL command to the constellation. match opts.url { Some(url) => { - let ConstellationChan(ref chan) = constellation_chan; - chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap(); + constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap(); }, None => () }; constellation_chan } + +/// Content process entry point. +pub fn run_content_process(token: String) { + let (unprivileged_content_sender, unprivileged_content_receiver) = + ipc::channel::().unwrap(); + let connection_bootstrap: IpcSender> = + IpcSender::connect(token).unwrap(); + connection_bootstrap.send(unprivileged_content_sender).unwrap(); + + let unprivileged_content = unprivileged_content_receiver.recv().unwrap(); + opts::set_defaults(unprivileged_content.opts()); + + // Enter the sandbox if necessary. + if opts::get().sandbox { + ChildSandbox::new(sandboxing::content_process_sandbox_profile()).activate().unwrap(); + } + + script::init(); + + unprivileged_content.start_all::(true); +} + diff --git a/servo/components/servo/main.rs b/servo/components/servo/main.rs index 45a64d747842..437df3108325 100644 --- a/servo/components/servo/main.rs +++ b/servo/components/servo/main.rs @@ -39,7 +39,7 @@ use offscreen_gl_context::GLContext; use servo::Browser; use servo::compositing::windowing::WindowEvent; use servo::net_traits::hosts; -use servo::util::opts; +use servo::util::opts::{self, ArgumentParsingResult}; use std::rc::Rc; #[cfg(not(target_os = "android"))] @@ -52,11 +52,17 @@ fn load_gl_when_headless() {} fn main() { // Parse the command line options and store them globally - opts::from_cmdline_args(&*args()); + let opts_result = opts::from_cmdline_args(&*args()); - if opts::get().is_running_problem_test && ::std::env::var("RUST_LOG").is_err() { - ::std::env::set_var("RUST_LOG", "compositing::constellation"); - } + let content_process_token = if let ArgumentParsingResult::ContentProcess(token) = opts_result { + Some(token) + } else { + if opts::get().is_running_problem_test && ::std::env::var("RUST_LOG").is_err() { + ::std::env::set_var("RUST_LOG", "compositing::constellation"); + } + + None + }; env_logger::init().unwrap(); @@ -65,6 +71,10 @@ fn main() { // Possibly interpret the `HOST_FILE` environment variable hosts::global_init(); + if let Some(token) = content_process_token { + return servo::run_content_process(token) + } + let window = if opts::get().headless { // Load gl functions even when in headless mode, // to avoid crashing with webgl diff --git a/servo/components/util/ipc.rs b/servo/components/util/ipc.rs index 20c80cf042bb..be0196abdaf8 100644 --- a/servo/components/util/ipc.rs +++ b/servo/components/util/ipc.rs @@ -2,18 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc::{self, IpcSender, OpaqueIpcSender}; use ipc_channel::router::ROUTER; use opts; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::any::Any; +use std::any::{Any, TypeId}; use std::collections::HashMap; +use std::marker::Reflect; +use std::mem; use std::sync::Mutex; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::sync::mpsc::{self, Receiver, Sender}; lazy_static! { - static ref IN_PROCESS_SENDERS: Mutex>> = + static ref IN_PROCESS_SENDERS: Mutex> = Mutex::new(HashMap::new()); } @@ -31,6 +33,17 @@ impl OptionalIpcSender where T: Deserialize + Serialize + Send + Any { OptionalIpcSender::InProcess(ref sender) => sender.send(value).map_err(|_| ()), } } + + pub fn to_opaque(self) -> OptionalOpaqueIpcSender { + match self { + OptionalIpcSender::OutOfProcess(ipc_sender) => { + OptionalOpaqueIpcSender::OutOfProcess(ipc_sender.to_opaque()) + } + OptionalIpcSender::InProcess(sender) => { + OptionalOpaqueIpcSender::InProcess(OpaqueSender::new(sender)) + } + } + } } impl Clone for OptionalIpcSender where T: Deserialize + Serialize + Send + Any { @@ -49,18 +62,13 @@ impl Clone for OptionalIpcSender where T: Deserialize + Serialize + Send + impl Deserialize for OptionalIpcSender where T: Deserialize + Serialize + Send + Any { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer { - if opts::get().multiprocess { + if opts::multiprocess() { return Ok(OptionalIpcSender::OutOfProcess(try!(Deserialize::deserialize( deserializer)))) } let id: usize = try!(Deserialize::deserialize(deserializer)); - let sender = (*IN_PROCESS_SENDERS.lock() - .unwrap() - .remove(&id) - .unwrap() - .downcast_ref::>() - .unwrap()).clone(); - Ok(OptionalIpcSender::InProcess(sender)) + let sender = IN_PROCESS_SENDERS.lock().unwrap().remove(&id).unwrap(); + Ok(OptionalIpcSender::InProcess(sender.to().unwrap())) } } @@ -72,16 +80,91 @@ impl Serialize for OptionalIpcSender where T: Deserialize + Serialize + Se let id = NEXT_SENDER_ID.fetch_add(1, Ordering::SeqCst); IN_PROCESS_SENDERS.lock() .unwrap() - .insert(id, Box::new((*sender).clone()) as Box); + .insert(id, OpaqueSender::new((*sender).clone())); id.serialize(serializer) } } } } +#[derive(Clone)] +pub enum OptionalOpaqueIpcSender { + OutOfProcess(OpaqueIpcSender), + InProcess(OpaqueSender), +} + +impl OptionalOpaqueIpcSender { + pub fn to(self) -> OptionalIpcSender + where T: Deserialize + Serialize + Send + Any + 'static { + match self { + OptionalOpaqueIpcSender::OutOfProcess(ipc_sender) => { + OptionalIpcSender::OutOfProcess(ipc_sender.to()) + } + OptionalOpaqueIpcSender::InProcess(sender) => { + OptionalIpcSender::InProcess(sender.to().unwrap()) + } + } + } +} + +impl Deserialize for OptionalOpaqueIpcSender { + fn deserialize(deserializer: &mut D) + -> Result where D: Deserializer { + if opts::multiprocess() { + return Ok(OptionalOpaqueIpcSender::OutOfProcess(try!(Deserialize::deserialize( + deserializer)))) + } + let id: usize = try!(Deserialize::deserialize(deserializer)); + let sender = IN_PROCESS_SENDERS.lock().unwrap().remove(&id).unwrap(); + Ok(OptionalOpaqueIpcSender::InProcess(sender)) + } +} + +impl Serialize for OptionalOpaqueIpcSender { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { + match *self { + OptionalOpaqueIpcSender::OutOfProcess(ref ipc_sender) => { + ipc_sender.serialize(serializer) + } + OptionalOpaqueIpcSender::InProcess(ref sender) => { + let id = NEXT_SENDER_ID.fetch_add(1, Ordering::SeqCst); + IN_PROCESS_SENDERS.lock().unwrap().insert(id, (*sender).clone()); + id.serialize(serializer) + } + } + } +} + +#[derive(Clone)] +pub struct OpaqueSender { + sender: Sender<()>, + id: TypeId, +} + +impl OpaqueSender { + fn new(sender: Sender) -> OpaqueSender where T: 'static + Reflect + Send { + unsafe { + OpaqueSender { + sender: mem::transmute::<_, Sender<()>>(sender), + id: TypeId::of::(), + } + } + } + + fn to(self) -> Option> where T: 'static + Reflect + Send { + unsafe { + if self.id != TypeId::of::() { + None + } else { + Some(mem::transmute::<_, Sender>(self.sender)) + } + } + } +} + pub fn optional_ipc_channel() -> (OptionalIpcSender, Receiver) where T: Deserialize + Serialize + Send + Any { - if opts::get().multiprocess { + if opts::multiprocess() { let (ipc_sender, ipc_receiver) = ipc::channel().unwrap(); let receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_receiver); (OptionalIpcSender::OutOfProcess(ipc_sender), receiver) diff --git a/servo/components/util/lib.rs b/servo/components/util/lib.rs index 595a6b54e20e..82fcf7ab62b2 100644 --- a/servo/components/util/lib.rs +++ b/servo/components/util/lib.rs @@ -14,6 +14,7 @@ #![feature(optin_builtin_traits)] #![cfg_attr(not(target_os = "android"), feature(path_ext))] #![feature(plugin)] +#![feature(reflect_marker)] #![feature(slice_splits)] #![feature(step_by)] #![feature(step_trait)] diff --git a/servo/components/util/opts.rs b/servo/components/util/opts.rs index ce8c75cd045a..761568de42df 100644 --- a/servo/components/util/opts.rs +++ b/servo/components/util/opts.rs @@ -18,10 +18,11 @@ use std::fs::File; use std::io::{self, Read, Write}; use std::path::Path; use std::process; +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; use url::{self, Url}; /// Global flags for Servo, currently set on the command line. -#[derive(Clone)] +#[derive(Clone, Deserialize, Serialize)] pub struct Opts { pub is_running_problem_test: bool, @@ -143,9 +144,12 @@ pub struct Opts { /// An optional string allowing the user agent to be set for testing. pub user_agent: String, - /// Whether to run in multiprocess mode. + /// Whether we're running in multiprocess mode. pub multiprocess: bool, + /// Whether we're running inside the sandbox. + pub sandbox: bool, + /// Dumps the flow tree after a layout. pub dump_flow_tree: bool, @@ -375,6 +379,13 @@ static FORCE_CPU_PAINTING: bool = true; #[cfg(not(target_os = "android"))] static FORCE_CPU_PAINTING: bool = false; +static MULTIPROCESS: AtomicBool = ATOMIC_BOOL_INIT; + +#[inline] +pub fn multiprocess() -> bool { + MULTIPROCESS.load(Ordering::Relaxed) +} + enum UserAgent { Desktop, Android, @@ -460,6 +471,7 @@ pub fn default_opts() -> Opts { initial_window_size: Size2D::typed(800, 600), user_agent: default_user_agent_string(DEFAULT_USER_AGENT), multiprocess: false, + sandbox: false, dump_flow_tree: false, dump_display_list: false, dump_display_list_json: false, @@ -479,7 +491,7 @@ pub fn default_opts() -> Opts { } } -pub fn from_cmdline_args(args: &[String]) { +pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { let (app_name, args) = args.split_first().unwrap(); let mut opts = Options::new(); @@ -509,11 +521,14 @@ pub fn from_cmdline_args(args: &[String]) { "Set custom user agent string (or android / gonk / desktop for platform default)", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"); opts.optflag("M", "multiprocess", "Run in multiprocess mode"); + opts.optflag("S", "sandbox", "Run in a sandbox if multiprocess"); opts.optopt("Z", "debug", "A comma-separated string of debug options. Pass help to show available options.", ""); opts.optflag("h", "help", "Print this message"); opts.optopt("", "resources-path", "Path to find static resources", "/home/servo/resources"); opts.optflag("", "sniff-mime-types" , "Enable MIME sniffing"); + opts.optopt("", "content-process" , "Run as a content process and connect to the given pipe", + "servo-ipc-channel.abcdefg"); opts.optmulti("", "pref", "A preference to set to enable", "dom.mozbrowser.enabled"); opts.optflag("b", "no-native-titlebar", "Do not use native titlebar"); @@ -530,6 +545,13 @@ pub fn from_cmdline_args(args: &[String]) { process::exit(0); }; + // If this is the content process, we'll receive the real options over IPC. So just fill in + // some dummy options for now. + if let Some(content_process) = opt_match.opt_str("content-process") { + MULTIPROCESS.store(true, Ordering::SeqCst); + return ArgumentParsingResult::ContentProcess(content_process); + } + let debug_string = match opt_match.opt_str("Z") { Some(string) => string, None => String::new() @@ -627,6 +649,10 @@ pub fn from_cmdline_args(args: &[String]) { } }; + if opt_match.opt_present("M") { + MULTIPROCESS.store(true, Ordering::SeqCst) + } + let user_agent = match opt_match.opt_str("u") { Some(ref ua) if ua == "android" => default_user_agent_string(UserAgent::Android), Some(ref ua) if ua == "gonk" => default_user_agent_string(UserAgent::Gonk), @@ -675,6 +701,7 @@ pub fn from_cmdline_args(args: &[String]) { initial_window_size: initial_window_size, user_agent: user_agent, multiprocess: opt_match.opt_present("M"), + sandbox: opt_match.opt_present("S"), show_debug_borders: debug_options.show_compositor_borders, show_debug_fragment_borders: debug_options.show_fragment_borders, show_debug_parallel_paint: debug_options.show_parallel_paint, @@ -704,6 +731,22 @@ pub fn from_cmdline_args(args: &[String]) { for pref in opt_match.opt_strs("pref").iter() { prefs::set_pref(pref, PrefValue::Boolean(true)); } + + ArgumentParsingResult::ChromeProcess +} + +pub enum ArgumentParsingResult { + ChromeProcess, + ContentProcess(String), +} + +static EXPERIMENTAL_ENABLED: AtomicBool = ATOMIC_BOOL_INIT; + +/// Turn on experimental features globally. Normally this is done +/// during initialization by `set` or `from_cmdline_args`, but +/// tests that require experimental features will also set it. +pub fn set_experimental_enabled(new_value: bool) { + EXPERIMENTAL_ENABLED.store(new_value, Ordering::SeqCst); } // Make Opts available globally. This saves having to clone and pass diff --git a/servo/components/util/str.rs b/servo/components/util/str.rs index de3b5eb42b91..9978464ee0df 100644 --- a/servo/components/util/str.rs +++ b/servo/components/util/str.rs @@ -558,7 +558,7 @@ pub fn parse_legacy_color(mut input: &str) -> Result { } -#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)] pub struct LowercaseString { inner: String, } diff --git a/servo/components/util/task.rs b/servo/components/util/task.rs index 2495bb8acd5d..c9b9463ed294 100644 --- a/servo/components/util/task.rs +++ b/servo/components/util/task.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use ipc_channel::ipc::IpcSender; +use serde::Serialize; use std::borrow::ToOwned; use std::sync::mpsc::Sender; use std::thread; @@ -15,15 +17,36 @@ pub fn spawn_named(name: String, f: F) builder.spawn(f).unwrap(); } +/// An abstraction over `Sender` and `IpcSender`, for use in +/// `spawn_named_with_send_on_failure`. +pub trait SendOnFailure { + type Value; + fn send_on_failure(&mut self, value: Self::Value); +} + +impl SendOnFailure for Sender where T: Send + 'static { + type Value = T; + fn send_on_failure(&mut self, value: T) { + self.send(value).unwrap(); + } +} + +impl SendOnFailure for IpcSender where T: Send + Serialize + 'static { + type Value = T; + fn send_on_failure(&mut self, value: T) { + self.send(value).unwrap(); + } +} + /// Arrange to send a particular message to a channel if the task fails. -pub fn spawn_named_with_send_on_failure(name: String, - state: task_state::TaskState, - f: F, - msg: T, - dest: Sender) - where F: FnOnce() + Send + 'static, - T: Send + 'static -{ +pub fn spawn_named_with_send_on_failure(name: String, + state: task_state::TaskState, + f: F, + msg: T, + mut dest: S) + where F: FnOnce() + Send + 'static, + T: Send + 'static, + S: Send + SendOnFailure + 'static { let future_handle = thread::Builder::new().name(name.to_owned()).spawn(move || { task_state::initialize(state); f() @@ -35,8 +58,9 @@ pub fn spawn_named_with_send_on_failure(name: String, Ok(()) => (), Err(..) => { debug!("{} failed, notifying constellation", name); - dest.send(msg).unwrap(); + dest.send_on_failure(msg); } } }).unwrap(); } + diff --git a/servo/components/webdriver_server/lib.rs b/servo/components/webdriver_server/lib.rs index a899da65137e..2abdb5621148 100644 --- a/servo/components/webdriver_server/lib.rs +++ b/servo/components/webdriver_server/lib.rs @@ -28,7 +28,7 @@ use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use keys::keycodes_to_keys; use msg::constellation_msg::CompositorMsg as ConstellationMsg; -use msg::constellation_msg::{ConstellationChan, FrameId, LoadData, PipelineId}; +use msg::constellation_msg::{FrameId, LoadData, PipelineId}; use msg::constellation_msg::{NavigationDirection, PixelFormat, WebDriverCommandMsg}; use msg::webdriver_msg::{LoadStatus, WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverScriptCommand}; use regex::Captures; @@ -37,6 +37,7 @@ use rustc_serialize::json::{Json, ToJson}; use std::borrow::ToOwned; use std::collections::BTreeMap; use std::net::SocketAddr; +use std::sync::mpsc::Sender; use std::thread::{self, sleep_ms}; use url::Url; use util::prefs::{get_pref, reset_all_prefs, reset_pref, set_pref, PrefValue}; @@ -57,7 +58,7 @@ fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> { (Post, "/session/{sessionId}/servo/prefs/reset", ServoExtensionRoute::ResetPrefs)] } -pub fn start_server(port: u16, constellation_chan: ConstellationChan) { +pub fn start_server(port: u16, constellation_chan: Sender) { let handler = Handler::new(constellation_chan); spawn_named("WebdriverHttpServer".to_owned(), move || { server::start(SocketAddr::new("0.0.0.0".parse().unwrap(), port), handler, @@ -72,7 +73,7 @@ struct WebDriverSession { struct Handler { session: Option, - constellation_chan: ConstellationChan, + constellation_chan: Sender, script_timeout: u32, load_timeout: u32, implicit_wait_timeout: u32 @@ -208,7 +209,7 @@ impl WebDriverSession { } impl Handler { - pub fn new(constellation_chan: ConstellationChan) -> Handler { + pub fn new(constellation_chan: Sender) -> Handler { Handler { session: None, constellation_chan: constellation_chan, @@ -267,8 +268,7 @@ impl Handler { fn pipeline(&self, frame_id: Option) -> Option { let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::GetPipeline(frame_id, sender)).unwrap(); + self.constellation_chan.send(ConstellationMsg::GetPipeline(frame_id, sender)).unwrap(); receiver.recv().unwrap() @@ -307,9 +307,8 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); let load_data = LoadData::new(url); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::LoadUrl(pipeline_id, load_data, sender.clone()); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); self.wait_for_load(sender, receiver) } @@ -337,10 +336,9 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, WebDriverScriptCommand::GetUrl(sender)); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); let url = receiver.recv().unwrap(); @@ -348,14 +346,12 @@ impl Handler { } fn handle_go_back(&self) -> WebDriverResult { - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Back)).unwrap(); + self.constellation_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Back)).unwrap(); Ok(WebDriverResponse::Void) } fn handle_go_forward(&self) -> WebDriverResult { - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Forward)).unwrap(); + self.constellation_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Forward)).unwrap(); Ok(WebDriverResponse::Void) } @@ -364,9 +360,8 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::Refresh(pipeline_id, sender.clone()); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); self.wait_for_load(sender, receiver) } @@ -375,10 +370,9 @@ impl Handler { let pipeline_id = try!(self.root_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, WebDriverScriptCommand::GetTitle(sender)); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); let value = receiver.recv().unwrap(); Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))) } @@ -406,10 +400,9 @@ impl Handler { } let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => { let value_resp = value.map(|x| WebElement::new(x).to_json()).to_json(); @@ -448,16 +441,14 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetFrameId(frame_id, sender); { - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::WebDriverCommand( + self.constellation_chan.send(ConstellationMsg::WebDriverCommand( WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd))).unwrap(); } let frame = match receiver.recv().unwrap() { Ok(Some(pipeline_id)) => { let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::GetFrame(pipeline_id, sender)).unwrap(); + self.constellation_chan.send(ConstellationMsg::GetFrame(pipeline_id, sender)).unwrap(); receiver.recv().unwrap() }, Ok(None) => None, @@ -481,10 +472,9 @@ impl Handler { } let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => { let resp_value: Vec = value.into_iter().map( @@ -500,10 +490,9 @@ impl Handler { let pipeline_id = try!(self.frame_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::GetElementText(element.id.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -515,10 +504,9 @@ impl Handler { let pipeline_id = try!(self.frame_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::GetActiveElement(sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); let value = receiver.recv().unwrap().map(|x| WebElement::new(x).to_json()); Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))) } @@ -527,10 +515,9 @@ impl Handler { let pipeline_id = try!(self.frame_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -542,10 +529,9 @@ impl Handler { let pipeline_id = try!(self.frame_pipeline()); let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd = WebDriverScriptCommand::GetElementAttribute(element.id.clone(), name.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -601,9 +587,8 @@ impl Handler { -> WebDriverResult { let pipeline_id = try!(self.frame_pipeline()); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, command); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), @@ -618,12 +603,11 @@ impl Handler { keys: &SendKeysParameters) -> WebDriverResult { let pipeline_id = try!(self.frame_pipeline()); - let ConstellationChan(ref const_chan) = self.constellation_chan; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::FocusElement(element.id.clone(), sender); let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); // TODO: distinguish the not found and not focusable cases try!(receiver.recv().unwrap().or_else(|_| Err(WebDriverError::new( @@ -633,7 +617,7 @@ impl Handler { Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, "Failed to convert keycodes")))); let cmd_msg = WebDriverCommandMsg::SendKeys(pipeline_id, keys); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); Ok(WebDriverResponse::Void) } @@ -647,9 +631,8 @@ impl Handler { for _ in 0..iterations { let (sender, receiver) = ipc::channel().unwrap(); - let ConstellationChan(ref const_chan) = self.constellation_chan; let cmd_msg = WebDriverCommandMsg::TakeScreenshot(pipeline_id, sender); - const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); + self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); if let Some(x) = receiver.recv().unwrap() { img = Some(x); diff --git a/servo/ports/cef/Cargo.lock b/servo/ports/cef/Cargo.lock index 4d5970755c44..7178aed16ca3 100644 --- a/servo/ports/cef/Cargo.lock +++ b/servo/ports/cef/Cargo.lock @@ -253,6 +253,7 @@ dependencies = [ "core-text 0.1.0 (git+https://github.com/servo/core-text-rs)", "devtools_traits 0.0.1", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gfx_traits 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -260,6 +261,7 @@ dependencies = [ "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout_traits 0.0.1", + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -268,6 +270,8 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", + "serde 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_macros 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "style_traits 0.0.1", "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -559,6 +563,16 @@ dependencies = [ "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gaol" +version = "0.0.1" +source = "git+https://github.com/pcwalton/gaol#71865ff8a1824cbc1cbee4d388d56c5ba1b5ffc2" +dependencies = [ + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.16" @@ -1555,9 +1569,11 @@ dependencies = [ "devtools_traits 0.0.1", "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_app 0.0.1", + "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout 0.0.1", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/servo/ports/gonk/Cargo.lock b/servo/ports/gonk/Cargo.lock index 402e376b86e4..f94e3e5ccc15 100644 --- a/servo/ports/gonk/Cargo.lock +++ b/servo/ports/gonk/Cargo.lock @@ -245,6 +245,7 @@ dependencies = [ "core-text 0.1.0 (git+https://github.com/servo/core-text-rs)", "devtools_traits 0.0.1", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gfx_traits 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -252,6 +253,7 @@ dependencies = [ "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout_traits 0.0.1", + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -260,6 +262,8 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", + "serde 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_macros 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "style_traits 0.0.1", "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -561,6 +565,16 @@ dependencies = [ "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gaol" +version = "0.0.1" +source = "git+https://github.com/pcwalton/gaol#71865ff8a1824cbc1cbee4d388d56c5ba1b5ffc2" +dependencies = [ + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.16" @@ -1535,8 +1549,10 @@ dependencies = [ "devtools_traits 0.0.1", "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gaol 0.0.1 (git+https://github.com/pcwalton/gaol)", "gfx 0.0.1", "gleam 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout 0.0.1", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",