diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 3866bdaf26f1..58a477e85ae5 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -66,14 +66,20 @@ features = ["texture_surface"] version = "0.2" features = [ "serde_serialization" ] +[dependencies.gaol] +git = "https://github.com/pcwalton/gaol" + [dependencies] app_units = "0.1" image = "0.4.0" +libc = "0.1" log = "0.3" num = "0.1.24" time = "0.1.17" gleam = "0.1" euclid = "0.2" +serde = "0.6" +serde_macros = "0.5" [target.x86_64-apple-darwin.dependencies] core-graphics = "0.1" diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 3482d730be46..5696c1ab8203 100644 --- a/components/compositing/compositor.rs +++ b/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}; @@ -44,7 +44,6 @@ use std::fs::File; use std::mem as std_mem; use std::rc::Rc; use std::slice::bytes::copy_memory; -use std::sync::mpsc::Sender; use style_traits::viewport::ViewportConstraints; use surface_map::SurfaceMap; use time::{precise_time_ns, precise_time_s}; @@ -674,7 +673,7 @@ impl IOCompositor { fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree, - response_chan: Sender<()>, + response_chan: IpcSender<()>, new_constellation_chan: ConstellationChan) { response_chan.send(()).unwrap(); diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index b9d73c6b25da..c30e98554547 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -5,9 +5,10 @@ //! 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}; @@ -62,11 +63,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) => { @@ -82,7 +83,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(); } @@ -154,7 +155,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 @@ -182,7 +183,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<()>, ConstellationChan), /// 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). diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index d0c5003f9086..8a4bc6b10c21 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -18,8 +18,10 @@ use compositor_task::Msg as CompositorMsg; 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, IpcReceiver, IpcSender}; use layout_traits::{LayoutControlChan, LayoutTaskFactory}; use msg::compositor_msg::Epoch; use msg::constellation_msg::AnimationState; @@ -36,19 +38,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}; use style_traits::viewport::ViewportConstraints; use timer_scheduler::TimerScheduler; use url::Url; @@ -68,7 +72,7 @@ pub struct Constellation { pub chan: ConstellationChan, /// Receives messages. - pub request_port: Receiver, + pub request_port: IpcReceiver, /// A channel (the implementation of which is port-specific) through which messages can be sent /// to the compositor. @@ -138,7 +142,10 @@ pub struct Constellation { /// A list of in-process senders to `WebGLPaintTask`s. webgl_paint_tasks: Vec>, - scheduler_chan: Sender, + scheduler_chan: IpcSender, + + /// A list of child content processes. + child_processes: Vec, } /// State needed to construct a constellation. @@ -242,6 +249,11 @@ enum ExitPipelineMode { Force, } +enum ChildProcess { + Sandboxed(gaol::platform::process::Process), + Unsandboxed(process::Child), +} + impl Constellation { pub fn start(state: InitialConstellationState) -> ConstellationChan { let (constellation_port, constellation_chan) = ConstellationChan::new(); @@ -285,6 +297,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); @@ -314,10 +327,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, @@ -338,12 +351,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)); @@ -1205,7 +1245,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(); @@ -1367,7 +1407,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(CompositorMsg::SetFrameTree(frame_tree, chan, self.chan.clone())); diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index a38f1d8490fa..fa2ad24fd60c 100644 --- a/components/compositing/lib.rs +++ b/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/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index ad425224d265..7adca879e783 100644 --- a/components/compositing/pipeline.rs +++ b/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::{ConstellationChan, Failure, FrameId, PipelineId, SubpageId}; use msg::constellation_msg::{LoadData, MozBrowserEvent, PipelineExitType, WindowSizeData}; use msg::constellation_msg::{PipelineNamespaceId}; @@ -25,7 +26,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; @@ -33,20 +33,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. @@ -62,12 +63,15 @@ 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, } /// Initial setup data needed to construct a pipeline. +/// +/// *DO NOT* add any Senders to this unless you absolutely know what you're doing, or pcwalton will +/// have to rewrite your code. Use IPC senders instead. pub struct InitialPipelineState { /// The ID of the pipeline to create. pub id: PipelineId, @@ -77,7 +81,7 @@ pub struct InitialPipelineState { /// A channel to the associated constellation. pub constellation_chan: ConstellationChan, /// A channel to schedule timer events. - pub scheduler_chan: Sender, + pub scheduler_chan: IpcSender, /// A channel to the compositor. pub compositor_proxy: Box, /// A channel to the developer tools, if applicable. @@ -100,7 +104,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. @@ -111,13 +115,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 { @@ -144,6 +149,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) = @@ -153,10 +161,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)) @@ -164,11 +173,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(), @@ -180,45 +192,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, + }; + + 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, pipeline_content) + (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 { @@ -316,13 +346,14 @@ impl Pipeline { } } -pub struct PipelineContent { +#[derive(Deserialize, Serialize)] +pub struct UnprivilegedPipelineContent { id: PipelineId, parent_info: Option<(PipelineId, SubpageId)>, constellation_chan: ConstellationChan, - scheduler_chan: Sender, - compositor_proxy: Box, + scheduler_chan: IpcSender, devtools_chan: Option>, + script_to_compositor_chan: IpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, resource_task: ResourceTask, @@ -330,39 +361,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(), @@ -376,6 +399,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>, @@ -392,7 +416,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/components/compositing/sandboxing.rs b/components/compositing/sandboxing.rs new file mode 100644 index 000000000000..dd02f878ae30 --- /dev/null +++ b/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/components/compositing/timer_scheduler.rs b/components/compositing/timer_scheduler.rs index d29137be7880..bbc6e307b0a1 100644 --- a/components/compositing/timer_scheduler.rs +++ b/components/compositing/timer_scheduler.rs @@ -3,6 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::length::Length; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; use num::traits::Saturating; use script_traits::{MsDuration, NsDuration, precise_time_ms, precise_time_ns}; use script_traits::{TimerEvent, TimerEventRequest}; @@ -11,7 +13,7 @@ use std::cmp::{self, Ord}; use std::collections::BinaryHeap; use std::sync::Arc; use std::sync::atomic::{self, AtomicBool}; -use std::sync::mpsc::{channel, Receiver, Select, Sender}; +use std::sync::mpsc::{channel, Receiver, Select}; use std::thread::{self, spawn, Thread}; use util::task::spawn_named; @@ -107,11 +109,11 @@ enum Task { } impl TimerScheduler { - pub fn start() -> Sender { - let (chan, port) = channel(); + pub fn start() -> IpcSender { + let (chan, port) = ipc::channel().unwrap(); let timer_scheduler = TimerScheduler { - port: port, + port: ROUTER.route_ipc_receiver_to_new_mpsc_receiver(port), scheduled_events: RefCell::new(BinaryHeap::new()), @@ -126,6 +128,7 @@ impl TimerScheduler { } fn run_event_loop(&self) { + loop { match self.receive_next_task() { Some(Task::HandleRequest(request)) => self.handle_request(request), diff --git a/components/gfx/font_cache_task.rs b/components/gfx/font_cache_task.rs index a568af35840e..a8f9b149e8a7 100644 --- a/components/gfx/font_cache_task.rs +++ b/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, @@ -276,14 +278,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 || { @@ -317,7 +319,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(); @@ -332,7 +334,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(); @@ -344,12 +346,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/components/gfx/font_template.rs b/components/gfx/font_template.rs index 843216a81bbf..45becf31d5c3 100644 --- a/components/gfx/font_template.rs +++ b/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/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index e33432caf577..c45ead7644e3 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -36,8 +36,7 @@ use std::sync::mpsc::{Receiver, Select, Sender, channel}; use url::Url; use util::geometry::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. @@ -540,7 +541,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/components/layout/layout_task.rs b/components/layout/layout_task.rs index 9c3a3e7ea4c6..cba2ab05dacd 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -57,13 +57,14 @@ use script::layout_interface::Animation; use script::layout_interface::{LayoutChan, LayoutRPC, OffsetParentResponse}; use script::layout_interface::{Msg, NewLayoutTaskInfo, Reflow, ReflowGoal, ReflowQueryType}; use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress}; -use script_traits::StylesheetLoadResponder; use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel}; +use script_traits::{StylesheetLoadResponder}; use selectors::parser::PseudoElement; use sequential; use serde_json; +use std; use std::borrow::ToOwned; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::mem::transmute; @@ -86,7 +87,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::LayoutNode; @@ -198,7 +199,7 @@ 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 we or others can send messages to ourselves. pub chan: LayoutChan, @@ -207,7 +208,7 @@ pub struct LayoutTask { pub constellation_chan: ConstellationChan, /// The channel on which messages can be sent to the script task. - pub script_chan: Sender, + pub script_chan: IpcSender, /// The channel on which messages can be sent to the painting task. pub paint_chan: OptionalIpcSender, @@ -249,17 +250,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_chan = LayoutChan(sender); @@ -283,6 +285,7 @@ impl LayoutTaskFactory for LayoutTask { }, reporter_name, layout_chan.0, Msg::CollectReports); } shutdown_chan.send(()).unwrap(); + content_process_shutdown_chan.send(()).unwrap(); }, ConstellationMsg::Failure(failure_msg), con_chan); } } @@ -320,7 +323,7 @@ impl<'a> DerefMut for RWGuard<'a> { 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 { @@ -341,7 +344,7 @@ impl LayoutTask { chan: LayoutChan, pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, - script_chan: Sender, + script_chan: IpcSender, paint_chan: OptionalIpcSender, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, @@ -371,7 +374,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)); @@ -379,7 +385,7 @@ impl LayoutTask { add_font_face_rules(user_or_user_agent_stylesheet, &stylist.device, &font_cache_task, - &font_cache_sender, + &ipc_font_cache_sender, &outstanding_web_fonts_counter); } @@ -401,7 +407,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, rw_data: Arc::new(Mutex::new( @@ -688,14 +694,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 @@ -766,10 +771,19 @@ impl LayoutTask { Some(environment_encoding), Origin::Author); - //TODO: mark critical subresources as blocking load as well (#5974) - self.script_chan.send(ConstellationControlMsg::StylesheetLoadComplete(self.id, - url, - responder)).unwrap(); + // TODO: mark critical subresources as blocking load as well (#5974) + // TODO(ipc-channel#9, pcwalton): Make an API on the router for this. This option dance is + // pretty ugly. + let (ipc_stylesheet_load_responder_chan, ipc_stylesheet_load_responder_port) = + ipc::channel().unwrap(); + let responder = RefCell::new(Some(responder)); + ROUTER.add_route(ipc_stylesheet_load_responder_port.to_opaque(), box move |_| { + std::mem::replace(&mut *responder.borrow_mut(), None).unwrap().respond(); + }); + self.script_chan.send(ConstellationControlMsg::StylesheetLoadComplete( + self.id, + url, + ipc_stylesheet_load_responder_chan)).unwrap(); self.handle_add_stylesheet(sheet, mq, possibly_locked_rw_data); } diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index 025713894e2c..a56028827526 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -28,7 +28,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; @@ -48,11 +47,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/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index a0548d308739..240f77505051 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -169,3 +169,4 @@ pub enum EventResult { DefaultAllowed, DefaultPrevented, } + diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 98fdcc052fbe..67f7ca63983c 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -11,13 +11,13 @@ 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 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,12 +25,12 @@ use util::geometry::{PagePx, ViewportPx}; use util::mem::HeapSizeOf; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; -#[derive(Clone)] -pub struct ConstellationChan(pub Sender); +#[derive(Clone, Deserialize, Serialize)] +pub struct ConstellationChan(pub IpcSender); impl ConstellationChan { - pub fn new() -> (Receiver, ConstellationChan) { - let (chan, port) = channel(); + pub fn new() -> (IpcReceiver, ConstellationChan) { + let (chan, port) = ipc::channel().unwrap(); (port, ConstellationChan(chan)) } } diff --git a/components/profile/time.rs b/components/profile/time.rs index 21acdba83669..47dd61086a91 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -93,6 +93,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::ScriptXhrEvent => "Script Xhr Event", diff --git a/components/profile_traits/mem.rs b/components/profile_traits/mem.rs index caf7ba8a7934..9fc78febdd69 100644 --- a/components/profile_traits/mem.rs +++ b/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/components/profile_traits/time.rs b/components/profile_traits/time.rs index 8cf485348f7e..fc82aa8f1c6b 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -62,15 +62,16 @@ pub enum ProfilerCategory { ScriptDevtoolsMsg, ScriptDocumentEvent, ScriptDomEvent, + ScriptEvent, ScriptFileRead, ScriptImageCacheMsg, ScriptInputEvent, ScriptNetworkEvent, ScriptResize, - ScriptEvent, - ScriptUpdateReplacedElement, ScriptSetViewport, ScriptTimerEvent, + ScriptStylesheetLoad, + ScriptUpdateReplacedElement, ScriptWebSocketEvent, ScriptWorkerEvent, ScriptXhrEvent, diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index a1a1c68c8c01..462f1079ef0c 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -23,7 +23,6 @@ use net_traits::ResourceTask; use profile_traits::mem; use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask}; use script_traits::TimerEventRequest; -use std::sync::mpsc::Sender; use url::Url; use util::mem::HeapSizeOf; @@ -99,7 +98,7 @@ impl<'a> GlobalRef<'a> { } /// Get the scheduler channel to request timer events. - pub fn scheduler_chan(&self) -> Sender { + pub fn scheduler_chan(&self) -> IpcSender { match *self { GlobalRef::Window(window) => window.scheduler_chan(), GlobalRef::Worker(worker) => worker.scheduler_chan(), diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 156239eb2732..e05e29cb0179 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -60,7 +60,7 @@ use net_traits::storage_task::StorageType; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_task::ScriptChan; -use script_traits::{TimerEventChan, TimerEventId, TimerSource, UntrustedNodeAddress}; +use script_traits::{TimerEventId, TimerSource, UntrustedNodeAddress}; use selectors::parser::PseudoElement; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -316,13 +316,6 @@ impl JSTraceable for Box { } } -impl JSTraceable for Box { - #[inline] - fn trace(&self, _trc: *mut JSTracer) { - // Do nothing - } -} - impl JSTraceable for Box { #[inline] fn trace(&self, _trc: *mut JSTracer) { diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 2c9a6f9f629c..562b871ada88 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -19,7 +19,7 @@ use dom::messageevent::MessageEvent; use dom::worker::{SimpleWorkerErrorHandler, TrustedWorkerAddress, WorkerMessageHandler}; use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::WorkerGlobalScopeInit; -use ipc_channel::ipc::IpcReceiver; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::{HandleValue, JSContext, RootedValue}; use js::jsapi::{JSAutoCompartment, JSAutoRequest}; @@ -30,7 +30,7 @@ use net_traits::load_whole_resource; use rand::random; use script_task::ScriptTaskEventCategory::WorkerEvent; use script_task::{ScriptTask, ScriptChan, ScriptPort, StackRootTLS, CommonScriptMsg}; -use script_traits::{TimerEvent, TimerEventChan, TimerSource}; +use script_traits::{TimerEvent, TimerSource}; use std::mem::replace; use std::rc::Rc; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; @@ -102,29 +102,6 @@ impl ScriptPort for Receiver<(TrustedWorkerAddress, WorkerScriptMsg)> { } } -/// A TimerEventChan that can be cloned freely and will silently send a TrustedWorkerAddress -/// with timer events. While this SendableWorkerScriptChan is alive, the associated Worker -/// object will remain alive. -struct WorkerThreadTimerEventChan { - sender: Sender<(TrustedWorkerAddress, TimerEvent)>, - worker: TrustedWorkerAddress, -} - -impl TimerEventChan for WorkerThreadTimerEventChan { - fn send(&self, event: TimerEvent) -> Result<(), ()> { - self.sender - .send((self.worker.clone(), event)) - .map_err(|_| ()) - } - - fn clone(&self) -> Box { - box WorkerThreadTimerEventChan { - sender: self.sender.clone(), - worker: self.worker.clone(), - } - } -} - /// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular /// value for the duration of this object's lifetime. This ensures that the related Worker /// object only lives as long as necessary (ie. while events are being executed), while @@ -182,7 +159,7 @@ impl DedicatedWorkerGlobalScope { parent_sender: Box, own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - timer_event_chan: Box, + timer_event_chan: IpcSender, timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>) -> DedicatedWorkerGlobalScope { @@ -206,7 +183,7 @@ impl DedicatedWorkerGlobalScope { parent_sender: Box, own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - timer_event_chan: Box, + timer_event_chan: IpcSender, timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>) -> Root { let scope = box DedicatedWorkerGlobalScope::new_inherited( @@ -248,15 +225,17 @@ impl DedicatedWorkerGlobalScope { ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan); let (timer_tx, timer_rx) = channel(); - let timer_event_chan = box WorkerThreadTimerEventChan { - sender: timer_tx, - worker: worker.clone(), - }; + let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); + let worker_for_route = worker.clone(); + ROUTER.add_route(timer_ipc_port.to_opaque(), box move |message| { + let event = message.to().unwrap(); + timer_tx.send((worker_for_route.clone(), event)).unwrap(); + }); let global = DedicatedWorkerGlobalScope::new( init, url, id, devtools_mpsc_port, runtime.clone(), parent_sender.clone(), own_sender, receiver, - timer_event_chan, timer_rx); + timer_ipc_chan, timer_rx); // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter // registration (#6631), so we instead use a random number and cross our fingers. let scope = global.upcast::(); diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index fc7a1818c004..c4b02a5924a9 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -24,6 +24,7 @@ use dom::virtualmethods::VirtualMethods; use layout_interface::{LayoutChan, Msg}; use msg::constellation_msg::ConstellationChan; use msg::constellation_msg::Msg as ConstellationMsg; +use script_task::{CommonScriptMsg, Runnable, ScriptChan, ScriptTaskEventCategory}; use script_traits::StylesheetLoadResponder; use std::ascii::AsciiExt; use std::borrow::ToOwned; @@ -165,8 +166,11 @@ impl HTMLLinkElement { let media = parse_media_query_list(&mut css_parser); let doc = window.Document(); - let link_element = Trusted::new(window.get_cx(), self, window.script_chan().clone()); - let load_dispatcher = StylesheetLoadDispatcher::new(link_element); + let link_element = Trusted::new(window.get_cx(), + self, + window.script_chan().clone()); + let load_dispatcher = + StylesheetLoadDispatcher::new(link_element, window.script_chan().clone()); let pending = doc.r().prepare_async_load(LoadType::Stylesheet(url.clone())); let LayoutChan(ref layout_chan) = window.layout_chan(); @@ -247,18 +251,31 @@ impl HTMLLinkElementMethods for HTMLLinkElement { pub struct StylesheetLoadDispatcher { elem: Trusted, + script_chan: Box, } impl StylesheetLoadDispatcher { - pub fn new(elem: Trusted) -> StylesheetLoadDispatcher { + pub fn new(elem: Trusted, script_chan: Box) + -> StylesheetLoadDispatcher { StylesheetLoadDispatcher { elem: elem, + script_chan: script_chan, } } } impl StylesheetLoadResponder for StylesheetLoadDispatcher { fn respond(self: Box) { + // Need to clone the script chan so it doesn't take out a borrow on self. + self.script_chan + .clone() + .send(CommonScriptMsg::RunnableMsg(ScriptTaskEventCategory::StylesheetLoad, self)) + .unwrap(); + } +} + +impl Runnable for StylesheetLoadDispatcher { + fn handler(self: Box) { let elem = self.elem.root(); let window = window_from_node(elem.r()); let event = Event::new(GlobalRef::Window(window.r()), "load".to_owned(), @@ -267,3 +284,4 @@ impl StylesheetLoadResponder for StylesheetLoadDispatcher { event.fire(elem.upcast::()); } } + diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index afae2923bf5e..99bb491eaf2b 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -53,8 +53,9 @@ use page::Page; use profile_traits::mem; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg}; -use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan}; -use script_traits::{ConstellationControlMsg, TimerEventChan, TimerEventId, TimerEventRequest, TimerSource}; +use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan}; +use script_traits::{ConstellationControlMsg, TimerEvent, TimerEventId, TimerEventRequest}; +use script_traits::{TimerSource}; use selectors::parser::PseudoElement; use std::ascii::AsciiExt; use std::borrow::ToOwned; @@ -110,7 +111,7 @@ pub struct Window { #[ignore_heap_size_of = "trait objects are hard"] script_chan: MainThreadScriptChan, #[ignore_heap_size_of = "channels are hard"] - control_chan: Sender, + control_chan: IpcSender, console: MutNullableHeap>, crypto: MutNullableHeap>, navigator: MutNullableHeap>, @@ -129,7 +130,7 @@ pub struct Window { session_storage: MutNullableHeap>, local_storage: MutNullableHeap>, #[ignore_heap_size_of = "channels are hard"] - scheduler_chan: Sender, + scheduler_chan: IpcSender, timers: ActiveTimers, next_worker_id: Cell, @@ -1091,7 +1092,7 @@ impl Window { self.constellation_chan.clone() } - pub fn scheduler_chan(&self) -> Sender { + pub fn scheduler_chan(&self) -> IpcSender { self.scheduler_chan.clone() } @@ -1228,7 +1229,7 @@ impl Window { page: Rc, script_chan: MainThreadScriptChan, image_cache_chan: ImageCacheChan, - control_chan: Sender, + control_chan: IpcSender, compositor: IpcSender, image_cache_task: ImageCacheTask, resource_task: Arc, @@ -1236,8 +1237,8 @@ impl Window { mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option>, constellation_chan: ConstellationChan, - scheduler_chan: Sender, - timer_event_chan: MainThreadTimerEventChan, + scheduler_chan: IpcSender, + timer_event_chan: IpcSender, layout_chan: LayoutChan, id: PipelineId, parent_info: Option<(PipelineId, SubpageId)>, @@ -1271,7 +1272,7 @@ impl Window { session_storage: Default::default(), local_storage: Default::default(), scheduler_chan: scheduler_chan.clone(), - timers: ActiveTimers::new(box timer_event_chan, scheduler_chan), + timers: ActiveTimers::new(timer_event_chan, scheduler_chan), next_worker_id: Cell::new(WorkerId(0)), id: id, parent_info: parent_info, diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index abf7f9352401..eb16b4c20525 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -24,11 +24,11 @@ use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId}; use net_traits::{ResourceTask, load_whole_resource}; use profile_traits::mem; use script_task::{CommonScriptMsg, ScriptChan, ScriptPort}; -use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource}; +use script_traits::{TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use std::cell::Cell; use std::default::Default; use std::rc::Rc; -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::Receiver; use timers::{ActiveTimers, IsInterval, TimerCallback}; use url::{Url, UrlParser}; use util::str::DOMString; @@ -44,7 +44,7 @@ pub struct WorkerGlobalScopeInit { pub to_devtools_sender: Option>, pub from_devtools_sender: Option>, pub constellation_chan: ConstellationChan, - pub scheduler_chan: Sender, + pub scheduler_chan: IpcSender, pub worker_id: WorkerId, } @@ -87,7 +87,7 @@ pub struct WorkerGlobalScope { constellation_chan: ConstellationChan, #[ignore_heap_size_of = "Defined in std"] - scheduler_chan: Sender, + scheduler_chan: IpcSender, } impl WorkerGlobalScope { @@ -95,7 +95,7 @@ impl WorkerGlobalScope { worker_url: Url, runtime: Rc, from_devtools_receiver: Receiver, - timer_event_chan: Box) + timer_event_chan: IpcSender) -> WorkerGlobalScope { WorkerGlobalScope { eventtarget: EventTarget::new_inherited(), @@ -139,7 +139,7 @@ impl WorkerGlobalScope { self.constellation_chan.clone() } - pub fn scheduler_chan(&self) -> Sender { + pub fn scheduler_chan(&self) -> IpcSender { self.scheduler_chan.clone() } diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 9647ddd4376c..a4f116d1c232 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -30,6 +30,8 @@ use style::media_queries::MediaQueryList; use style::stylesheets::Stylesheet; use style::viewport::ViewportRule; use url::Url; +use util::ipc::OptionalOpaqueIpcSender; + pub use dom::node::TrustedNodeAddress; /// Asynchronous messages that script can send to layout. @@ -176,7 +178,7 @@ pub struct ScriptReflow { /// The document node. pub document_root: TrustedNodeAddress, /// The channel through which messages can be sent back to the script task. - pub script_chan: Sender, + pub script_chan: IpcSender, /// The current window size. pub window_size: WindowSizeData, /// The channel that we send a notification to. @@ -255,8 +257,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/components/script/script_task.rs b/components/script/script_task.rs index 30bb371667b0..d00c6e2c4140 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -83,7 +83,7 @@ use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEven use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo}; use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory}; -use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource}; +use script_traits::{TimerEvent, TimerEventRequest, TimerSource}; use std::any::Any; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; @@ -101,7 +101,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; @@ -198,8 +198,9 @@ pub enum ScriptTaskEventCategory { Resize, ScriptEvent, TimerEvent, - UpdateReplacedElement, SetViewport, + StylesheetLoad, + UpdateReplacedElement, WebSocketEvent, WorkerEvent, XhrEvent, @@ -320,20 +321,6 @@ impl MainThreadScriptChan { } } -pub struct MainThreadTimerEventChan(Sender); - -impl TimerEventChan for MainThreadTimerEventChan { - fn send(&self, event: TimerEvent) -> Result<(), ()> { - let MainThreadTimerEventChan(ref chan) = *self; - chan.send(event).map_err(|_| ()) - } - - fn clone(&self) -> Box { - let MainThreadTimerEventChan(ref chan) = *self; - box MainThreadTimerEventChan((*chan).clone()) - } -} - pub struct StackRootTLS; impl StackRootTLS { @@ -377,7 +364,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. @@ -416,7 +403,7 @@ pub struct ScriptTask { /// List of pipelines that have been owned and closed by this script task. closed_pipelines: RefCell>, - scheduler_chan: Sender, + scheduler_chan: IpcSender, timer_event_chan: Sender, timer_event_port: Receiver, } @@ -465,7 +452,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 } @@ -477,9 +465,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); @@ -488,6 +477,7 @@ impl ScriptTaskFactory for ScriptTask { let parent_info = state.parent_info; let mem_profiler_chan = state.mem_profiler_chan.clone(); let window_size = state.window_size; + let content_process_shutdown_chan = state.content_process_shutdown_chan.clone(); let script_task = ScriptTask::new(state, script_port, chan); @@ -507,6 +497,8 @@ impl ScriptTaskFactory for ScriptTask { script_task.start(); }, reporter_name, channel_for_reporter, CommonScriptMsg::CollectReports); + content_process_shutdown_chan.send(()).unwrap(); + // This must always be the very last operation performed before the task completes failsafe.neuter(); }, ConstellationMsg::Failure(failure_info), const_chan); @@ -617,6 +609,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!()), @@ -631,7 +626,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, @@ -915,7 +910,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, @@ -969,7 +967,7 @@ impl ScriptTask { ConstellationControlMsg::WebFontLoaded(pipeline_id) => self.handle_web_font_loaded(pipeline_id), ConstellationControlMsg::StylesheetLoadComplete(id, url, responder) => { - responder.respond(); + responder.send(()).unwrap(); self.handle_resource_loaded(id, LoadType::Stylesheet(url)); } ConstellationControlMsg::GetCurrentState(sender, pipeline_id) => { @@ -1168,6 +1166,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>); @@ -1187,6 +1186,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(); @@ -1583,6 +1583,10 @@ impl ScriptTask { let mut page_remover = AutoPageRemover::new(self, page_to_remove); let MainThreadScriptChan(ref sender) = self.chan; + let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap(); + ROUTER.route_ipc_receiver_to_mpsc_sender(ipc_timer_event_port, + self.timer_event_chan.clone()); + // Create the window and document objects. let window = Window::new(self.js_runtime.clone(), page.clone(), @@ -1597,7 +1601,7 @@ impl ScriptTask { self.devtools_chan.clone(), self.constellation_chan.clone(), self.scheduler_chan.clone(), - MainThreadTimerEventChan(self.timer_event_chan.clone()), + ipc_timer_event_chan, incomplete.layout_chan, incomplete.pipeline_id, incomplete.parent_info, diff --git a/components/script/timers.rs b/components/script/timers.rs index 6c982cb1ad44..b132d0ad3f24 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -9,16 +9,16 @@ use dom::bindings::global::global_object_for_js_object; use dom::bindings::utils::Reflectable; use dom::window::ScriptHelpers; use euclid::length::Length; +use ipc_channel::ipc::IpcSender; use js::jsapi::{HandleValue, Heap, RootedValue}; use js::jsval::{JSVal, UndefinedValue}; use num::traits::Saturating; use script_traits::{MsDuration, precise_time_ms}; -use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource}; +use script_traits::{TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use std::cell::Cell; use std::cmp::{self, Ord, Ordering}; use std::default::Default; use std::rc::Rc; -use std::sync::mpsc::Sender; use util::mem::HeapSizeOf; use util::str::DOMString; @@ -29,9 +29,9 @@ pub struct TimerHandle(i32); #[privatize] pub struct ActiveTimers { #[ignore_heap_size_of = "Defined in std"] - timer_event_chan: Box, + timer_event_chan: IpcSender, #[ignore_heap_size_of = "Defined in std"] - scheduler_chan: Sender, + scheduler_chan: IpcSender, next_timer_handle: Cell, timers: DOMRefCell>, suspended_since: Cell>, @@ -116,8 +116,8 @@ impl HeapSizeOf for InternalTimerCallback { } impl ActiveTimers { - pub fn new(timer_event_chan: Box, - scheduler_chan: Sender) + pub fn new(timer_event_chan: IpcSender, + scheduler_chan: IpcSender) -> ActiveTimers { ActiveTimers { timer_event_chan: timer_event_chan, diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index eda5cf1b4593..88d9ef79f618 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -40,8 +40,8 @@ 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 url::Url; +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,17 +78,17 @@ 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<()>, } /// `StylesheetLoadResponder` is used to notify a responder that a style sheet @@ -98,7 +99,7 @@ pub trait StylesheetLoadResponder { } /// 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, @@ -107,6 +108,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), @@ -142,13 +144,13 @@ pub enum ConstellationControlMsg { /// reflowed. WebFontLoaded(PipelineId), /// Notifies script that a stylesheet has finished loading. - StylesheetLoadComplete(PipelineId, Url, Box), + StylesheetLoadComplete(PipelineId, Url, IpcSender<()>), /// 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, @@ -159,6 +161,7 @@ pub enum MouseButton { } /// Events from the compositor that the script task needs to know about +#[derive(Deserialize, Serialize)] pub enum CompositorEvent { /// The window was resized. ResizeEvent(WindowSizeData), @@ -185,23 +188,20 @@ pub enum CompositorEvent { pub struct OpaqueScriptLayoutChannel(pub (Box, Box)); /// Requests a TimerEvent-Message be sent after the given duration. -pub struct TimerEventRequest(pub Box, pub TimerSource, pub TimerEventId, pub MsDuration); +#[derive(Deserialize, Serialize)] +pub struct TimerEventRequest(pub IpcSender, + pub TimerSource, + pub TimerEventId, + pub MsDuration); /// Notifies the script task to fire due timers. /// TimerSource must be FromWindow when dispatched to ScriptTask and /// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope +#[derive(Deserialize, Serialize)] pub struct TimerEvent(pub TimerSource, pub TimerEventId); -/// A cloneable interface for sending timer events. -pub trait TimerEventChan { - /// Send a timer event to the associated event loop. - fn send(&self, msg: TimerEvent) -> Result<(), ()>; - /// Clone this handle. - fn clone(&self) -> Box; -} - /// Describes the task that requested the TimerEvent. -#[derive(Copy, Clone, HeapSizeOf)] +#[derive(Copy, Clone, HeapSizeOf, Deserialize, Serialize)] pub enum TimerSource { /// The event was requested from a window (ScriptTask). FromWindow(PipelineId), @@ -210,7 +210,7 @@ pub enum TimerSource { } /// The id to be used for a TimerEvent is defined by the corresponding TimerEventRequest. -#[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf)] +#[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf, Deserialize, Serialize)] pub struct TimerEventId(pub u32); /// Unit of measurement. @@ -235,6 +235,9 @@ pub fn precise_time_ns() -> NsDuration { } /// Data needed to construct a script thread. +/// +/// NB: *DO NOT* add any Senders or Receivers here! pcwalton will have to rewrite your code if you +/// do! Use IPC senders and receivers instead. pub struct InitialScriptState { /// The ID of the pipeline with which this script thread is associated. pub id: PipelineId, @@ -244,13 +247,13 @@ 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. - pub scheduler_chan: Sender, + pub scheduler_chan: IpcSender, /// Information that script sends out when it panics. pub failure_info: Failure, /// A channel to the resource manager task. @@ -269,8 +272,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/components/servo/Cargo.lock b/components/servo/Cargo.lock index 0a67dc614c17..98e16488ddb1 100644 --- a/components/servo/Cargo.lock +++ b/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.2.0 (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)", @@ -257,6 +259,7 @@ dependencies = [ "core-text 0.1.0 (git+https://github.com/servo/core-text-rs)", "devtools_traits 0.0.1", "euclid 0.2.0 (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)", @@ -264,6 +267,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.2 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -272,6 +276,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.5.3 (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)", @@ -595,6 +601,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#b309234ff02d77e6106f1bd6142a57091e28d052" +dependencies = [ + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.2 (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/components/servo/Cargo.toml b/components/servo/Cargo.toml index 45304b6d43eb..51a9e9da6aaf 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -121,6 +121,12 @@ features = [ "serde_serialization" ] [dependencies.euclid] version = "0.2" +[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" diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 359fc6a82ec6..c93af06fd0af 100644 --- a/components/servo/lib.rs +++ b/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; @@ -58,10 +61,14 @@ fn webdriver(_port: u16, _constellation: msg::constellation_msg::ConstellationCh 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::ConstellationChan; use msg::constellation_msg::Msg as ConstellationMsg; use net::image_cache_task::new_image_cache_task; @@ -86,6 +93,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; @@ -226,3 +234,26 @@ fn create_constellation(opts: opts::Opts, 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/components/servo/main.rs b/components/servo/main.rs index 3e81da46f1f5..2dcef3e4b396 100644 --- a/components/servo/main.rs +++ b/components/servo/main.rs @@ -27,10 +27,14 @@ extern crate env_logger; extern crate servo; extern crate time; +#[cfg(target_os = "android")] +#[macro_use] +extern crate android_glue; + use servo::Browser; use servo::compositing::windowing::WindowEvent; use servo::net_traits::hosts; -use servo::util::opts; +use servo::util::opts::{self, ArgumentParsingResult}; #[cfg(target_os = "android")] use std::borrow::ToOwned; use std::rc::Rc; @@ -39,13 +43,17 @@ fn main() { env_logger::init().unwrap(); // Parse the command line options and store them globally - opts::from_cmdline_args(&*args()); + let opts_result = opts::from_cmdline_args(&*args()); setup_logging(); // Possibly interpret the `HOST_FILE` environment variable hosts::global_init(); + if let ArgumentParsingResult::ContentProcess(token) = opts_result { + return servo::run_content_process(token) + } + let window = if opts::get().headless { None } else { diff --git a/components/util/ipc.rs b/components/util/ipc.rs index 20c80cf042bb..be0196abdaf8 100644 --- a/components/util/ipc.rs +++ b/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/components/util/lib.rs b/components/util/lib.rs index 5cfda0e8c7cc..ceb6d7450981 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -13,6 +13,7 @@ #![feature(optin_builtin_traits)] #![feature(path_ext)] #![feature(plugin)] +#![feature(reflect_marker)] #![feature(slice_splits)] #![feature(step_by)] #![feature(step_trait)] diff --git a/components/util/opts.rs b/components/util/opts.rs index 785f59092400..eb16c0392036 100644 --- a/components/util/opts.rs +++ b/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 { /// The initial URL to load. pub url: Option, @@ -135,9 +136,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, @@ -361,6 +365,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, @@ -424,6 +435,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, @@ -442,7 +454,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(); @@ -472,11 +484,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"); @@ -493,6 +508,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() @@ -581,6 +603,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), @@ -626,6 +652,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, @@ -655,6 +682,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/components/util/str.rs b/components/util/str.rs index 91d5562f4e4c..6dc5938d79b6 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -359,7 +359,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/components/util/task.rs b/components/util/task.rs index 2495bb8acd5d..c9b9463ed294 100644 --- a/components/util/task.rs +++ b/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/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 65aafad158ef..21535377f041 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -263,9 +263,12 @@ dependencies = [ "plugins 0.0.1", "profile_traits 0.0.1", "script_traits 0.0.1", + "serde 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_macros 0.4.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)", + "style 0.0.1", "util 0.0.1", ] diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 0b872ec8ec8d..9359705f2f70 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -234,6 +234,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.5.3 (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)",