diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index d10e0c297038..fc9a3980fb2e 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -249,7 +249,7 @@ impl IOCompositor { (Msg::Exit(chan), _) => { debug!("shutting down the constellation"); - let ConstellationChan(ref con_chan) = self.constellation_chan; + let con_chan = &mut self.constellation_chan; con_chan.send(ConstellationMsg::Exit); chan.send(()); self.shutdown_state = ShutdownState::ShuttingDown; @@ -659,12 +659,12 @@ impl IOCompositor { root_layer.add_child(new_layer); } - fn send_window_size(&self) { + fn send_window_size(&mut self) { let dppx = self.page_zoom * self.device_pixels_per_screen_px(); let initial_viewport = self.window_size.as_f32() / dppx; let visible_viewport = initial_viewport / self.viewport_zoom; - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { device_pixel_ratio: dppx, initial_viewport: initial_viewport, @@ -828,7 +828,7 @@ impl IOCompositor { WindowEvent::Quit => { debug!("shutting down the constellation for WindowEvent::Quit"); - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::Exit); self.shutdown_state = ShutdownState::ShuttingDown; } @@ -866,7 +866,7 @@ impl IOCompositor { let msg = ConstellationMsg::LoadUrl(root_pipeline_id, LoadData::new(Url::parse(url_string.as_slice()).unwrap())); - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(msg); } @@ -980,17 +980,17 @@ impl IOCompositor { self.composite_if_necessary(); } - fn on_navigation_window_event(&self, direction: WindowNavigateMsg) { + fn on_navigation_window_event(&mut self, direction: WindowNavigateMsg) { let direction = match direction { windowing::WindowNavigateMsg::Forward => NavigationDirection::Forward, windowing::WindowNavigateMsg::Back => NavigationDirection::Back, }; - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::Navigate(direction)) } - fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { - let ConstellationChan(ref chan) = self.constellation_chan; + fn on_key_event(&mut self, key: Key, state: KeyState, modifiers: KeyModifiers) { + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::KeyEvent(key, state, modifiers)) } @@ -1200,7 +1200,7 @@ impl IOCompositor { assert!(res.is_ok()); debug!("shutting down the constellation after generating an output file"); - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::Exit); self.shutdown_state = ShutdownState::ShuttingDown; } @@ -1351,7 +1351,8 @@ impl CompositorEventListener for IOCompositor where Window: Wind } match self.composition_request { - CompositionRequest::NoCompositingNecessary | CompositionRequest::CompositeOnScrollTimeout(_) => {} + CompositionRequest::NoCompositingNecessary | + CompositionRequest::CompositeOnScrollTimeout(_) => {} CompositionRequest::CompositeNow => self.composite(), } @@ -1405,12 +1406,12 @@ impl CompositorEventListener for IOCompositor where Window: Wind self.viewport_zoom.get() as f32 } - fn get_title_for_main_frame(&self) { + fn get_title_for_main_frame(&mut self) { let root_pipeline_id = match self.root_pipeline { None => return, Some(ref root_pipeline) => root_pipeline.id, }; - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut self.constellation_chan; chan.send(ConstellationMsg::GetPipelineTitle(root_pipeline_id)); } } diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 3ff651da2816..c9b86274cb32 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -19,9 +19,10 @@ use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphics use layers::layers::LayerBufferSet; use pipeline::CompositionPipeline; use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; -use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; +use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptToCompositorMsg, ScrollPolicy}; use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use servo_net::server::{Server, SharedServerProxy}; use servo_util::cursor::Cursor; use servo_util::geometry::PagePx; use servo_util::memory::MemoryProfilerChan; @@ -29,6 +30,8 @@ use servo_util::time::TimeProfilerChan; use std::comm::{channel, Sender, Receiver}; use std::fmt::{Error, Formatter, Show}; use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use std::task; /// Sends messages to the compositor. This is a trait supplied by the port because the method used /// to communicate with the compositor may have to kick OS event loops awake, communicate cross- @@ -62,41 +65,6 @@ impl CompositorReceiver for Receiver { } } -/// Implementation of the abstract `ScriptListener` interface. -impl ScriptListener for Box { - fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) { - let msg = Msg::ChangeReadyState(pipeline_id, ready_state); - self.send(msg); - } - - fn scroll_fragment_point(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - point: Point2D) { - self.send(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point)); - } - - fn close(&mut self) { - let (chan, port) = channel(); - self.send(Msg::Exit(chan)); - port.recv(); - } - - fn dup(&mut self) -> Box { - box self.clone_compositor_proxy() as Box - } - - fn set_title(&mut self, pipeline_id: PipelineId, title: Option) { - self.send(Msg::ChangePageTitle(pipeline_id, title)) - } - - fn send_key_event(&mut self, key: Key, state: KeyState, modifiers: KeyModifiers) { - if state == KeyState::Pressed { - self.send(Msg::KeyEvent(key, modifiers)); - } - } -} - /// Information about each layer that the compositor keeps. #[deriving(Copy)] pub struct LayerProperties { @@ -270,14 +238,55 @@ impl CompositorTask { NativeCompositingGraphicsContext::new() } + pub fn create_compositor_server_channel() -> (Server, + SharedServerProxy) { + let mut server = Server::new("CompositorTask"); + let server_proxy = Arc::new(Mutex::new(server.create_new_client())); + (server, server_proxy) + } + pub fn create(window: Option>, sender: Box, receiver: Box, + mut server: Server, constellation_chan: ConstellationChan, time_profiler_chan: TimeProfilerChan, memory_profiler_chan: MemoryProfilerChan) -> Box where Window: WindowMethods + 'static { + // Create a proxy server to forward messages received via IPC to the compositor. + let mut compositor_proxy_for_forwarder = sender.clone_compositor_proxy(); + task::spawn(proc() { + while let Some(msgs) = server.recv() { + for (_, msg) in msgs.into_iter() { + match msg { + ScriptToCompositorMsg::SetReadyState(pipeline_id, ready_state) => { + compositor_proxy_for_forwarder.send(Msg::ChangeReadyState( + pipeline_id, + ready_state)) + } + ScriptToCompositorMsg::ScrollFragmentPoint(pipeline_id, + layer_id, + point) => { + compositor_proxy_for_forwarder.send(Msg::ScrollFragmentPoint( + pipeline_id, + layer_id, + point)) + } + ScriptToCompositorMsg::SetTitle(pipeline_id, title) => { + compositor_proxy_for_forwarder.send(Msg::ChangePageTitle(pipeline_id, + title)) + } + ScriptToCompositorMsg::SendKeyEvent(key, state, modifiers) => { + if state == KeyState::Pressed { + compositor_proxy_for_forwarder.send(Msg::KeyEvent(key, modifiers)) + } + } + } + } + } + }); + match window { Some(window) => { box compositor::IOCompositor::create(window, @@ -305,6 +314,6 @@ pub trait CompositorEventListener { fn shutdown(&mut self); fn pinch_zoom_level(&self) -> f32; /// Requests that the compositor send the title for the main frame as soon as possible. - fn get_title_for_main_frame(&self); + fn get_title_for_main_frame(&mut self); } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 2aa1b9688ee7..beef2d863051 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -16,7 +16,7 @@ use layout_traits::LayoutTaskFactory; use libc; use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{ScriptControlChan, ScriptTaskFactory}; -use servo_msg::compositor_msg::LayerId; +use servo_msg::compositor_msg::{LayerId, ScriptToCompositorMsg}; use servo_msg::constellation_msg::{mod, ConstellationChan, Failure}; use servo_msg::constellation_msg::{IFrameSandboxState, NavigationDirection}; use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers}; @@ -25,9 +25,9 @@ use servo_msg::constellation_msg::{PipelineExitType, PipelineId}; use servo_msg::constellation_msg::{SubpageId, WindowSizeData}; use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; -use servo_net::resource_task::ResourceTask; -use servo_net::resource_task; -use servo_net::storage_task::{StorageTask, StorageTaskMsg}; +use servo_net::resource_task::{mod, ResourceTask}; +use servo_net::server::{ClientId, Server, SharedServerProxy}; +use servo_net::storage_task::StorageTask; use servo_util::cursor::Cursor; use servo_util::geometry::{PagePx, ViewportPx}; use servo_util::opts; @@ -39,20 +39,21 @@ use std::collections::{HashMap, HashSet}; use std::io; use std::mem::replace; use std::rc::Rc; +use std::sync::{Arc, Mutex}; use url::Url; /// Maintains the pipelines and navigation context and grants permission to composite. pub struct Constellation { - /// A channel through which messages can be sent to this object. - pub chan: ConstellationChan, - - /// Receives messages. - pub request_port: Receiver, + /// The server that we receive messages on. + pub server: Server, /// A channel (the implementation of which is port-specific) through which messages can be sent /// to the compositor. pub compositor_proxy: Box, + /// An client that the script thread uses to communicate with the compositor. + pub script_to_compositor_client: SharedServerProxy, + /// A channel through which messages can be sent to the resource task. pub resource_task: ResourceTask, @@ -341,6 +342,7 @@ impl NavigationContext { impl Constellation { pub fn start(compositor_proxy: Box, + script_to_compositor_client: SharedServerProxy, resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, @@ -348,13 +350,14 @@ impl Constellation { devtools_chan: Option, storage_task: StorageTask) -> ConstellationChan { - let (constellation_port, constellation_chan) = ConstellationChan::new(); - let constellation_chan_clone = constellation_chan.clone(); + let mut server = Server::new("Constellation"); + let constellation_chan = Arc::new(Mutex::new(server.create_new_client())); + let constellation_chan = ConstellationChan::from_server_proxy(constellation_chan); spawn_named("Constellation".to_owned(), proc() { let mut constellation: Constellation = Constellation { - chan: constellation_chan_clone, - request_port: constellation_port, + server: server, compositor_proxy: compositor_proxy, + script_to_compositor_client: script_to_compositor_client, devtools_chan: devtools_chan, resource_task: resource_task, image_cache_task: image_cache_task, @@ -379,10 +382,11 @@ impl Constellation { } fn run(&mut self) { - loop { - let request = self.request_port.recv(); - if !self.handle_request(request) { - break; + while let Some(msgs) = self.server.recv() { + for (client_id, msg) in msgs.into_iter() { + if !self.handle_request(client_id, msg) { + return + } } } } @@ -394,15 +398,18 @@ impl Constellation { script_pipeline: Option>, load_data: LoadData) -> Rc { + let constellation_chan = Arc::new(Mutex::new(self.server.create_new_client())); + let constellation_chan = ConstellationChan::from_server_proxy(constellation_chan); let pipe = Pipeline::create::(id, subpage_id, - self.chan.clone(), + constellation_chan, self.compositor_proxy.clone_compositor_proxy(), + self.script_to_compositor_client.clone(), self.devtools_chan.clone(), self.image_cache_task.clone(), - self.font_cache_task.clone(), + self.font_cache_task.create_new_client(), self.resource_task.clone(), - self.storage_task.clone(), + self.storage_task.create_new_client(), self.time_profiler_chan.clone(), self.window_size, script_pipeline, @@ -443,7 +450,7 @@ impl Constellation { } /// Handles loading pages, navigation, and granting access to the compositor - fn handle_request(&mut self, request: ConstellationMsg) -> bool { + fn handle_request(&mut self, _: ClientId, request: ConstellationMsg) -> bool { match request { ConstellationMsg::Exit => { debug!("constellation exiting"); @@ -464,7 +471,11 @@ impl Constellation { debug!("constellation got frame rect message"); self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect)); } - ConstellationMsg::ScriptLoadedURLInIFrame(url, source_pipeline_id, new_subpage_id, old_subpage_id, sandbox) => { + ConstellationMsg::ScriptLoadedURLInIFrame(url, + source_pipeline_id, + new_subpage_id, + old_subpage_id, + sandbox) => { debug!("constellation got iframe URL load message"); self.handle_script_loaded_url_in_iframe_msg(url, source_pipeline_id, @@ -521,8 +532,6 @@ impl Constellation { self.devtools_chan.as_ref().map(|chan| { chan.send(devtools_traits::ServerExitMsg); }); - self.storage_task.send(StorageTaskMsg::Exit); - self.font_cache_task.exit(); self.compositor_proxy.send(CompositorMsg::ShutdownComplete); } @@ -1068,9 +1077,11 @@ impl Constellation { fn send_frame_tree_and_grant_paint_permission(&mut self, frame_tree: Rc) { debug!("Constellation sending SetFrameTree"); let (chan, port) = channel(); + let constellation_chan = Arc::new(Mutex::new(self.server.create_new_client())); + let constellation_chan = ConstellationChan::from_server_proxy(constellation_chan); self.compositor_proxy.send(CompositorMsg::SetFrameTree(frame_tree.to_sendable(), chan, - self.chan.clone())); + constellation_chan)); if port.recv_opt().is_err() { debug!("Compositor has discarded SetFrameTree"); return; // Our message has been discarded, probably shutting down. diff --git a/components/compositing/content_process.rs b/components/compositing/content_process.rs new file mode 100644 index 000000000000..acad523c8ee7 --- /dev/null +++ b/components/compositing/content_process.rs @@ -0,0 +1,165 @@ +/* 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 gfx::font_cache_task::FontCacheTask; +use gfx::paint_task::LayoutToPaintChan; +use layout_traits::{LayoutControlMsg, LayoutTaskFactory}; +use libc::c_int; +use libc::funcs::posix88::unistd; +use script_traits::{ConstellationControlMsg, ScriptTaskFactory}; +use servo_msg::compositor_msg::ScriptToCompositorMsg; +use servo_msg::constellation_msg::{ConstellationChan, Failure, LoadData, PipelineId}; +use servo_msg::constellation_msg::{WindowSizeData}; +use servo_net::image_cache_task::ImageCacheTask; +use servo_net::resource_task::ResourceTask; +use servo_net::server::SharedServerProxy; +use servo_net::storage_task::StorageTask; +use servo_util::ipc::{mod, IpcReceiver}; +use servo_util::opts::{mod, Opts}; +use servo_util::time::TimeProfilerChan; +use std::os; +use std::ptr; + +/// Messages from the chrome process to the content process. +#[deriving(Encodable, Decodable)] +pub enum ContentProcessMsg { + CreateScriptAndLayoutThreads(AuxiliaryContentProcessData), +} + +/// IPC channels that a content process uses to communicate with the chrome process. +pub struct ContentProcessIpc { + pub script_to_compositor_client: SharedServerProxy, + pub script_port: IpcReceiver, + pub constellation_chan: ConstellationChan, + pub storage_task: StorageTask, + pub pipeline_to_layout_port: IpcReceiver, + pub layout_to_paint_chan: LayoutToPaintChan, + pub font_cache_task: FontCacheTask, +} + +/// Other data used to construct a content process. +#[deriving(Encodable, Decodable)] +pub struct AuxiliaryContentProcessData { + pub pipeline_id: PipelineId, + pub failure: Failure, + pub window_size: WindowSizeData, + pub zone: Zone, +} + +/// Information sent over IPC to bootstrap a content process. This consists of the auxiliary +/// content process data plus a set of file descriptors that can construct a `ContentProcessIpc`. +#[deriving(Encodable, Decodable)] +pub struct BootstrapInfo { + pub script_to_compositor_client: (c_int, c_int), + pub script_port: c_int, + pub constellation_chan: (c_int, c_int), + pub storage_task: (c_int, c_int), + pub pipeline_to_layout_port: c_int, + pub layout_to_paint_chan: c_int, + pub font_cache_task: (c_int, c_int), + pub auxiliary_data: AuxiliaryContentProcessData, + pub opts: Opts, +} + +pub struct ContentProcess where LTF: LayoutTaskFactory, STF: ScriptTaskFactory { + pub ipc: ContentProcessIpc, + pub resource_task: ResourceTask, + pub image_cache_task: ImageCacheTask, + pub time_profiler_chan: TimeProfilerChan, +} + +impl ContentProcess where LTF: LayoutTaskFactory, STF: ScriptTaskFactory { + pub fn create_script_and_layout_threads(self, data: AuxiliaryContentProcessData) { + let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>); + let (layout_to_script_sender, layout_to_script_receiver) = channel(); + ScriptTaskFactory::create(None::<&mut STF>, + data.pipeline_id, + self.ipc.script_to_compositor_client, + &layout_pair, + self.ipc.script_port, + self.ipc.constellation_chan.clone(), + data.failure.clone(), + layout_to_script_receiver, + self.resource_task.clone(), + self.ipc.storage_task, + self.image_cache_task.clone(), + None, + data.window_size); + LayoutTaskFactory::create(None::<&mut LTF>, + data.pipeline_id, + layout_pair, + self.ipc.pipeline_to_layout_port, + self.ipc.constellation_chan, + data.failure, + layout_to_script_sender, + self.ipc.layout_to_paint_chan, + self.resource_task.clone(), + self.image_cache_task.clone(), + self.ipc.font_cache_task, + self.time_profiler_chan); + } +} + +/// Spawns a content process. +/// +/// FIXME(pcwalton): This leaks most of the file descriptors. :( We will need to wait for Rust to +/// use `O_CLOEXEC` properly. +pub fn spawn(ipc: ContentProcessIpc, data: AuxiliaryContentProcessData) { + let (bootstrap_receiver, bootstrap_sender) = ipc::channel(); + let bootstrap_info = BootstrapInfo { + script_to_compositor_client: ipc.script_to_compositor_client.lock().fds(), + script_port: ipc.script_port.fd(), + constellation_chan: ipc.constellation_chan.server_proxy().lock().fds(), + storage_task: ipc.storage_task.client.lock().fds(), + pipeline_to_layout_port: ipc.pipeline_to_layout_port.fd(), + layout_to_paint_chan: ipc.layout_to_paint_chan.fd(), + font_cache_task: ipc.font_cache_task.client.lock().fds(), + auxiliary_data: data, + opts: (*opts::get()).clone(), + }; + + let args = [os::self_exe_name().expect("can't find our own executable name")]; + let argv: Vec<_> = args.iter().map(|arg| arg.to_c_str()).collect(); + let mut argv: Vec<_> = argv.iter().map(|arg| arg.as_ptr()).collect(); + argv.push(ptr::null()); + + os::setenv("SERVO_CONTENT_PROCESS", bootstrap_receiver.fd().to_string()); + + unsafe { + if unistd::fork() == 0 { + unistd::execv(argv[0], &mut argv[0]); + panic!("failed to execute content process: {}!", args[0].display()); + } + } + + ipc.storage_task.client.lock().forget(); + ipc.font_cache_task.client.lock().forget(); + + bootstrap_sender.send(bootstrap_info); +} + +/// The zone that the content process is restricted to. At present, this determines whether local +/// files can be accessed. +/// +/// TODO(pcwalton): Remove this once the resource task has been rewritten to be e10s-safe. At that +/// point the resource task can handle this policy and the sandbox will not have to be involved. +#[deriving(Clone, Encodable, Decodable)] +pub enum Zone { + /// The local zone. This allows full access to both local files and remote resources. + Local, + /// The remote zone. This allows access to remote resources but disallows access to local + /// files. + Remote, +} + +impl Zone { + pub fn from_load_data(load_data: &LoadData) -> Zone { + if load_data.url.scheme.as_slice() == "file" { + Zone::Local + } else { + Zone::Remote + } + } +} + diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 861886362602..971bc4d23c44 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -9,10 +9,8 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; -use servo_util::memory::MemoryProfilerChan; -use servo_util::memory; -use servo_util::time::TimeProfilerChan; -use servo_util::time; +use servo_util::memory::{mod, MemoryProfilerChan}; +use servo_util::time::{mod, TimeProfilerChan}; /// Starts the compositor, which listens for messages on the specified port. /// @@ -48,14 +46,14 @@ impl NullCompositor { time_profiler_chan: TimeProfilerChan, memory_profiler_chan: MemoryProfilerChan) -> NullCompositor { - let compositor = NullCompositor::new(port, - constellation_chan, - time_profiler_chan, - memory_profiler_chan); + let mut compositor = NullCompositor::new(port, + constellation_chan, + time_profiler_chan, + memory_profiler_chan); // Tell the constellation about the initial fake size. { - let ConstellationChan(ref chan) = compositor.constellation_chan; + let chan = &mut compositor.constellation_chan; chan.send(ConstellationMsg::ResizedWindow(WindowSizeData { initial_viewport: TypedSize2D(640_f32, 480_f32), visible_viewport: TypedSize2D(640_f32, 480_f32), @@ -72,7 +70,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; + let con_chan = &mut self.constellation_chan; con_chan.send(ConstellationMsg::Exit); chan.send(()); } @@ -136,5 +134,5 @@ impl CompositorEventListener for NullCompositor { 1.0 } - fn get_title_for_main_frame(&self) {} + fn get_title_for_main_frame(&mut self) {} } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index fee2fbf16715..4b35c53316df 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -26,6 +26,7 @@ extern crate "util" as servo_util; extern crate gleam; extern crate libc; +extern crate serialize; extern crate time; extern crate url; @@ -38,6 +39,7 @@ pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTa pub use constellation::Constellation; pub mod compositor_task; +pub mod content_process; mod compositor_layer; mod scrolling; diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index c8a054d16efe..dcc8ca780de7 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -3,19 +3,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use CompositorProxy; +use content_process::{mod, AuxiliaryContentProcessData, ContentProcess, ContentProcessIpc, Zone}; use layout_traits::{LayoutControlMsg, LayoutTaskFactory, LayoutControlChan}; use script_traits::{ScriptControlChan, ScriptTaskFactory}; -use script_traits::{NewLayoutInfo, ConstellationControlMsg}; +use script_traits::{ConstellationControlMsg}; use devtools_traits::DevtoolsControlChan; +use gfx::font_cache_task::FontCacheTask; use gfx::paint_task::Msg as PaintMsg; use gfx::paint_task::{PaintChan, PaintTask}; +use servo_msg::compositor_msg::ScriptToCompositorMsg; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId}; use servo_msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType}; use servo_net::image_cache_task::ImageCacheTask; -use gfx::font_cache_task::FontCacheTask; use servo_net::resource_task::ResourceTask; +use servo_net::server::SharedServerProxy; use servo_net::storage_task::StorageTask; +use servo_util::ipc; +use servo_util::opts; use servo_util::time::TimeProfilerChan; use std::rc::Rc; @@ -27,7 +32,6 @@ pub struct Pipeline { pub layout_chan: LayoutControlChan, pub paint_chan: PaintChan, pub layout_shutdown_port: Receiver<()>, - pub paint_shutdown_port: Receiver<()>, /// Load data corresponding to the most recently-loaded page. pub load_data: LoadData, /// The title of the most recently-loaded page. @@ -50,7 +54,9 @@ impl Pipeline { subpage_id: Option, constellation_chan: ConstellationChan, compositor_proxy: Box, - devtools_chan: Option, + script_to_compositor_client: SharedServerProxy, + _: Option, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, resource_task: ResourceTask, @@ -61,79 +67,66 @@ impl Pipeline { load_data: LoadData) -> Pipeline where LTF: LayoutTaskFactory, STF:ScriptTaskFactory { - let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>); let (paint_port, paint_chan) = PaintChan::new(); - let (paint_shutdown_chan, paint_shutdown_port) = channel(); - let (layout_shutdown_chan, layout_shutdown_port) = channel(); - let (pipeline_chan, pipeline_port) = channel(); + let (_, layout_shutdown_port) = channel(); + let (pipeline_port, pipeline_chan) = ipc::channel(); let failure = Failure { pipeline_id: id, subpage_id: subpage_id, }; - let script_chan = match script_pipeline { + let (script_port, script_chan) = ipc::channel(); + let content_process_ipc = ContentProcessIpc { + script_to_compositor_client: script_to_compositor_client, + script_port: script_port, + constellation_chan: constellation_chan.clone(), + storage_task: storage_task, + pipeline_to_layout_port: pipeline_port, + layout_to_paint_chan: paint_chan.create_layout_channel(), + font_cache_task: font_cache_task.clone(), + }; + + match script_pipeline { None => { - let (script_chan, script_port) = channel(); - ScriptTaskFactory::create(None::<&mut STF>, - id, - compositor_proxy.clone_compositor_proxy(), - &layout_pair, - ScriptControlChan(script_chan.clone()), - script_port, - constellation_chan.clone(), - failure.clone(), - resource_task.clone(), - storage_task.clone(), - image_cache_task.clone(), - devtools_chan, - window_size); - ScriptControlChan(script_chan) - } - Some(spipe) => { - let new_layout_info = NewLayoutInfo { - old_pipeline_id: spipe.id.clone(), - new_pipeline_id: id, - subpage_id: subpage_id.expect("script_pipeline != None but subpage_id == None"), - layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair), + let data = AuxiliaryContentProcessData { + pipeline_id: id, + failure: failure, + window_size: window_size, + zone: Zone::from_load_data(&load_data), }; - let ScriptControlChan(ref chan) = spipe.script_chan; - chan.send(ConstellationControlMsg::AttachLayout(new_layout_info)); - spipe.script_chan.clone() + if !opts::get().multiprocess { + let content_process = ContentProcess { + ipc: content_process_ipc, + resource_task: resource_task.clone(), + image_cache_task: image_cache_task.clone(), + time_profiler_chan: time_profiler_chan.clone(), + }; + content_process.create_script_and_layout_threads(data) + } else { + content_process::spawn(content_process_ipc, data) + } } - }; + Some(_spipe) => { + panic!("layout connection to existing script thread not yet ported to e10s") + } + } PaintTask::create(id, paint_port, compositor_proxy, constellation_chan.clone(), - font_cache_task.clone(), + font_cache_task, failure.clone(), - time_profiler_chan.clone(), - paint_shutdown_chan); - - LayoutTaskFactory::create(None::<&mut LTF>, - id, - layout_pair, - pipeline_port, - constellation_chan, - failure, - script_chan.clone(), - paint_chan.clone(), - resource_task, - image_cache_task, - font_cache_task, - time_profiler_chan, - layout_shutdown_chan); + time_profiler_chan.clone()); Pipeline::new(id, subpage_id, - script_chan, + ScriptControlChan(script_chan), LayoutControlChan(pipeline_chan), paint_chan, layout_shutdown_port, - paint_shutdown_port, load_data) } @@ -143,7 +136,6 @@ impl Pipeline { layout_chan: LayoutControlChan, paint_chan: PaintChan, layout_shutdown_port: Receiver<()>, - paint_shutdown_port: Receiver<()>, load_data: LoadData) -> Pipeline { Pipeline { @@ -153,7 +145,6 @@ impl Pipeline { layout_chan: layout_chan, paint_chan: paint_chan, layout_shutdown_port: layout_shutdown_port, - paint_shutdown_port: paint_shutdown_port, load_data: load_data, title: None, } @@ -182,7 +173,6 @@ impl Pipeline { if chan.send_opt(ConstellationControlMsg::ExitPipeline(self.id, exit_type)).is_ok() { // Wait until all slave tasks have terminated and run destructors // NOTE: We don't wait for script task as we don't always own it - let _ = self.paint_shutdown_port.recv_opt(); let _ = self.layout_shutdown_port.recv_opt(); } @@ -193,7 +183,7 @@ impl Pipeline { let _ = script_channel.send_opt( ConstellationControlMsg::ExitPipeline(self.id, PipelineExitType::PipelineOnly)); - let _ = self.paint_chan.send_opt(PaintMsg::Exit(None, PipelineExitType::PipelineOnly)); + let _ = self.paint_chan.send_opt(PaintMsg::Exit(PipelineExitType::PipelineOnly)); let LayoutControlChan(ref layout_channel) = self.layout_chan; let _ = layout_channel.send_opt(LayoutControlMsg::ExitNowMsg(PipelineExitType::PipelineOnly)); } diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 6ede4546b6b7..571d3f11b00d 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -30,6 +30,7 @@ use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D}; use geom::num::Zero; use libc::uintptr_t; use paint_task::PaintLayer; +use serialize::{Decodable, Decoder, Encodable, Encoder}; use servo_msg::compositor_msg::LayerId; use servo_net::image::base::Image; use servo_util::cursor::Cursor; @@ -60,7 +61,7 @@ pub static BOX_SHADOW_INFLATION_FACTOR: i32 = 3; /// Because the script task's GC does not trace layout, node data cannot be safely stored in layout /// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for /// locality reasons. Using `OpaqueNode` enforces this invariant. -#[deriving(Clone, PartialEq, Copy)] +#[deriving(Clone, PartialEq, Copy, Encodable, Decodable)] pub struct OpaqueNode(pub uintptr_t); impl OpaqueNode { @@ -151,7 +152,52 @@ impl DisplayList { } } +impl Encodable for DisplayList where S: Encoder { + fn encode(&self, s: &mut S) -> Result<(),E> { + s.emit_struct("DisplayList", 6, |s| { + try!(encode_dlist_field(s, &self.background_and_borders, "background_and_borders", 0)); + try!(encode_dlist_field(s, + &self.block_backgrounds_and_borders, + "block_backgrounds_and_borders", + 1)); + try!(encode_dlist_field(s, &self.floats, "floats", 2)); + try!(encode_dlist_field(s, &self.content, "content", 3)); + try!(encode_dlist_field(s, &self.outlines, "outlines", 4)); + encode_dlist_field(s, &self.children, "children", 5) + }) + } +} + +impl Decodable for DisplayList where D: Decoder { + fn decode(d: &mut D) -> Result { + d.read_struct("DisplayList", 6, |d| { + Ok(DisplayList { + background_and_borders: try!(decode_dlist_field(d, "background_and_borders", 0)), + block_backgrounds_and_borders: + try!(decode_dlist_field(d, "block_backgrounds_and_borders", 1)), + floats: try!(decode_dlist_field(d, "floats", 2)), + content: try!(decode_dlist_field(d, "content", 3)), + outlines: try!(decode_dlist_field(d, "outlines", 4)), + children: try!(decode_dlist_field(d, "children", 5)), + }) + }) + } +} + +fn encode_dlist_field(s: &mut S, dlist: &DList, name: &str, index: uint) + -> Result<(),E> + where T: Encodable, S: Encoder { + s.emit_struct_field(name, index, |s| servo_dlist::encode_dlist(s, dlist)) +} + +fn decode_dlist_field(d: &mut D, name: &str, index: uint) + -> Result,E> + where T: Decodable, D: Decoder { + d.read_struct_field(name, index, |d| servo_dlist::decode_dlist(d)) +} + /// Represents one CSS stacking context, which may or may not have a hardware layer. +#[deriving(Encodable, Decodable)] pub struct StackingContext { /// The display items that make up this stacking context. pub display_list: Box, @@ -473,7 +519,7 @@ pub fn find_stacking_context_with_layer_id(this: &Arc, layer_id } /// One drawing command in the list. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub enum DisplayItem { SolidColorClass(Box), TextClass(Box), @@ -485,7 +531,7 @@ pub enum DisplayItem { } /// Information common to all display items. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct BaseDisplayItem { /// The boundaries of the display item, in layer coordinates. pub bounds: Rect, @@ -512,7 +558,7 @@ impl BaseDisplayItem { /// A clipping region for a display item. Currently, this can describe rectangles, rounded /// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms /// are not supported because those are handled by the higher-level `StackingContext` abstraction. -#[deriving(Clone, PartialEq, Show)] +#[deriving(Clone, PartialEq, Show, Decodable, Encodable)] pub struct ClippingRegion { /// The main rectangular region. This does not include any corners. pub main: Rect, @@ -526,7 +572,7 @@ pub struct ClippingRegion { /// A complex clipping region. These don't as easily admit arbitrary intersection operations, so /// they're stored in a list over to the side. Currently a complex clipping region is just a /// rounded rectangle, but the CSS WGs will probably make us throw more stuff in here eventually. -#[deriving(Clone, PartialEq, Show)] +#[deriving(Clone, PartialEq, Show, Decodable, Encodable)] pub struct ComplexClippingRegion { /// The boundaries of the rectangle. pub rect: Rect, @@ -637,7 +683,7 @@ impl ClippingRegion { /// Metadata attached to each display item. This is useful for performing auxiliary tasks with /// the display list involving hit testing: finding the originating DOM node and determining the /// cursor to use when the element is hovered over. -#[deriving(Clone, Copy)] +#[deriving(Clone, Copy, Encodable, Decodable)] pub struct DisplayItemMetadata { /// The DOM node from which this display item originated. pub node: OpaqueNode, @@ -666,14 +712,14 @@ impl DisplayItemMetadata { } /// Paints a solid color. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct SolidColorDisplayItem { pub base: BaseDisplayItem, pub color: Color, } /// Paints text. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct TextDisplayItem { /// Fields common to all display items. pub base: BaseDisplayItem, @@ -691,7 +737,7 @@ pub struct TextDisplayItem { pub orientation: TextOrientation, } -#[deriving(Clone, Eq, PartialEq)] +#[deriving(Clone, Eq, PartialEq, Decodable, Encodable)] pub enum TextOrientation { Upright, SidewaysLeft, @@ -699,10 +745,13 @@ pub enum TextOrientation { } /// Paints an image. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct ImageDisplayItem { + /// Fields common to all display items. pub base: BaseDisplayItem, - pub image: Arc>, + + /// The actual image. + pub image: DisplayItemImage, /// The dimensions to which the image display item should be stretched. If this is smaller than /// the bounds of this display item, then the image will be repeated in the appropriate @@ -710,8 +759,18 @@ pub struct ImageDisplayItem { pub stretch_size: Size2D, } +#[deriving(Clone, Encodable, Decodable)] +pub struct DisplayItemImage(pub Arc>); + +impl DisplayItemImage { + pub fn get(&self) -> Arc> { + let DisplayItemImage(ref image) = *self; + (*image).clone() + } +} + /// Paints a gradient. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct GradientDisplayItem { /// Fields common to all display items. pub base: BaseDisplayItem, @@ -727,7 +786,7 @@ pub struct GradientDisplayItem { } /// Paints a border. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct BorderDisplayItem { /// Fields common to all display items. pub base: BaseDisplayItem, @@ -750,7 +809,7 @@ pub struct BorderDisplayItem { /// Information about the border radii. /// /// TODO(pcwalton): Elliptical radii. -#[deriving(Clone, Default, PartialEq, Show, Copy)] +#[deriving(Clone, Default, PartialEq, Show, Copy, Decodable, Encodable)] pub struct BorderRadii { pub top_left: T, pub top_right: T, @@ -768,7 +827,7 @@ impl BorderRadii where T: PartialEq + Zero { } /// Paints a line segment. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct LineDisplayItem { pub base: BaseDisplayItem, @@ -780,7 +839,7 @@ pub struct LineDisplayItem { } /// Paints a box shadow per CSS-BACKGROUNDS. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct BoxShadowDisplayItem { /// Fields common to all display items. pub base: BaseDisplayItem, @@ -854,7 +913,7 @@ impl DisplayItem { bounds.origin.y = bounds.origin.y + y_offset; bounds.size = image_item.stretch_size; - paint_context.draw_image(&bounds, image_item.image.clone()); + paint_context.draw_image(&bounds, image_item.image.get()); x_offset = x_offset + image_item.stretch_size.width; } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 653eff5e5f37..04c2922460c9 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -69,7 +69,7 @@ pub trait FontTableMethods { fn with_buffer(&self, |*const u8, uint|); } -#[deriving(Clone, Show)] +#[deriving(Clone, Show, Decodable, Encodable)] pub struct FontMetrics { pub underline_size: Au, pub underline_offset: Au, diff --git a/components/gfx/font_cache_task.rs b/components/gfx/font_cache_task.rs index 5bb3a209d680..99503e14dbc7 100644 --- a/components/gfx/font_cache_task.rs +++ b/components/gfx/font_cache_task.rs @@ -9,14 +9,15 @@ use platform::font_list::get_last_resort_font_families; use platform::font_context::FontContextHandle; use collections::str::Str; -use std::borrow::ToOwned; -use std::collections::HashMap; -use std::sync::Arc; use font_template::{FontTemplate, FontTemplateDescriptor}; use platform::font_template::FontTemplateData; use servo_net::resource_task::{ResourceTask, load_whole_resource}; +use servo_net::server::{Server, SharedServerProxy}; use servo_util::task::spawn_named; use servo_util::str::LowercaseString; +use std::borrow::ToOwned; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; use style::Source; /// A list of font templates that make up a given font family. @@ -73,22 +74,24 @@ impl FontFamily { } /// Commands that the FontContext sends to the font cache task. +#[deriving(Decodable, Encodable)] pub enum Command { - GetFontTemplate(String, FontTemplateDescriptor, Sender), - GetLastResortFontTemplate(FontTemplateDescriptor, Sender), - AddWebFont(String, Source, Sender<()>), - Exit(Sender<()>), + GetFontTemplate(String, FontTemplateDescriptor), + GetLastResortFontTemplate(FontTemplateDescriptor), + AddWebFont(String, Source), } /// Reply messages sent from the font cache task to the FontContext caller. +#[deriving(Decodable, Encodable)] pub enum Reply { GetFontTemplateReply(Option>), + AddWebFontReply, } /// The font cache task itself. It maintains a list of reference counted /// font templates that are currently in use. struct FontCache { - port: Receiver, + server: Server, generic_fonts: HashMap, local_families: HashMap, web_families: HashMap, @@ -108,52 +111,54 @@ fn add_generic_font(generic_fonts: &mut HashMap { - let family = LowercaseString::new(family.as_slice()); - let maybe_font_template = self.get_font_template(&family, &descriptor); - result.send(Reply::GetFontTemplateReply(maybe_font_template)); - } - Command::GetLastResortFontTemplate(descriptor, result) => { - let font_template = self.get_last_resort_font_template(&descriptor); - result.send(Reply::GetFontTemplateReply(Some(font_template))); - } - Command::AddWebFont(family_name, src, result) => { - let family_name = LowercaseString::new(family_name.as_slice()); - if !self.web_families.contains_key(&family_name) { - let family = FontFamily::new(); - self.web_families.insert(family_name.clone(), family); + while let Some(msgs) = self.server.recv() { + for (client_id, msg) in msgs.into_iter() { + match msg { + Command::GetFontTemplate(family, descriptor) => { + let family = LowercaseString::new(family.as_slice()); + let maybe_font_template = self.get_font_template(&family, &descriptor); + self.server.send(client_id, + Reply::GetFontTemplateReply(maybe_font_template)); + } + Command::GetLastResortFontTemplate(descriptor) => { + let font_template = self.get_last_resort_font_template(&descriptor); + self.server.send(client_id, + Reply::GetFontTemplateReply(Some(font_template))); } + Command::AddWebFont(family_name, src) => { + let family_name = LowercaseString::new(family_name.as_slice()); + if !self.web_families.contains_key(&family_name) { + let family = FontFamily::new(); + self.web_families.insert(family_name.clone(), family); + } - match src { - Source::Url(ref url_source) => { - let url = &url_source.url; - let maybe_resource = load_whole_resource(&self.resource_task, url.clone()); - match maybe_resource { - Ok((_, bytes)) => { - let family = &mut self.web_families[family_name]; - family.add_template(url.to_string().as_slice(), Some(bytes)); - }, - Err(_) => { - debug!("Failed to load web font: family={} url={}", family_name, url); + match src { + Source::Url(ref url_source) => { + let url = &url_source.url; + let maybe_resource = load_whole_resource(&self.resource_task, + url.clone()); + match maybe_resource { + Ok((_, bytes)) => { + let family = &mut self.web_families[family_name]; + family.add_template(url.to_string().as_slice(), + Some(bytes)); + }, + Err(_) => { + debug!("Failed to load web font: family={} url={}", + family_name, + url); + } } } + Source::Local(ref local_family_name) => { + let family = &mut self.web_families[family_name]; + get_variations_for_family(local_family_name.as_slice(), |path| { + family.add_template(path.as_slice(), None); + }); + } } - Source::Local(ref local_family_name) => { - let family = &mut self.web_families[family_name]; - get_variations_for_family(local_family_name.as_slice(), |path| { - family.add_template(path.as_slice(), None); - }); - } + self.server.send(client_id, Reply::AddWebFontReply); } - result.send(()); - } - Command::Exit(result) => { - result.send(()); - break; } } } @@ -177,8 +182,10 @@ impl FontCache { } } - fn find_font_in_local_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor) - -> Option> { + fn find_font_in_local_family<'a>(&'a mut self, + family_name: &LowercaseString, + desc: &FontTemplateDescriptor) + -> Option> { // TODO(Issue #188): look up localized font family names if canonical name not found // look up canonical name if self.local_families.contains_key(family_name) { @@ -205,8 +212,10 @@ impl FontCache { } } - fn find_font_in_web_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor) - -> Option> { + fn find_font_in_web_family<'a>(&'a mut self, + family_name: &LowercaseString, + desc: &FontTemplateDescriptor) + -> Option> { if self.web_families.contains_key(family_name) { let family = &mut self.web_families[*family_name]; let maybe_font = family.find_font_for_style(desc, &self.font_context); @@ -246,12 +255,13 @@ impl FontCache { /// the per-thread/task FontContext structures. #[deriving(Clone)] pub struct FontCacheTask { - chan: Sender, + pub client: SharedServerProxy, } impl FontCacheTask { pub fn new(resource_task: ResourceTask) -> FontCacheTask { - let (chan, port) = channel(); + let mut server = Server::new("FontCache"); + let client = Arc::new(Mutex::new(server.create_new_client())); spawn_named("FontCacheTask".to_owned(), proc() { // TODO: Allow users to specify these. @@ -263,7 +273,7 @@ impl FontCacheTask { add_generic_font(&mut generic_fonts, "monospace", "Menlo"); let mut cache = FontCache { - port: port, + server: server, generic_fonts: generic_fonts, local_families: HashMap::new(), web_families: HashMap::new(), @@ -276,49 +286,46 @@ impl FontCacheTask { }); FontCacheTask { - chan: chan, + client: client, + } + } + + #[inline] + pub fn from_client(client: SharedServerProxy) -> FontCacheTask { + FontCacheTask { + client: client, } } pub fn get_font_template(&self, family: String, desc: FontTemplateDescriptor) -> Option> { - let (response_chan, response_port) = channel(); - self.chan.send(Command::GetFontTemplate(family, desc, response_chan)); - - let reply = response_port.recv(); - - match reply { - Reply::GetFontTemplateReply(data) => { - data - } + let response = self.client.lock().send_sync(Command::GetFontTemplate(family, desc)); + if let Reply::GetFontTemplateReply(data) = response { + data + } else { + panic!("get_font_template(): unexpected server response") } } pub fn get_last_resort_font_template(&self, desc: FontTemplateDescriptor) -> Arc { - let (response_chan, response_port) = channel(); - self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)); - - let reply = response_port.recv(); - - match reply { - Reply::GetFontTemplateReply(data) => { - data.unwrap() - } + let response = self.client.lock().send_sync(Command::GetLastResortFontTemplate(desc)); + if let Reply::GetFontTemplateReply(data) = response { + data.unwrap() + } else { + panic!("get_font_template(): unexpected server response") } } pub fn add_web_font(&self, family: String, src: Source) { - let (response_chan, response_port) = channel(); - self.chan.send(Command::AddWebFont(family, src, response_chan)); - response_port.recv(); + self.client.lock().send_sync(Command::AddWebFont(family, src)); } - pub fn exit(&self) { - let (response_chan, response_port) = channel(); - self.chan.send(Command::Exit(response_chan)); - response_port.recv(); + pub fn create_new_client(&self) -> FontCacheTask { + FontCacheTask { + client: Arc::new(Mutex::new(self.client.lock().create_new_client())), + } } } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index e439d482f64f..d6cb52be758d 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -66,7 +66,6 @@ struct PaintFontCacheEntry { /// required. pub struct FontContext { platform_handle: FontContextHandle, - font_cache_task: FontCacheTask, /// TODO: See bug https://github.com/servo/servo/issues/3300. layout_font_cache: Vec, @@ -81,11 +80,10 @@ pub struct FontContext { } impl FontContext { - pub fn new(font_cache_task: FontCacheTask) -> FontContext { + pub fn new() -> FontContext { let handle = FontContextHandle::new(); FontContext { platform_handle: handle, - font_cache_task: font_cache_task, layout_font_cache: vec!(), fallback_font_cache: vec!(), paint_font_cache: vec!(), @@ -126,8 +124,10 @@ impl FontContext { /// Create a group of fonts for use in layout calculations. May return /// a cached font if this font instance has already been used by /// this context. - pub fn get_layout_font_group_for_style(&mut self, style: Arc) - -> Rc { + pub fn get_layout_font_group_for_style(&mut self, + font_cache_task: &FontCacheTask, + style: Arc) + -> Rc { let matches = match self.last_style { Some(ref last_style) => arc_ptr_eq(&style, last_style), None => false, @@ -168,9 +168,8 @@ impl FontContext { } if !cache_hit { - let font_template = self.font_cache_task.get_font_template(family.name() - .to_owned(), - desc.clone()); + let font_template = font_cache_task.get_font_template(family.name().into_string(), + desc.clone()); match font_template { Some(font_template) => { let layout_font = self.create_layout_font(font_template, @@ -210,7 +209,7 @@ impl FontContext { } if !cache_hit { - let font_template = self.font_cache_task.get_last_resort_font_template(desc.clone()); + let font_template = font_cache_task.get_last_resort_font_template(desc.clone()); let layout_font = self.create_layout_font(font_template, desc.clone(), style.font_size, @@ -250,9 +249,4 @@ impl FontContext { }); paint_font } - - /// Returns a reference to the font cache task. - pub fn font_cache_task(&self) -> FontCacheTask { - self.font_cache_task.clone() - } } diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 833495d21778..0618ae5f2a38 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -15,7 +15,7 @@ use font::FontHandleMethods; /// This is very basic at the moment and needs to be /// expanded or refactored when we support more of the /// font styling parameters. -#[deriving(Clone, Copy)] +#[deriving(Clone, Copy, Decodable, Encodable)] pub struct FontTemplateDescriptor { pub weight: font_weight::T, pub italic: bool, diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index efb1182a795f..36197492cf0d 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -20,24 +20,27 @@ use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsCo use layers::platform::surface::NativeSurface; use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet}; use layers; +use libc::c_int; use servo_msg::compositor_msg::{Epoch, PaintState, LayerId}; use servo_msg::compositor_msg::{LayerMetadata, PaintListener, ScrollPolicy}; use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; use servo_msg::constellation_msg::PipelineExitType; +use servo_net::server; +use servo_util::ipc::{mod, IpcSender}; use servo_util::geometry::{Au, ZERO_POINT}; use servo_util::opts; use servo_util::smallvec::SmallVec; -use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task_state; use servo_util::time::{TimeProfilerChan, TimeProfilerCategory, profile}; use std::comm::{Receiver, Sender, channel}; use std::mem; -use std::task::TaskBuilder; use std::sync::Arc; +use std::task as std_task; +use std::task::TaskBuilder; /// Information about a hardware graphics layer that layout sends to the painting task. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct PaintLayer { /// A per-pipeline ID describing this layer that should be stable across reflows. pub id: LayerId, @@ -58,6 +61,7 @@ impl PaintLayer { } } +#[deriving(Decodable, Encodable)] pub struct PaintRequest { pub buffer_requests: Vec, pub scale: f32, @@ -65,13 +69,24 @@ pub struct PaintRequest { pub epoch: Epoch, } +/// Messages from the compositor, pipeline, and/or layout tasks to the paint task. pub enum Msg { - PaintInit(Arc), + FromLayout(LayoutToPaintMsg), Paint(Vec), UnusedBuffer(Vec>), PaintPermissionGranted, PaintPermissionRevoked, - Exit(Option>, PipelineExitType), + Exit(PipelineExitType), +} + +/// Messages from the layout task to the paint task. +#[deriving(Decodable, Encodable)] +pub enum LayoutToPaintMsg { + /// A new display list has arrived. + SerializedPaintInit(Arc), + /// Same as `PaintInit`, but cast to a pointer. This is to be used only in + UnserializedPaintInit(uint), + Exit(PipelineExitType), } #[deriving(Clone)] @@ -92,6 +107,44 @@ impl PaintChan { let &PaintChan(ref chan) = self; chan.send_opt(msg) } + + /// Creates an IPC channel through which layout can send messages to the painting thread. This + /// spawns a helper thread to forward messages to the painter. + pub fn create_layout_channel(&self) -> LayoutToPaintChan { + let (ipc_receiver, ipc_sender) = ipc::channel(); + let &PaintChan(ref paint_channel) = self; + let paint_channel = (*paint_channel).clone(); + std_task::spawn(proc() { + while let Ok(msg) = ipc_receiver.recv_opt() { + if paint_channel.send_opt(Msg::FromLayout(msg)).is_err() { + return + } + } + }); + LayoutToPaintChan(ipc_sender) + } +} + +/// The channel on which the layout thread sends messages to the painting thread. +#[deriving(Clone)] +pub struct LayoutToPaintChan(IpcSender); + +impl LayoutToPaintChan { + #[inline] + pub fn from_channel(channel: IpcSender) -> LayoutToPaintChan { + LayoutToPaintChan(channel) + } + + #[inline] + pub fn fd(&self) -> c_int { + let LayoutToPaintChan(ref channel) = *self; + channel.fd() + } + + pub fn send(&self, msg: LayoutToPaintMsg) { + let &LayoutToPaintChan(ref chan) = self; + chan.send(msg) + } } pub struct PaintTask { @@ -141,80 +194,75 @@ impl PaintTask where C: PaintListener + Send { constellation_chan: ConstellationChan, font_cache_task: FontCacheTask, failure_msg: Failure, - time_profiler_chan: TimeProfilerChan, - shutdown_chan: Sender<()>) { + time_profiler_chan: TimeProfilerChan) { let ConstellationChan(c) = constellation_chan.clone(); - spawn_named_with_send_on_failure("PaintTask", task_state::PAINT, proc() { - { - // Ensures that the paint task and graphics context are destroyed before the - // shutdown message. - let mut compositor = compositor; - let native_graphics_context = compositor.get_graphics_metadata().map( - |md| NativePaintingGraphicsContext::from_metadata(&md)); - let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(), - font_cache_task, - time_profiler_chan.clone()); - - // FIXME: rust/#5967 - let mut paint_task = PaintTask { - id: id, - port: port, - compositor: compositor, - constellation_chan: constellation_chan, - time_profiler_chan: time_profiler_chan, - native_graphics_context: native_graphics_context, - root_stacking_context: None, - paint_permission: false, - epoch: Epoch(0), - buffer_map: BufferMap::new(10000000), - worker_threads: worker_threads, - used_buffer_count: 0, - }; - - paint_task.start(); - - // Destroy all the buffers. - match paint_task.native_graphics_context.as_ref() { - Some(ctx) => paint_task.buffer_map.clear(ctx), - None => (), - } + server::spawn_named_with_send_to_server_on_failure("PaintTask", task_state::PAINT, proc() { + let mut compositor = compositor; + let native_graphics_context = compositor.get_graphics_metadata().map(|md| { + NativePaintingGraphicsContext::from_metadata(&md) + }); + let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(), + font_cache_task, + time_profiler_chan.clone()); + + // FIXME: rust/#5967 + let mut paint_task = PaintTask { + id: id, + port: port, + compositor: compositor, + constellation_chan: constellation_chan, + time_profiler_chan: time_profiler_chan, + native_graphics_context: native_graphics_context, + root_stacking_context: None, + paint_permission: false, + epoch: Epoch(0), + buffer_map: BufferMap::new(10000000), + worker_threads: worker_threads, + used_buffer_count: 0, + }; - // Tell all the worker threads to shut down. - for worker_thread in paint_task.worker_threads.iter_mut() { - worker_thread.exit() - } + paint_task.start(); + + // Destroy all the buffers. + match paint_task.native_graphics_context.as_ref() { + Some(ctx) => paint_task.buffer_map.clear(ctx), + None => (), } - debug!("paint_task: shutdown_chan send"); - shutdown_chan.send(()); + // Tell all the worker threads to shut down. + for worker_thread in paint_task.worker_threads.iter_mut() { + worker_thread.exit() + } }, ConstellationMsg::Failure(failure_msg), c); } fn start(&mut self) { debug!("PaintTask: beginning painting loop"); - let mut exit_response_channel : Option> = None; let mut waiting_for_compositor_buffers_to_exit = false; loop { match self.port.recv() { - Msg::PaintInit(stacking_context) => { - self.root_stacking_context = Some(stacking_context.clone()); - - if !self.paint_permission { - debug!("PaintTask: paint ready msg"); - let ConstellationChan(ref mut c) = self.constellation_chan; - c.send(ConstellationMsg::PainterReady(self.id)); - continue; + Msg::FromLayout(LayoutToPaintMsg::SerializedPaintInit(stacking_context)) => { + self.root_stacking_context = Some(stacking_context); + self.received_paint_init(); + } + Msg::FromLayout(LayoutToPaintMsg::UnserializedPaintInit(stacking_context)) => { + // Security check: in multiprocess mode, an untrusted process can send these to + // us, so we must forbid them in that case. + if opts::get().multiprocess || opts::get().force_display_list_serialization { + panic!("`UnserializedPaintInit` messages not allowed in this mode!"); } - - self.epoch.next(); - self.initialize_layers(); + let stacking_context: Arc = unsafe { + mem::transmute(stacking_context) + }; + self.root_stacking_context = Some(stacking_context); + self.received_paint_init(); } Msg::Paint(requests) => { if !self.paint_permission { debug!("PaintTask: paint ready msg"); let ConstellationChan(ref mut c) = self.constellation_chan; - c.send(ConstellationMsg::PainterReady(self.id)); + c.lock().send_async(ConstellationMsg::PainterReady(self.id)); self.compositor.paint_msg_discarded(); continue; } @@ -250,7 +298,6 @@ impl PaintTask where C: PaintListener + Send { if waiting_for_compositor_buffers_to_exit && self.used_buffer_count == 0 { debug!("PaintTask: Received all loaned buffers, exiting."); - exit_response_channel.map(|channel| channel.send(())); break; } } @@ -265,7 +312,7 @@ impl PaintTask where C: PaintListener + Send { Msg::PaintPermissionRevoked => { self.paint_permission = false; } - Msg::Exit(response_channel, exit_type) => { + Msg::Exit(exit_type) | Msg::FromLayout(LayoutToPaintMsg::Exit(exit_type)) => { let should_wait_for_compositor_buffers = match exit_type { PipelineExitType::Complete => false, PipelineExitType::PipelineOnly => self.used_buffer_count != 0 @@ -273,7 +320,6 @@ impl PaintTask where C: PaintListener + Send { if !should_wait_for_compositor_buffers { debug!("PaintTask: Exiting without waiting for compositor buffers."); - response_channel.map(|channel| channel.send(())); break; } @@ -282,12 +328,23 @@ impl PaintTask where C: PaintListener + Send { // When doing a complete exit, the compositor lets all buffers leak. println!("PaintTask: Saw ExitMsg, {} buffers in use", self.used_buffer_count); waiting_for_compositor_buffers_to_exit = true; - exit_response_channel = response_channel; } } } } + fn received_paint_init(&mut self) { + if !self.paint_permission { + debug!("PaintTask: paint ready msg"); + let ConstellationChan(ref mut c) = self.constellation_chan; + c.lock().send_async(ConstellationMsg::PainterReady(self.id)); + return; + } + + self.epoch.next(); + self.initialize_layers(); + } + /// Retrieves an appropriately-sized layer buffer from the cache to match the requirements of /// the given tile, or creates one if a suitable one cannot be found. fn find_or_create_layer_buffer_for_tile(&mut self, tile: &BufferRequest, scale: f32) @@ -387,9 +444,10 @@ impl PaintTask where C: PaintListener + Send { page_position: &Point2D) { let page_position = stacking_context.bounds.origin + *page_position; if let Some(ref paint_layer) = stacking_context.layer { - // Layers start at the top left of their overflow rect, as far as the info we give to - // the compositor is concerned. - let overflow_relative_page_position = page_position + stacking_context.overflow.origin; + // Layers start at the top left of their overflow rect, as far as the info we give + // to the compositor is concerned. + let overflow_relative_page_position = page_position + + stacking_context.overflow.origin; let layer_position = Rect(Point2D(overflow_relative_page_position.x.to_nearest_px() as i32, overflow_relative_page_position.y.to_nearest_px() as i32), @@ -477,7 +535,7 @@ impl WorkerThread { fn new(sender: Sender, receiver: Receiver, native_graphics_metadata: Option, - font_cache_task: FontCacheTask, + _: FontCacheTask, time_profiler_sender: TimeProfilerChan) -> WorkerThread { WorkerThread { @@ -486,7 +544,7 @@ impl WorkerThread { native_graphics_context: native_graphics_metadata.map(|metadata| { NativePaintingGraphicsContext::from_metadata(&metadata) }), - font_context: box FontContext::new(font_cache_task.clone()), + font_context: box FontContext::new(), time_profiler_sender: time_profiler_sender, } } diff --git a/components/gfx/platform/freetype/font_template.rs b/components/gfx/platform/freetype/font_template.rs index f2be1ad6d148..6facfbd60abc 100644 --- a/components/gfx/platform/freetype/font_template.rs +++ b/components/gfx/platform/freetype/font_template.rs @@ -10,6 +10,7 @@ use std::io::File; /// The identifier is an absolute path, and the bytes /// field is the loaded data that can be passed to /// freetype and azure directly. +#[deriving(Encodable, Decodable)] pub struct FontTemplateData { pub bytes: Vec, pub identifier: String, diff --git a/components/gfx/platform/macos/font_template.rs b/components/gfx/platform/macos/font_template.rs index 9c00c729a20c..9534f5ef3c1c 100644 --- a/components/gfx/platform/macos/font_template.rs +++ b/components/gfx/platform/macos/font_template.rs @@ -6,6 +6,7 @@ use core_graphics::data_provider::CGDataProvider; use core_graphics::font::CGFont; use core_text::font::CTFont; use core_text; +use serialize::{Decodable, Decoder, Encodable, Encoder}; use std::borrow::ToOwned; @@ -16,12 +17,28 @@ use std::borrow::ToOwned; pub struct FontTemplateData { pub ctfont: Option, pub identifier: String, + pub font_data: Option>, +} + +impl Encodable for FontTemplateData where S: Encoder { + fn encode(&self, s: &mut S) -> Result<(),E> { + try!(self.identifier.encode(s)); + self.font_data.encode(s) + } +} + +impl Decodable for FontTemplateData where D: Decoder { + fn decode(d: &mut D) -> Result { + let identifier: String = try!(Decodable::decode(d)); + let font_data: Option> = try!(Decodable::decode(d)); + Ok(FontTemplateData::new(identifier.as_slice(), font_data)) + } } impl FontTemplateData { pub fn new(identifier: &str, font_data: Option>) -> FontTemplateData { let ctfont = match font_data { - Some(bytes) => { + Some(ref bytes) => { let fontprov = CGDataProvider::from_buffer(bytes.as_slice()); let cgfont_result = CGFont::from_data_provider(fontprov); match cgfont_result { @@ -37,6 +54,7 @@ impl FontTemplateData { FontTemplateData { ctfont: ctfont, identifier: identifier.to_owned(), + font_data: font_data, } } } diff --git a/components/gfx/text/glyph.rs b/components/gfx/text/glyph.rs index 312c0692847a..4b2ea80a4562 100644 --- a/components/gfx/text/glyph.rs +++ b/components/gfx/text/glyph.rs @@ -23,7 +23,7 @@ use geom::point::Point2D; /// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or /// glyph offsets), we pack the glyph count into GlyphEntry, and store the other glyph information /// in DetailedGlyphStore. -#[deriving(Clone, Show, Copy)] +#[deriving(Clone, Show, Copy, Decodable, Encodable)] struct GlyphEntry { value: u32, } @@ -252,7 +252,7 @@ impl GlyphEntry { // Stores data for a detailed glyph, in the case that several glyphs // correspond to one character, or the glyph's data couldn't be packed. -#[deriving(Clone, Show, Copy)] +#[deriving(Clone, Show, Copy, Decodable, Encodable)] struct DetailedGlyph { id: GlyphId, // glyph's advance, in the text's direction (RTL or RTL) @@ -271,7 +271,7 @@ impl DetailedGlyph { } } -#[deriving(PartialEq, Clone, Eq, Show, Copy)] +#[deriving(PartialEq, Clone, Eq, Show, Copy, Decodable, Encodable)] struct DetailedGlyphRecord { // source string offset/GlyphEntry offset in the TextRun entry_offset: CharIndex, @@ -295,6 +295,7 @@ impl Ord for DetailedGlyphRecord { // until a lookup is actually performed; this matches the expected // usage pattern of setting/appending all the detailed glyphs, and // then querying without setting. +#[deriving(Encodable, Decodable)] struct DetailedGlyphStore { // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector // optimization. @@ -500,6 +501,7 @@ impl<'a> GlyphInfo<'a> { /// | +---+---+ | /// +---------------------------------------------+ /// ~~~ +#[deriving(Encodable, Decodable)] pub struct GlyphStore { // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector // optimization. @@ -514,7 +516,7 @@ pub struct GlyphStore { } int_range_index! { - #[deriving(Encodable)] + #[deriving(Decodable, Encodable)] #[doc = "An index that refers to a character in a text run. This could \ point to the middle of a glyph."] struct CharIndex(int) diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index da5b6f3435e5..cabb74c54740 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -14,7 +14,7 @@ use std::sync::Arc; use text::glyph::{CharIndex, GlyphStore}; /// A single "paragraph" of text in one font size and style. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct TextRun { pub text: Arc, pub font_template: Arc, @@ -25,7 +25,7 @@ pub struct TextRun { } /// A single series of glyphs within a text run. -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct GlyphRun { /// The glyphs. pub glyph_store: Arc, diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 9445eef9c04b..3b950ac24a41 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -333,6 +333,9 @@ impl<'a> FlowConstructor<'a> { // for runs might collapse so much whitespace away that only hypothetical fragments // remain. In that case the inline flow will compute its ascent and descent to be zero. let fragments = TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), + &self.layout_context + .shared + .font_cache_task, fragments); let mut inline_flow_ref = FlowRef::new(box InlineFlow::from_fragments(fragments, node.style().writing_mode)); @@ -348,6 +351,9 @@ impl<'a> FlowConstructor<'a> { let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.layout_context.font_context(), + &self.layout_context + .shared + .font_cache_task, &**node.style()); inline_flow.minimum_block_size_above_baseline = ascent; inline_flow.minimum_depth_below_baseline = descent; @@ -991,9 +997,11 @@ impl<'a> FlowConstructor<'a> { let mut unscanned_marker_fragments = DList::new(); unscanned_marker_fragments.push_back(Fragment::new_from_specific_info( node, - SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::from_text(text)))); + SpecificFragmentInfo::UnscannedText( + UnscannedTextFragmentInfo::from_text(text)))); let marker_fragments = TextRunScanner::new().scan_for_runs( self.layout_context.font_context(), + &self.layout_context.shared.font_cache_task, unscanned_marker_fragments); debug_assert!(marker_fragments.len() == 1); marker_fragments.fragments.into_iter().next() diff --git a/components/layout/context.rs b/components/layout/context.rs index b329a20221de..1cce57f7c0b2 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -30,11 +30,11 @@ struct LocalLayoutContext { thread_local!(static LOCAL_CONTEXT_KEY: Cell<*mut LocalLayoutContext> = Cell::new(ptr::null_mut())) -fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) -> *mut LocalLayoutContext { +fn create_or_get_local_context() -> *mut LocalLayoutContext { LOCAL_CONTEXT_KEY.with(|ref r| { if r.get().is_null() { let context = box LocalLayoutContext { - font_context: FontContext::new(shared_layout_context.font_cache_task.clone()), + font_context: FontContext::new(), applicable_declarations_cache: ApplicableDeclarationsCache::new(), style_sharing_candidate_cache: StyleSharingCandidateCache::new(), }; @@ -87,9 +87,7 @@ pub struct LayoutContext<'a> { impl<'a> LayoutContext<'a> { pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> { - - let local_context = create_or_get_local_context(shared_layout_context); - + let local_context = create_or_get_local_context(); LayoutContext { shared: shared_layout_context, cached_local_layout_context: local_context, diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 73d0461e13d8..94b179344edd 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -26,7 +26,7 @@ use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use gfx::color; use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BorderRadii, BoxShadowDisplayItem, ClippingRegion}; -use gfx::display_list::{DisplayItem, DisplayList, DisplayItemMetadata}; +use gfx::display_list::{DisplayItem, DisplayItemImage, DisplayList, DisplayItemMetadata}; use gfx::display_list::{GradientDisplayItem}; use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem}; use gfx::display_list::TextOrientation; @@ -37,7 +37,6 @@ use png; use png::PixelsByColorType; use servo_msg::compositor_msg::ScrollPolicy; use servo_msg::constellation_msg::Msg as ConstellationMsg; -use servo_msg::constellation_msg::ConstellationChan; use servo_net::image::holder::ImageHolder; use servo_util::cursor::Cursor; use servo_util::geometry::{mod, Au, to_px}; @@ -376,7 +375,7 @@ impl FragmentDisplayListBuilding for Fragment { style, Cursor::DefaultCursor), clip), - image: image.clone(), + image: DisplayItemImage(image.clone()), stretch_size: Size2D(Au::from_px(image.width as int), Au::from_px(image.height as int)), }), level); @@ -860,7 +859,7 @@ impl FragmentDisplayListBuilding for Fragment { &*self.style, Cursor::DefaultCursor), (*clip).clone()), - image: image.clone(), + image: DisplayItemImage(image.clone()), stretch_size: stacking_relative_content_box.size, })); } else { @@ -891,11 +890,11 @@ impl FragmentDisplayListBuilding for Fragment { &*self.style, Cursor::DefaultCursor), (*clip).clone()), - image: Arc::new(box png::Image { + image: DisplayItemImage(Arc::new(box png::Image { width: width as u32, height: height as u32, pixels: PixelsByColorType::RGBA8(canvas_data), - }), + })), stretch_size: stacking_relative_content_box.size, }; @@ -919,7 +918,7 @@ impl FragmentDisplayListBuilding for Fragment { debug!("finalizing position and size of iframe for {},{}", iframe_fragment.pipeline_id, iframe_fragment.subpage_id); - let ConstellationChan(ref chan) = layout_context.shared.constellation_chan; + let chan = &layout_context.shared.constellation_chan; chan.send(ConstellationMsg::FrameRect(iframe_fragment.pipeline_id, iframe_fragment.subpage_id, iframe_rect)); diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 2eb26244059a..b61e546a1ed9 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -904,7 +904,9 @@ impl Fragment { pub fn calculate_line_height(&self, layout_context: &LayoutContext) -> Au { let font_style = self.style.get_font_arc(); - let font_metrics = text::font_metrics_for_style(layout_context.font_context(), font_style); + let font_metrics = text::font_metrics_for_style(layout_context.font_context(), + &layout_context.shared.font_cache_task, + font_style); text::line_height_from_style(&*self.style, &font_metrics) } @@ -1613,6 +1615,8 @@ impl Fragment { let block_flow = info.flow_ref.as_immutable_block(); let font_style = self.style.get_font_arc(); let font_metrics = text::font_metrics_for_style(layout_context.font_context(), + &layout_context.shared + .font_cache_task, font_style); InlineMetrics::from_block_height(&font_metrics, block_flow.base.position.size.block + diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 5944759f053c..8acd17ff2679 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -22,6 +22,7 @@ use text; use collections::{RingBuf}; use geom::{Point2D, Rect}; use gfx::font::FontMetrics; +use gfx::font_cache_task::FontCacheTask; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; use servo_util::arc_ptr_eq; @@ -890,6 +891,7 @@ impl InlineFlow { /// `style` is the style of the block. pub fn compute_minimum_ascent_and_descent(&self, font_context: &mut FontContext, + font_cache_task: &FontCacheTask, style: &ComputedValues) -> (Au, Au) { // As a special case, if this flow contains only hypothetical fragments, then the entire @@ -899,7 +901,7 @@ impl InlineFlow { } let font_style = style.get_font_arc(); - let font_metrics = text::font_metrics_for_style(font_context, font_style); + let font_metrics = text::font_metrics_for_style(font_context, font_cache_task, font_style); let line_height = text::line_height_from_style(style, &font_metrics); let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height); @@ -913,7 +915,9 @@ impl InlineFlow { Some(ref inline_context) => { for style in inline_context.styles.iter() { let font_style = style.get_font_arc(); - let font_metrics = text::font_metrics_for_style(font_context, font_style); + let font_metrics = text::font_metrics_for_style(font_context, + font_cache_task, + font_style); let line_height = text::line_height_from_style(&**style, &font_metrics); let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height); diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 6d91a303ecf8..819644ed2e15 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -28,8 +28,7 @@ use gfx::color; use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, OpaqueNode}; use gfx::display_list::{StackingContext}; use gfx::font_cache_task::FontCacheTask; -use gfx::paint_task::{PaintChan, PaintLayer}; -use gfx::paint_task::Msg as PaintMsg; +use gfx::paint_task::{LayoutToPaintChan, LayoutToPaintMsg, PaintLayer}; use layout_traits::{LayoutControlMsg, LayoutTaskFactory}; use log; use script::dom::bindings::js::JS; @@ -42,28 +41,30 @@ use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC}; use script::layout_interface::{MouseOverResponse, Msg}; use script::layout_interface::{Reflow, ReflowGoal, ScriptLayoutChan, TrustedNodeAddress}; use script_traits::{ConstellationControlMsg, CompositorEvent, OpaqueScriptLayoutChannel}; -use script_traits::{ScriptControlChan, UntrustedNodeAddress}; +use script_traits::{UntrustedNodeAddress}; use servo_msg::compositor_msg::ScrollPolicy; use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_net::resource_task::{ResourceTask, load_bytes_iter}; +use servo_net::server; +use servo_util::ipc::IpcReceiver; use servo_util::cursor::Cursor; use servo_util::geometry::Au; use servo_util::logical_geometry::LogicalPoint; use servo_util::opts; -use servo_util::smallvec::{SmallVec, SmallVec1, VecLike}; -use servo_util::task::spawn_named_with_send_on_failure; +use servo_util::smallvec::SmallVec; use servo_util::task_state; use servo_util::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan}; use servo_util::time::{TimerMetadataFrameType, TimerMetadataReflowType, profile}; use servo_util::workqueue::WorkQueue; use std::borrow::ToOwned; use std::cell::Cell; -use std::comm::{channel, Sender, Receiver, Select}; +use std::comm::{Sender, Receiver, Select}; use std::mem; use std::ptr; +use std::task as std_task; use style::computed_values::{filter, mix_blend_mode}; use style::{StylesheetOrigin, Stylesheet, Stylist, TNode, iter_font_face_rules}; use style::{MediaType, Device}; @@ -86,6 +87,7 @@ pub struct LayoutTaskData { /// The root stacking context. pub stacking_context: Option>, + /// The CSS resolution context. pub stylist: Box, /// The workers that we use for parallel operation. @@ -116,14 +118,14 @@ pub struct LayoutTask { /// The port on which we receive messages from the constellation pub pipeline_port: Receiver, - //// The channel to send messages to ourself. + /// The channel to send messages to ourselves. pub chan: LayoutChan, /// The channel on which messages can be sent to the script task. - pub script_chan: ScriptControlChan, + pub script_chan: Sender, /// The channel on which messages can be sent to the painting task. - pub paint_chan: PaintChan, + pub paint_chan: LayoutToPaintChan, /// The channel on which messages can be sent to the time profiler. pub time_profiler_chan: TimeProfilerChan, @@ -149,7 +151,7 @@ pub struct LayoutTask { struct LayoutImageResponder { id: PipelineId, - script_chan: ScriptControlChan, + script_chan: Sender, } impl ImageResponder for LayoutImageResponder { @@ -158,12 +160,12 @@ impl ImageResponder for LayoutImageResponder { let script_chan = self.script_chan.clone(); let f: proc(ImageResponseMsg, UntrustedNodeAddress):Send = proc(_, node_address) { - let ScriptControlChan(chan) = script_chan; - debug!("Dirtying {:x}", node_address.0 as uint); - let mut nodes = SmallVec1::new(); - nodes.vec_push(node_address); - drop(chan.send_opt(ConstellationControlMsg::SendEvent( - id.clone(), CompositorEvent::ReflowEvent(nodes)))) + debug!("Dirtying {:x}", node_address.to_uint()); + let mut nodes = Vec::new(); + nodes.push(node_address); + drop(script_chan.send_opt(ConstellationControlMsg::SendEvent( + id.clone(), + CompositorEvent::ReflowEvent(nodes)))) }; f } @@ -172,38 +174,34 @@ impl ImageResponder for LayoutImageResponder { impl LayoutTaskFactory for LayoutTask { /// Spawns a new layout task. fn create(_phantom: Option<&mut LayoutTask>, - id: PipelineId, - chan: OpaqueScriptLayoutChannel, - pipeline_port: Receiver, - constellation_chan: ConstellationChan, - failure_msg: Failure, - script_chan: ScriptControlChan, - paint_chan: PaintChan, - resource_task: ResourceTask, - img_cache_task: ImageCacheTask, - font_cache_task: FontCacheTask, - time_profiler_chan: TimeProfilerChan, - shutdown_chan: Sender<()>) { + id: PipelineId, + chan: OpaqueScriptLayoutChannel, + pipeline_port: IpcReceiver, + constellation_chan: ConstellationChan, + failure_msg: Failure, + script_chan: Sender, + paint_chan: LayoutToPaintChan, + resource_task: ResourceTask, + img_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, + time_profiler_chan: TimeProfilerChan) { let ConstellationChan(con_chan) = constellation_chan.clone(); - spawn_named_with_send_on_failure("LayoutTask", task_state::LAYOUT, proc() { - { // Ensures layout task is destroyed before we send shutdown message - let sender = chan.sender(); - let layout = - LayoutTask::new( - id, - chan.receiver(), - LayoutChan(sender), - pipeline_port, - constellation_chan, - script_chan, - paint_chan, - resource_task, - img_cache_task, - font_cache_task, - time_profiler_chan); - layout.start(); - } - shutdown_chan.send(()); + server::spawn_named_with_send_to_server_on_failure("LayoutTask", + task_state::LAYOUT, + proc() { + let sender = chan.sender(); + let layout = LayoutTask::new(id, + chan.receiver(), + LayoutChan(sender), + pipeline_port, + constellation_chan, + script_chan, + paint_chan, + resource_task, + img_cache_task, + font_cache_task, + time_profiler_chan); + layout.start(); }, ConstellationMsg::Failure(failure_msg), con_chan); } } @@ -242,10 +240,10 @@ impl LayoutTask { fn new(id: PipelineId, port: Receiver, chan: LayoutChan, - pipeline_port: Receiver, + pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, - script_chan: ScriptControlChan, - paint_chan: PaintChan, + script_chan: Sender, + paint_chan: LayoutToPaintChan, resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, @@ -254,18 +252,29 @@ impl LayoutTask { let local_image_cache = Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone()))); let screen_size = Size2D(Au(0), Au(0)); - let device = Device::new(MediaType::Screen, opts::get().initial_window_size.as_f32() * ScaleFactor(1.0)); + let device = Device::new(MediaType::Screen, + opts::get().initial_window_size.as_f32() * ScaleFactor(1.0)); let parallel_traversal = if opts::get().layout_threads != 1 { - Some(WorkQueue::new("LayoutWorker", task_state::LAYOUT, - opts::get().layout_threads, ptr::null())) + Some(WorkQueue::new("LayoutWorker", + task_state::LAYOUT, + opts::get().layout_threads, + ptr::null())) } else { None }; + // Create a proxy to proxy messages received from the pipeline over IPC to us. + let (proxy_pipeline_sender, proxy_pipeline_receiver) = channel(); + std_task::spawn(proc() { + while let Ok(msg) = pipeline_port.recv_opt() { + proxy_pipeline_sender.send(msg) + } + }); + LayoutTask { id: id, port: port, - pipeline_port: pipeline_port, + pipeline_port: proxy_pipeline_receiver, chan: chan, script_chan: script_chan, paint_chan: paint_chan, @@ -274,19 +283,18 @@ impl LayoutTask { image_cache_task: image_cache_task.clone(), font_cache_task: font_cache_task, first_reflow: Cell::new(true), - rw_data: Arc::new(Mutex::new( - LayoutTaskData { - local_image_cache: local_image_cache, - constellation_chan: constellation_chan, - screen_size: screen_size, - stacking_context: None, - stylist: box Stylist::new(device), - parallel_traversal: parallel_traversal, - dirty: Rect::zero(), - generation: 0, - content_box_response: Rect::zero(), - content_boxes_response: Vec::new(), - })), + rw_data: Arc::new(Mutex::new(LayoutTaskData { + local_image_cache: local_image_cache, + constellation_chan: constellation_chan, + screen_size: screen_size, + stacking_context: None, + stylist: box Stylist::new(device), + parallel_traversal: parallel_traversal, + dirty: Rect::zero(), + generation: 0, + content_box_response: Rect::zero(), + content_boxes_response: Vec::new(), + })), } } @@ -426,9 +434,9 @@ impl LayoutTask { true } - /// Enters a quiescent state in which no new messages except for `layout_interface::Msg::ReapLayoutData` will be - /// processed until an `ExitNowMsg` is received. A pong is immediately sent on the given - /// response channel. + /// Enters a quiescent state in which no new messages except for + /// `layout_interface::Msg::ReapLayoutData` will be processed until an `ExitNowMsg` is + /// received. A pong is immediately sent on the given response channel. fn prepare_to_exit<'a>(&'a self, response_chan: Sender<()>, possibly_locked_rw_data: &mut Option>) { @@ -458,8 +466,6 @@ impl LayoutTask { fn exit_now<'a>(&'a self, possibly_locked_rw_data: &mut Option>, exit_type: PipelineExitType) { - let (response_chan, response_port) = channel(); - { let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); match (&mut *rw_data).parallel_traversal { @@ -469,8 +475,7 @@ impl LayoutTask { LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data); } - self.paint_chan.send(PaintMsg::Exit(Some(response_chan), exit_type)); - response_port.recv() + self.paint_chan.send(LayoutToPaintMsg::Exit(exit_type)); } fn handle_load_stylesheet<'a>(&'a self, @@ -706,7 +711,14 @@ impl LayoutTask { debug!("Layout done!"); - self.paint_chan.send(PaintMsg::PaintInit(stacking_context)); + if opts::get().multiprocess || opts::get().force_display_list_serialization { + self.paint_chan.send(LayoutToPaintMsg::SerializedPaintInit(stacking_context)); + } else { + unsafe { + self.paint_chan.send(LayoutToPaintMsg::UnserializedPaintInit(mem::transmute( + stacking_context))); + } + } }); } @@ -874,8 +886,7 @@ impl LayoutTask { // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without // either select or a filtered recv() that only looks for messages of a given type. data.script_join_chan.send(()); - let ScriptControlChan(ref chan) = data.script_chan; - chan.send(ConstellationControlMsg::ReflowComplete(self.id, data.id)); + self.script_chan.send(ConstellationControlMsg::ReflowComplete(self.id, data.id)); } unsafe fn dirty_all_nodes(node: &mut LayoutNode) { @@ -989,7 +1000,7 @@ impl LayoutRPC for LayoutRPCImpl { let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); { let &LayoutRPCImpl(ref rw_data) = self; - let rw_data = rw_data.lock(); + let mut rw_data = rw_data.lock(); match rw_data.stacking_context { None => panic!("no root stacking context!"), Some(ref stacking_context) => { @@ -1003,7 +1014,7 @@ impl LayoutRPC for LayoutRPCImpl { } else { Cursor::DefaultCursor }; - let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan; + let constellation_chan = &mut rw_data.constellation_chan; constellation_chan.send(ConstellationMsg::SetCursor(cursor)); } diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 6bd59cd8cc60..8b928cbae092 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -88,10 +88,12 @@ pub trait ParallelPreorderDomTraversal : PreorderDomTraversal { unsafe_node: UnsafeLayoutNode, proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>, top_down_func: extern "Rust" fn(UnsafeFlow, - &mut WorkerProxy<*const SharedLayoutContext, + &mut WorkerProxy<*const + SharedLayoutContext, UnsafeLayoutNode>), bottom_up_func: extern "Rust" fn(UnsafeFlow, - &mut WorkerProxy<*const SharedLayoutContext, + &mut WorkerProxy<*const + SharedLayoutContext, UnsafeFlow>)) { // Get a real layout node. let node: LayoutNode = unsafe { @@ -168,7 +170,8 @@ trait ParallelPostorderDomTraversal : PostorderDomTraversal { unsafe_node = layout_node_to_unsafe_layout_node(&parent); - let parent_layout_data: &mut LayoutDataWrapper = mem::transmute(parent_layout_data); + let parent_layout_data: &mut LayoutDataWrapper = + mem::transmute(parent_layout_data); if parent_layout_data .data .parallel @@ -268,10 +271,12 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>, top_down_func: extern "Rust" fn(UnsafeFlow, - &mut WorkerProxy<*const SharedLayoutContext, + &mut WorkerProxy<*const + SharedLayoutContext, UnsafeFlow>), bottom_up_func: extern "Rust" fn(UnsafeFlow, - &mut WorkerProxy<*const SharedLayoutContext, + &mut WorkerProxy<*const + SharedLayoutContext, UnsafeFlow>)) { let mut had_children = false; unsafe { @@ -373,7 +378,8 @@ fn assign_inline_sizes(unsafe_flow: UnsafeFlow, } fn assign_block_sizes_and_store_overflow(unsafe_flow: UnsafeFlow, - proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) { + proxy: &mut WorkerProxy<*const SharedLayoutContext, + UnsafeFlow>) { let shared_layout_context = unsafe { &**proxy.user_data() }; let layout_context = LayoutContext::new(shared_layout_context); let assign_block_sizes_traversal = AssignBSizesAndStoreOverflow { @@ -449,7 +455,8 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, profiler_metadata: ProfilerMetadata, time_profiler_chan: TimeProfilerChan, shared_layout_context: &SharedLayoutContext, - queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) { + queue: &mut WorkQueue<*const SharedLayoutContext, + UnsafeFlow>) { queue.data = shared_layout_context as *const _; profile(TimeProfilerCategory::LayoutParallelWarmup, profiler_metadata, diff --git a/components/layout/text.rs b/components/layout/text.rs index 324604281b4a..724e8afcf7cf 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -11,6 +11,7 @@ use inline::InlineFragments; use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG}; use gfx::font::{RunMetrics, ShapingFlags, ShapingOptions}; +use gfx::font_cache_task::FontCacheTask; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; @@ -40,7 +41,10 @@ impl TextRunScanner { } } - pub fn scan_for_runs(&mut self, font_context: &mut FontContext, mut fragments: DList) + pub fn scan_for_runs(&mut self, + font_context: &mut FontContext, + font_cache_task: &FontCacheTask, + mut fragments: DList) -> InlineFragments { debug!("TextRunScanner: scanning {} fragments for text runs...", fragments.len()); @@ -61,6 +65,7 @@ impl TextRunScanner { // Flush that clump to the list of fragments we're building up. last_whitespace = self.flush_clump_to_list(font_context, + font_cache_task, &mut new_fragments, last_whitespace); } @@ -79,6 +84,7 @@ impl TextRunScanner { /// be adjusted. fn flush_clump_to_list(&mut self, font_context: &mut FontContext, + font_cache_task: &FontCacheTask, out_fragments: &mut Vec, mut last_whitespace: bool) -> bool { @@ -114,7 +120,8 @@ impl TextRunScanner { let in_fragment = self.clump.front().unwrap(); let font_style = in_fragment.style().get_font_arc(); let inherited_text_style = in_fragment.style().get_inheritedtext(); - fontgroup = font_context.get_layout_font_group_for_style(font_style); + fontgroup = font_context.get_layout_font_group_for_style(font_cache_task, + font_style); compression = match in_fragment.white_space() { white_space::T::normal | white_space::T::nowrap => { CompressionMode::CompressWhitespaceNewline @@ -299,9 +306,11 @@ fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode) /// /// `#[inline]` because often the caller only needs a few fields from the font metrics. #[inline] -pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: Arc) +pub fn font_metrics_for_style(font_context: &mut FontContext, + font_cache_task: &FontCacheTask, + font_style: Arc) -> FontMetrics { - let fontgroup = font_context.get_layout_font_group_for_style(font_style); + let fontgroup = font_context.get_layout_font_group_for_style(font_cache_task, font_style); fontgroup.fonts.get(0).borrow().metrics.clone() } diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index 178861d7a938..3b314d55af30 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -12,27 +12,31 @@ extern crate "msg" as servo_msg; extern crate "net" as servo_net; extern crate "util" as servo_util; +extern crate serialize; + // This module contains traits in layout used generically // in the rest of Servo. // The traits are here instead of in layout so // that these modules won't have to depend on layout. use gfx::font_cache_task::FontCacheTask; -use gfx::paint_task::PaintChan; +use gfx::paint_task::LayoutToPaintChan; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType}; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; +use servo_util::ipc::{IpcReceiver, IpcSender}; use servo_util::time::TimeProfilerChan; -use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; +use script_traits::{ConstellationControlMsg, OpaqueScriptLayoutChannel}; use std::comm::Sender; /// Messages sent to the layout task from the constellation +#[deriving(Decodable, Encodable)] pub enum LayoutControlMsg { ExitNowMsg(PipelineExitType), } /// A channel wrapper for constellation messages -pub struct LayoutControlChan(pub Sender); +pub struct LayoutControlChan(pub IpcSender); // A static method creating a layout task // Here to remove the compositor -> layout dependency @@ -41,14 +45,13 @@ pub trait LayoutTaskFactory { fn create(_phantom: Option<&mut Self>, id: PipelineId, chan: OpaqueScriptLayoutChannel, - pipeline_port: Receiver, + pipeline_port: IpcReceiver, constellation_chan: ConstellationChan, failure_msg: Failure, - script_chan: ScriptControlChan, - paint_chan: PaintChan, + script_chan: Sender, + paint_chan: LayoutToPaintChan, resource_task: ResourceTask, img_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, - time_profiler_chan: TimeProfilerChan, - shutdown_chan: Sender<()>); + time_profiler_chan: TimeProfilerChan); } diff --git a/components/msg/Cargo.toml b/components/msg/Cargo.toml index 460a277a7a78..264dbef2a04b 100644 --- a/components/msg/Cargo.toml +++ b/components/msg/Cargo.toml @@ -8,6 +8,9 @@ authors = ["The Servo Project Developers"] name = "msg" path = "lib.rs" +[dependencies.net] +path = "../net" + [dependencies.style] path = "../style" diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index a2e3c4d19095..4529fbbbab27 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -20,7 +20,7 @@ pub enum PaintState { Painting, } -#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone, Show, Copy)] +#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone, Show, Copy, Decodable, Encodable)] pub enum ReadyState { /// Informs the compositor that nothing has been done yet. Used for setting status Blank, @@ -33,7 +33,7 @@ pub enum ReadyState { } /// A newtype struct for denoting the age of messages; prevents race conditions. -#[deriving(PartialEq, Eq, Show, Copy)] +#[deriving(PartialEq, Eq, Show, Copy, Decodable, Encodable)] pub struct Epoch(pub uint); impl Epoch { @@ -43,7 +43,7 @@ impl Epoch { } } -#[deriving(Clone, PartialEq, Eq, Copy)] +#[deriving(Clone, PartialEq, Eq, Copy, Decodable, Encodable)] pub struct LayerId(pub uint, pub uint); impl Show for LayerId { @@ -61,7 +61,7 @@ impl LayerId { } /// The scrolling policy of a layer. -#[deriving(Clone, PartialEq, Eq, Copy)] +#[deriving(Clone, PartialEq, Eq, Copy, Decodable, Encodable)] pub enum ScrollPolicy { /// These layers scroll when the parent receives a scrolling message. Scrollable, @@ -107,15 +107,12 @@ pub trait PaintListener for Sized? { /// The interface used by the script task to tell the compositor to update its ready state, /// which is used in displaying the appropriate message in the window's title. -pub trait ScriptListener { - fn set_ready_state(&mut self, PipelineId, ReadyState); - fn scroll_fragment_point(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - point: Point2D); +#[deriving(Decodable, Encodable)] +pub enum ScriptToCompositorMsg { + SetReadyState(PipelineId, ReadyState), + ScrollFragmentPoint(PipelineId, LayerId, Point2D), /// Informs the compositor that the title of the page with the given pipeline ID has changed. - fn set_title(&mut self, pipeline_id: PipelineId, new_title: Option); - fn close(&mut self); - fn dup(&mut self) -> Box; - fn send_key_event(&mut self, key: Key, state: KeyState, modifiers: KeyModifiers); + SetTitle(PipelineId, Option), + SendKeyEvent(Key, KeyState, KeyModifiers), } + diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 5cd2a0cce391..50a8ef4ddbbf 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -11,35 +11,56 @@ use geom::scale_factor::ScaleFactor; use hyper::header::Headers; use hyper::method::Method; use layers::geometry::DevicePixel; +use libc::c_int; +use servo_net::server::SharedServerProxy; use servo_util::cursor::Cursor; use servo_util::geometry::{PagePx, ViewportPx}; -use std::comm::{channel, Sender, Receiver}; use url::Url; #[deriving(Clone)] -pub struct ConstellationChan(pub Sender); +pub struct ConstellationChan(pub SharedServerProxy); impl ConstellationChan { - pub fn new() -> (Receiver, ConstellationChan) { - let (chan, port) = channel(); - (port, ConstellationChan(chan)) + #[inline] + pub fn from_server_proxy(server_proxy: SharedServerProxy) -> ConstellationChan { + ConstellationChan(server_proxy) + } + + #[inline] + pub fn fds(&self) -> (c_int, c_int) { + let ConstellationChan(ref channel) = *self; + channel.lock().fds() + } + + /// Currently, the constellation handles only asynchronous messages, so we provide this message + /// for convenience. + #[inline] + pub fn send(&self, msg: Msg) { + let ConstellationChan(ref channel) = *self; + channel.lock().send_async(msg) + } + + #[inline] + pub fn server_proxy(&self) -> &SharedServerProxy { + let ConstellationChan(ref channel) = *self; + channel } } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Encodable, Decodable)] pub enum IFrameSandboxState { IFrameSandboxed, IFrameUnsandboxed } // We pass this info to various tasks, so it lives in a separate, cloneable struct. -#[deriving(Clone, Copy)] +#[deriving(Clone, Copy, Encodable, Decodable)] pub struct Failure { pub pipeline_id: PipelineId, pub subpage_id: Option, } -#[deriving(Copy)] +#[deriving(Copy, Encodable, Decodable)] pub struct WindowSizeData { /// The size of the initial layout viewport, before parsing an /// http://www.w3.org/TR/css-device-adapt/#initial-viewport @@ -52,7 +73,7 @@ pub struct WindowSizeData { pub device_pixel_ratio: ScaleFactor, } -#[deriving(PartialEq, Eq, Copy, Clone)] +#[deriving(PartialEq, Eq, Copy, Clone, Encodable, Decodable)] pub enum KeyState { Pressed, Released, @@ -60,7 +81,7 @@ pub enum KeyState { } //N.B. Straight up copied from glfw-rs -#[deriving(Show, PartialEq, Eq, Copy, Clone)] +#[deriving(Show, PartialEq, Eq, Copy, Clone, Encodable, Decodable)] pub enum Key { Space, Apostrophe, @@ -186,7 +207,7 @@ pub enum Key { } bitflags! { - #[deriving(Copy)] + #[deriving(Copy, Decodable, Encodable)] flags KeyModifiers: u8 { const SHIFT = 0x01, const CONTROL = 0x02, @@ -196,6 +217,7 @@ bitflags! { } /// Messages from the compositor and script to the constellation. +#[deriving(Decodable, Encodable)] pub enum Msg { Exit, Failure(Failure), @@ -218,7 +240,7 @@ pub enum Msg { /// Similar to net::resource_task::LoadData /// can be passed to LoadUrl to load a page with GET/POST /// parameters or headers -#[deriving(Clone)] +#[deriving(Clone, Encodable, Decodable)] pub struct LoadData { pub url: Url, pub method: Method, @@ -244,21 +266,21 @@ pub enum NavigationType { Navigate, // browser forward/back buttons } -#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show)] +#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show, Decodable, Encodable)] pub enum NavigationDirection { Forward, Back, } -#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show)] +#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show, Encodable, Decodable)] pub struct PipelineId(pub uint); -#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show)] +#[deriving(Clone, PartialEq, Eq, Copy, Hash, Show, Encodable, Decodable)] pub struct SubpageId(pub uint); // The type of pipeline exit. During complete shutdowns, pipelines do not have to // release resources automatically released on process termination. -#[deriving(Copy)] +#[deriving(Copy, Encodable, Decodable)] pub enum PipelineExitType { PipelineOnly, Complete, diff --git a/components/msg/lib.rs b/components/msg/lib.rs index 234fba053616..bd5cb60d5a11 100644 --- a/components/msg/lib.rs +++ b/components/msg/lib.rs @@ -10,7 +10,9 @@ extern crate azure; extern crate geom; extern crate hyper; extern crate layers; +extern crate libc; extern crate serialize; +extern crate "net" as servo_net; extern crate "util" as servo_util; extern crate url; @@ -21,3 +23,4 @@ extern crate io_surface; pub mod compositor_msg; pub mod constellation_msg; + diff --git a/components/net/image_cache_task.rs b/components/net/image_cache_task.rs index 3197389caa5b..a682b5a8b4c0 100644 --- a/components/net/image_cache_task.rs +++ b/components/net/image_cache_task.rs @@ -15,7 +15,6 @@ use std::collections::HashMap; use std::collections::hash_map::{Occupied, Vacant}; use std::mem::replace; use std::sync::{Arc, Mutex}; -use serialize::{Encoder, Encodable}; use url::Url; pub enum Msg { @@ -73,12 +72,6 @@ pub struct ImageCacheTask { chan: Sender, } -impl> Encodable for ImageCacheTask { - fn encode(&self, _: &mut S) -> Result<(), E> { - Ok(()) - } -} - type DecoderFactory = fn() -> (proc(&[u8]) : 'static -> Option); impl ImageCacheTask { diff --git a/components/net/lib.rs b/components/net/lib.rs index de1335b5c3b8..6ab8e28da375 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -2,7 +2,7 @@ * 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/. */ -#![feature(default_type_params, globs, phase)] +#![feature(default_type_params, globs, phase, unsafe_destructor)] #![deny(unused_imports)] #![deny(unused_variables)] @@ -14,6 +14,7 @@ extern crate hyper; extern crate png; #[phase(plugin, link)] extern crate log; +extern crate libc; extern crate serialize; extern crate "util" as servo_util; extern crate stb_image; @@ -38,6 +39,7 @@ pub mod image_cache_task; pub mod local_image_cache; pub mod resource_task; pub mod storage_task; +pub mod server; mod sniffer_task; /// An implementation of the [Fetch spec](http://fetch.spec.whatwg.org/) diff --git a/components/net/server.rs b/components/net/server.rs new file mode 100644 index 000000000000..2976c921de39 --- /dev/null +++ b/components/net/server.rs @@ -0,0 +1,271 @@ +/* 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/. */ + +//! Encapsulates the notion of a "server". +//! +//! Servers maintain connections to content threads (in single-process mode) or processes (in +//! multiprocess mode) and serve them resources. Examples of servers are the image cache thread, +//! the resource thread, and the font cache thread. + +use libc::c_int; +use serialize::{Decodable, Encodable}; +use servo_util::ipc::{mod, IpcReceiver, IpcSender}; +use servo_util::platform::unix::ipc as unix_ipc; +use servo_util::platform::unix::ipc::{POLLRDBAND, POLLRDNORM, ServoUnixSocket, pollfd}; +use servo_util::sbsf::{ServoDecoder, ServoEncoder}; +use servo_util::task_state; +use std::collections::HashMap; +use std::io::IoError; +use std::sync::{Arc, Mutex}; +use std::task::TaskBuilder; + +/// A server which maintains connections to content threads. +/// +/// `M` is the type of a message from the client to the server. `R` is the type of a response from +/// the server to the client (in reply to a synchronous message). +pub struct Server { + /// The name of this server, for debugging. + name: &'static str, + /// A list of clients to be serviced. + clients: HashMap>, +} + +/// The type of a client ID. On Unix, this is just the receiving-end file descriptor. +pub type ClientId = c_int; + +/// Information that the server keeps about each client. +struct ClientInfo { + /// The channel on which messages can be sent to the client. + sender: IpcSender>, + /// The channel on which messages are received from the client. + receiver: IpcReceiver>, +} + +/// Messages sent to the clients. +#[deriving(Decodable, Encodable)] +enum ClientMsg { + /// A response to a request. + Response(R), +} + +impl Server where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + /// Creates a new server. + pub fn new(name: &'static str) -> Server { + Server { + name: name, + clients: HashMap::new(), + } + } + + /// Creates a new client and returns the proxy it uses to communicate with the server. + pub fn create_new_client(&mut self) -> ServerProxy { + let (client_msg_receiver, client_msg_sender) = ipc::channel(); + let (server_msg_receiver, server_msg_sender) = ipc::channel(); + let client_id = server_msg_receiver.fd(); + self.clients.insert(client_id, ClientInfo { + sender: client_msg_sender, + receiver: server_msg_receiver, + }); + ServerProxy { + sender: server_msg_sender, + receiver: client_msg_receiver, + } + } + + /// Returns the next message or messages. If `None` is returned, then all clients have exited. + /// + /// TODO(pcwalton): Refactor this to not be so Unix-specific. We will need real async I/O + /// support in Rust or a library to do this. + pub fn recv(&mut self) -> Option> { + let mut result = Vec::new(); + while result.is_empty() { + if self.clients.len() == 0 { + return None + } + + let mut pollfds = Vec::new(); + for (_, client) in self.clients.iter() { + pollfds.push(pollfd { + fd: client.receiver.fd(), + events: POLLRDNORM | POLLRDBAND, + revents: 0, + }); + } + + unix_ipc::poll_fds(pollfds.as_mut_slice(), None).unwrap(); + + for pollfd in pollfds.iter() { + if pollfd.revents == 0 { + continue + } + let client_id = pollfd.fd; + match self.clients[client_id].receiver.recv() { + ServerMsg::Msg(msg) => result.push((client_id, msg)), + ServerMsg::CreateNewClient => { + // Create a new pair of sockets and send it to the client. + let (mut their_socket, my_sending_socket) = + ServoUnixSocket::pair().unwrap(); + let my_receiving_socket = my_sending_socket.dup(); + let sender = IpcSender::from_socket(my_sending_socket); + let receiver = IpcReceiver::from_socket(my_receiving_socket); + let new_client_id = receiver.fd(); + self.clients.insert(new_client_id, ClientInfo { + sender: sender, + receiver: receiver, + }); + + let fds = [their_socket.fd()]; + self.clients[client_id].sender.socket().send_fds(&fds).unwrap(); + their_socket.forget(); + } + ServerMsg::Exit => { + self.clients.remove(&client_id); + } + } + } + } + Some(result) + } + + /// Sends a response to a client. + pub fn send(&self, client_id: ClientId, response: R) { + self.clients[client_id].sender.send(ClientMsg::Response(response)) + } +} + +/// Messages sent to the server. `M` is the type of the messages specific to this server. +#[deriving(Decodable, Encodable)] +pub enum ServerMsg { + /// A server-specific asynchronous or synchronous message. + Msg(M), + /// Requests that a new client ID be created. + CreateNewClient, + /// A notification that the client has exited. + Exit, +} + +/// A proxy from each client to the server. +/// +/// `M` is the type of a message from the server to the client. `N` is the type of a notification +/// message from the client to the server. `R` is the type of a request from the client to the +/// server. +pub struct ServerProxy where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + /// A channel on which messages can be sent to the server. + sender: IpcSender>, + /// A channel on which messages can be received from the server. + receiver: IpcReceiver>, +} + +impl ServerProxy where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + /// Creates a server proxy from a pair of file descriptors. + #[inline] + pub fn from_fds(sender_fd: c_int, receiver_fd: c_int) -> ServerProxy { + ServerProxy { + sender: IpcSender::from_fd(sender_fd), + receiver: IpcReceiver::from_fd(receiver_fd), + } + } + + /// Returns the raw sending and receiving file descriptors, respectively. + #[inline] + pub fn fds(&self) -> (c_int, c_int) { + (self.sender.fd(), self.receiver.fd()) + } + + /// Leaks the file descriptors! Obviously, you must be careful when using this function. + /// + /// The only time this is used at the moment is when serializing the file descriptors over IPC. + pub fn forget(&mut self) { + self.sender.socket().forget(); + self.receiver.socket().forget(); + } + + /// Sends an asynchronous message to the server, without waiting for a response. + pub fn send_async(&self, msg: M) { + self.sender.send(ServerMsg::Msg(msg)) + } + + /// Sends a request to the server, blocks to wait for a response, and returns it. + pub fn send_sync(&self, msg: M) -> R { + self.sender.send(ServerMsg::Msg(msg)); + let ClientMsg::Response(response) = self.receiver.recv(); + return response + } + + /// Creates a new client, effectively cloning this server proxy. + pub fn create_new_client(&self) -> ServerProxy { + self.sender.send(ServerMsg::CreateNewClient); + + // Receive our end of the new Unix socket. + let mut fds = [0]; + assert!(self.receiver.socket().recv_fds(&mut fds) == Ok(1)); + let new_receiver = ServoUnixSocket::from_fd(fds[0]); + let new_sender = new_receiver.dup(); + ServerProxy { + sender: IpcSender::from_socket(new_sender), + receiver: IpcReceiver::from_socket(new_receiver), + } + } +} + +/// A convenience typedef for the common case of multiple threads in a process sharing a server +/// proxy. +pub type SharedServerProxy = Arc>>; + +#[unsafe_destructor] +impl Drop for ServerProxy where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + fn drop(&mut self) { + drop(self.sender.send_opt(ServerMsg::Exit)); + } +} + +/// Spawns a task with an arrangement to send a particular message to a server if the task fails. +pub fn spawn_named_with_send_to_server_on_failure(name: &'static str, + state: task_state::TaskState, + body: proc(): Send, + failure_msg: M, + failure_dest: SharedServerProxy) + where M: Send + + 'static + + for<'a> + Decodable, + IoError> + + for<'a> + Encodable, + IoError>, + R: for<'a> + Decodable, + IoError> + + for<'a> + Encodable, + IoError> { + let future_result = TaskBuilder::new().named(name).try_future(proc() { + task_state::initialize(state); + // FIXME: Find replacement for this post-runtime removal + // rtinstrument::instrument(f); + body(); + }); + + let watched_name = name.into_string(); + let watcher_name = format!("{}Watcher", watched_name); + TaskBuilder::new().named(watcher_name).spawn(proc() { + if future_result.into_inner().is_err() { + debug!("{} failed, notifying constellation", name); + failure_dest.lock().send_async(failure_msg); + } + }); +} + diff --git a/components/net/storage_task.rs b/components/net/storage_task.rs index aafe08859924..257844cab1e7 100644 --- a/components/net/storage_task.rs +++ b/components/net/storage_task.rs @@ -3,41 +3,75 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; -use std::comm::{channel, Receiver, Sender}; -use std::collections::HashMap; -use std::collections::TreeMap; +use server::{ClientId, Server, SharedServerProxy}; +use std::collections::{HashMap, TreeMap}; +use std::sync::{Arc, Mutex}; use url::Url; use servo_util::str::DOMString; use servo_util::task::spawn_named; /// Request operations on the storage data associated with a particular url +#[deriving(Decodable, Encodable)] pub enum StorageTaskMsg { /// gets the number of key/value pairs present in the associated storage data - Length(Sender, Url), + Length(Url), /// gets the name of the key at the specified index in the associated storage data - Key(Sender>, Url, u32), + Key(Url, u32), /// gets the value associated with the given key in the associated storage data - GetItem(Sender>, Url, DOMString), + GetItem(Url, DOMString), /// sets the value of the given key in the associated storage data /// TODO throw QuotaExceededError in case of error - SetItem(Sender, Url, DOMString, DOMString), + SetItem(Url, DOMString, DOMString), /// removes the key/value pair for the given key in the associated storage data - RemoveItem(Sender, Url, DOMString), + RemoveItem(Url, DOMString), /// clears the associated storage data by removing all the key/value pairs - Clear(Sender, Url), + Clear(Url), +} - /// shut down this task - Exit +/// Response operations on the storage data associated with a particular URL. +#[deriving(Decodable, Encodable)] +pub enum StorageTaskResponse { + /// The number of key/value pairs present in the associated storage data. + Length(u32), + /// The name of the key at the specified index in the associated storage data. + Key(Option), + /// The value associated with the given key in the associated storage data. + GetItem(Option), + /// A simple acknowledgement of success/failure, used for `SetItem`, `RemoveItem`, and `Clear`. + Complete(bool), } /// Handle to a storage task -pub type StorageTask = Sender; +#[deriving(Clone)] +pub struct StorageTask { + pub client: SharedServerProxy, +} + +impl StorageTask { + #[inline] + pub fn from_client(client: SharedServerProxy) + -> StorageTask { + StorageTask { + client: client, + } + } + + pub fn send(&self, msg: StorageTaskMsg) -> StorageTaskResponse { + self.client.lock().send_sync(msg) + } + + pub fn create_new_client(&self) -> StorageTask { + StorageTask { + client: Arc::new(Mutex::new(self.client.lock().create_new_client())), + } + } +} pub trait StorageTaskFactory { fn new() -> StorageTask; @@ -46,23 +80,26 @@ pub trait StorageTaskFactory { impl StorageTaskFactory for StorageTask { /// Create a StorageTask fn new() -> StorageTask { - let (chan, port) = channel(); + let mut server = Server::new("StorageTask"); + let client = Arc::new(Mutex::new(server.create_new_client())); spawn_named("StorageManager".to_owned(), proc() { - StorageManager::new(port).start(); + StorageManager::new(server).start(); }); - chan + StorageTask { + client: client, + } } } struct StorageManager { - port: Receiver, + server: Server, data: HashMap>, } impl StorageManager { - fn new(port: Receiver) -> StorageManager { + fn new(server: Server) -> StorageManager { StorageManager { - port: port, + server: server, data: HashMap::new(), } } @@ -70,46 +107,40 @@ impl StorageManager { impl StorageManager { fn start(&mut self) { - loop { - match self.port.recv() { - StorageTaskMsg::Length(sender, url) => { - self.length(sender, url) - } - StorageTaskMsg::Key(sender, url, index) => { - self.key(sender, url, index) - } - StorageTaskMsg::SetItem(sender, url, name, value) => { - self.set_item(sender, url, name, value) - } - StorageTaskMsg::GetItem(sender, url, name) => { - self.get_item(sender, url, name) - } - StorageTaskMsg::RemoveItem(sender, url, name) => { - self.remove_item(sender, url, name) - } - StorageTaskMsg::Clear(sender, url) => { - self.clear(sender, url) - } - StorageTaskMsg::Exit => { - break + while let Some(msgs) = self.server.recv() { + for (client_id, msg) in msgs.into_iter() { + match msg { + StorageTaskMsg::Length(url) => self.length(client_id, url), + StorageTaskMsg::Key(url, index) => self.key(client_id, url, index), + StorageTaskMsg::SetItem(url, name, value) => { + self.set_item(client_id, url, name, value) + } + StorageTaskMsg::GetItem(url, name) => self.get_item(client_id, url, name), + StorageTaskMsg::RemoveItem(url, name) => { + self.remove_item(client_id, url, name) + } + StorageTaskMsg::Clear(url) => self.clear(client_id, url), } } } } - fn length(&self, sender: Sender, url: Url) { + fn length(&self, sender: ClientId, url: Url) { let origin = self.get_origin_as_string(url); - sender.send(self.data.get(&origin).map_or(0u, |entry| entry.len()) as u32); + self.server.send(sender, StorageTaskResponse::Length( + self.data.get(&origin).map_or(0u, |entry| entry.len()) as u32)); } - fn key(&self, sender: Sender>, url: Url, index: u32) { + fn key(&self, sender: ClientId, url: Url, index: u32) { let origin = self.get_origin_as_string(url); - sender.send(self.data.get(&origin) - .and_then(|entry| entry.keys().nth(index as uint)) - .map(|key| key.clone())); + self.server.send(sender, + StorageTaskResponse::Key( + self.data.get(&origin) + .and_then(|entry| entry.keys().nth(index as uint)) + .map(|key| key.clone()))); } - fn set_item(&mut self, sender: Sender, url: Url, name: DOMString, value: DOMString) { + fn set_item(&mut self, sender: ClientId, url: Url, name: DOMString, value: DOMString) { let origin = self.get_origin_as_string(url); if !self.data.contains_key(&origin) { self.data.insert(origin.clone(), TreeMap::new()); @@ -124,32 +155,37 @@ impl StorageManager { } }).unwrap(); - sender.send(updated); + self.server.send(sender, StorageTaskResponse::Complete(updated)); } - fn get_item(&self, sender: Sender>, url: Url, name: DOMString) { + fn get_item(&self, sender: ClientId, url: Url, name: DOMString) { let origin = self.get_origin_as_string(url); - sender.send(self.data.get(&origin) - .and_then(|entry| entry.get(&name)) - .map(|value| value.to_string())); + self.server.send(sender, + StorageTaskResponse::GetItem( + self.data.get(&origin) + .and_then(|entry| entry.get(&name)) + .map(|value| value.to_string()))); } - fn remove_item(&mut self, sender: Sender, url: Url, name: DOMString) { + fn remove_item(&mut self, sender: ClientId, url: Url, name: DOMString) { let origin = self.get_origin_as_string(url); - sender.send(self.data.get_mut(&origin) - .map_or(false, |entry| entry.remove(&name).is_some())); + self.server.send(sender, + StorageTaskResponse::Complete( + self.data.get_mut(&origin) + .map_or(false, |entry| entry.remove(&name).is_some()))); } - fn clear(&mut self, sender: Sender, url: Url) { + fn clear(&mut self, sender: ClientId, url: Url) { let origin = self.get_origin_as_string(url); - sender.send(self.data.get_mut(&origin) - .map_or(false, |entry| { - if !entry.is_empty() { - entry.clear(); - true - } else { - false - }})); + self.server.send(sender, + StorageTaskResponse::Complete(self.data.get_mut(&origin) + .map_or(false, |entry| { + if !entry.is_empty() { + entry.clear(); + true + } else { + false + }}))); } fn get_origin_as_string(&self, url: Url) -> String { diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index ff730ea1e12a..4d07db070b9c 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -46,16 +46,21 @@ use layout_interface::{LayoutRPC, LayoutChan}; use libc; use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData}; use net::image_cache_task::ImageCacheTask; +use net::storage_task::StorageTask; use script_traits::ScriptControlChan; use script_traits::UntrustedNodeAddress; -use servo_msg::compositor_msg::ScriptListener; +use serialize::{Decodable, Encodable}; use servo_msg::constellation_msg::ConstellationChan; +use servo_net::server::{ServerProxy, SharedServerProxy}; +use servo_util::ipc::{IpcReceiver, IpcSender}; +use servo_util::sbsf::{ServoDecoder, ServoEncoder}; use servo_util::smallvec::{SmallVec1, SmallVec}; use servo_util::str::{LengthOrPercentageOrAuto}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::comm::{Receiver, Sender}; use std::io::timer::Timer; +use std::io::IoError; use std::rc::Rc; use string_cache::{Atom, Namespace}; use style::PropertyDeclarationBlock; @@ -219,6 +224,7 @@ no_jsmanaged_fields!(WindowProxyHandler) no_jsmanaged_fields!(UntrustedNodeAddress) no_jsmanaged_fields!(LengthOrPercentageOrAuto) no_jsmanaged_fields!(RGBA) +no_jsmanaged_fields!(StorageTask) impl JSTraceable for Box { #[inline] @@ -241,7 +247,35 @@ impl JSTraceable for fn(A) -> B { } } -impl JSTraceable for Box { +impl JSTraceable for IpcReceiver { + #[inline] + fn trace(&self, _: *mut JSTracer) { + // Do nothing + } +} + +impl JSTraceable for IpcSender { + #[inline] + fn trace(&self, _: *mut JSTracer) { + // Do nothing + } +} + +impl JSTraceable for ServerProxy where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + #[inline] + fn trace(&self, _: *mut JSTracer) { + // Do nothing + } +} + +impl JSTraceable for SharedServerProxy + where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { #[inline] fn trace(&self, _: *mut JSTracer) { // Do nothing diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 66d0f824fa27..efd8cfe05254 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -22,7 +22,7 @@ use dom::virtualmethods::VirtualMethods; use dom::window::Window; use page::{IterablePage, Page}; -use servo_msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan}; +use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_util::str::DOMString; @@ -108,12 +108,12 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { self.containing_page_pipeline_id.set(Some(page.id)); - let ConstellationChan(ref chan) = page.constellation_chan; - chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, - page.id, - new_subpage_id, - old_subpage_id, - sandboxed)); + let chan = &mut *page.constellation_chan.borrow_mut(); + chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, + page.id, + new_subpage_id, + old_subpage_id, + sandboxed)); } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 5433e666180f..a5fee2a4007f 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1635,7 +1635,7 @@ impl Node { None => {} Some(chan) => { let LayoutChan(chan) = chan; - chan.send(Msg::ReapLayoutData(layout_data)) + chan.send_opt(Msg::ReapLayoutData(layout_data)); }, } } diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index cbbabb912a71..585694f0e5f1 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -9,9 +9,7 @@ use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::bindings::error::Fallible; use servo_util::str::DOMString; -use servo_net::storage_task::StorageTask; -use servo_net::storage_task::StorageTaskMsg; -use std::comm::channel; +use servo_net::storage_task::{StorageTask, StorageTaskMsg, StorageTaskResponse}; use url::Url; #[dom_struct] @@ -52,24 +50,30 @@ impl Storage { impl<'a> StorageMethods for JSRef<'a, Storage> { fn Length(self) -> u32 { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::Length(sender, self.get_url())); - receiver.recv() + if let StorageTaskResponse::Length(length) = + self.get_storage_task().send(StorageTaskMsg::Length(self.get_url())) { + length + } else { + panic!("Storage::Length(): got unexpected reply") + } } fn Key(self, index: u32) -> Option { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::Key(sender, self.get_url(), index)); - receiver.recv() + if let StorageTaskResponse::Key(key) = + self.get_storage_task().send(StorageTaskMsg::Key(self.get_url(), index)) { + key + } else { + panic!("Storage::Key(): got unexpected reply") + } } fn GetItem(self, name: DOMString) -> Option { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::GetItem(sender, self.get_url(), name)); - receiver.recv() + if let StorageTaskResponse::GetItem(item) = + self.get_storage_task().send(StorageTaskMsg::GetItem(self.get_url(), name)) { + item + } else { + panic!("Storage::GetItem(): got unexpected reply") + } } fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option { @@ -79,12 +83,8 @@ impl<'a> StorageMethods for JSRef<'a, Storage> { } fn SetItem(self, name: DOMString, value: DOMString) { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::SetItem(sender, self.get_url(), name, value)); - if receiver.recv() { - //TODO send notification - } + self.get_storage_task().send(StorageTaskMsg::SetItem(self.get_url(), name, value)); + //TODO send notification } fn NamedSetter(self, name: DOMString, value: DOMString) { @@ -96,12 +96,8 @@ impl<'a> StorageMethods for JSRef<'a, Storage> { } fn RemoveItem(self, name: DOMString) { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::RemoveItem(sender, self.get_url(), name)); - if receiver.recv() { - //TODO send notification - } + self.get_storage_task().send(StorageTaskMsg::RemoveItem(self.get_url(), name)); + //TODO send notification } fn NamedDeleter(self, name: DOMString) { @@ -109,12 +105,8 @@ impl<'a> StorageMethods for JSRef<'a, Storage> { } fn Clear(self) { - let (sender, receiver) = channel(); - - self.get_storage_task().send(StorageTaskMsg::Clear(sender, self.get_url())); - if receiver.recv() { - //TODO send notification - } + self.get_storage_task().send(StorageTaskMsg::Clear(self.get_url())); + //TODO send notification } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 91402fe6c1fd..828bc587b277 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -27,12 +27,12 @@ use layout_interface::{ReflowGoal, ReflowQueryType}; use page::Page; use script_task::{TimerSource, ScriptChan}; use script_task::ScriptMsg; -use script_traits::ScriptControlChan; use timers::{IsInterval, TimerId, TimerManager, TimerCallback}; -use servo_msg::compositor_msg::ScriptListener; +use servo_msg::compositor_msg::ScriptToCompositorMsg; use servo_msg::constellation_msg::LoadData; use servo_net::image_cache_task::ImageCacheTask; +use servo_net::server::SharedServerProxy; use servo_net::storage_task::StorageTask; use servo_util::str::{DOMString,HTML_SPACE_CHARACTERS}; @@ -54,12 +54,11 @@ use time; pub struct Window { eventtarget: EventTarget, script_chan: Box, - control_chan: ScriptControlChan, console: MutNullableJS, location: MutNullableJS, navigator: MutNullableJS, image_cache_task: ImageCacheTask, - compositor: DOMRefCell>, + compositor: DOMRefCell>, browser_context: DOMRefCell>, page: Rc, performance: MutNullableJS, @@ -80,15 +79,11 @@ impl Window { self.script_chan.clone() } - pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { - &self.control_chan - } - pub fn image_cache_task<'a>(&'a self) -> &'a ImageCacheTask { &self.image_cache_task } - pub fn compositor(&self) -> RefMut> { + pub fn compositor(&self) -> RefMut> { self.compositor.borrow_mut() } @@ -389,14 +384,12 @@ impl Window { pub fn new(cx: *mut JSContext, page: Rc, script_chan: Box, - control_chan: ScriptControlChan, - compositor: Box, + compositor: SharedServerProxy, image_cache_task: ImageCacheTask) -> Temporary { let win = box Window { eventtarget: EventTarget::new_inherited(EventTargetTypeId::Window), script_chan: script_chan, - control_chan: control_chan, console: Default::default(), compositor: DOMRefCell::new(compositor), page: page, diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 2856930b0730..72960d6e755f 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -10,7 +10,7 @@ use dom::node::LayoutDataRef; use geom::point::Point2D; use geom::rect::Rect; -use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress}; +use script_traits::{OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use servo_msg::constellation_msg::{PipelineExitType, WindowSizeData}; use servo_util::geometry::Au; use std::any::{Any, AnyRefExt}; @@ -102,8 +102,6 @@ pub struct Reflow { pub url: Url, /// Is the current reflow of an iframe, as opposed to a root window? pub iframe: bool, - /// The channel through which messages can be sent back to the script task. - pub script_chan: ScriptControlChan, /// The current window size. pub window_size: WindowSizeData, /// The channel that we send a notification to. diff --git a/components/script/page.rs b/components/script/page.rs index 979f49f6681c..5edb5d16f75a 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -17,17 +17,16 @@ use layout_interface::{ ReflowGoal, ReflowQueryType, TrustedNodeAddress }; -use script_traits::{UntrustedNodeAddress, ScriptControlChan}; +use script_traits::{UntrustedNodeAddress}; use geom::{Point2D, Rect, Size2D}; use js::rust::Cx; -use servo_msg::compositor_msg::ScriptListener; +use servo_msg::compositor_msg::ScriptToCompositorMsg; use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_net::resource_task::ResourceTask; use servo_net::storage_task::StorageTask; -use servo_util::geometry::{Au, MAX_RECT}; -use servo_util::geometry; +use servo_util::geometry::{mod, Au, MAX_RECT}; use servo_util::str::DOMString; use servo_util::smallvec::SmallVec; use std::cell::{Cell, Ref, RefMut}; @@ -87,7 +86,7 @@ pub struct Page { pub storage_task: StorageTask, /// A handle for communicating messages to the constellation task. - pub constellation_chan: ConstellationChan, + pub constellation_chan: DOMRefCell, // Child Pages. pub children: DOMRefCell>>, @@ -157,16 +156,14 @@ impl Page { last_reflow_id: Cell::new(0), resource_task: resource_task, storage_task: storage_task, - constellation_chan: constellation_chan, + constellation_chan: DOMRefCell::new(constellation_chan), children: DOMRefCell::new(vec!()), page_clip_rect: Cell::new(MAX_RECT), } } pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.r().control_chan().clone(), &mut **window.r().compositor(), query); + self.reflow(goal, query); } pub fn layout(&self) -> &LayoutRPC { @@ -238,7 +235,11 @@ impl Page { Some(ref frame) => { let window = frame.window.root(); let document = frame.document.root(); - window.r().compositor().set_title(self.id, Some(document.r().Title())); + window.r() + .compositor() + .lock() + .send_async(ScriptToCompositorMsg::SetTitle(self.id, + Some(document.r().Title()))); } } } @@ -336,11 +337,7 @@ impl Page { /// yet, the page is presumed invisible and no reflow is performed. /// /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. - pub fn reflow(&self, - goal: ReflowGoal, - script_chan: ScriptControlChan, - _: &mut ScriptListener, - query_type: ReflowQueryType) { + pub fn reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType) { let root = match *self.frame() { None => return, Some(ref frame) => { @@ -383,7 +380,6 @@ impl Page { iframe: self.subpage_id.is_some(), goal: goal, window_size: window_size, - script_chan: script_chan, script_join_chan: join_chan, id: last_reflow_id.get(), query_type: query_type, diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 6726c0e832e8..5aae0e4b43a2 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -39,18 +39,17 @@ use page::{Page, IterablePage, Frame}; use timers::TimerId; use devtools; -use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, GetRootNode, DevtoolsPageInfo}; -use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, GetDocumentElement}; +use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, GetRootNode}; +use devtools_traits::{DevtoolsPageInfo, DevtoolScriptControlMsg, EvaluateJS, GetDocumentElement}; use devtools_traits::{GetChildren, GetLayout, ModifyAttribute}; use script_traits::CompositorEvent; use script_traits::CompositorEvent::{ResizeEvent, ReflowEvent, ClickEvent}; use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent}; use script_traits::CompositorEvent::{MouseMoveEvent, KeyEvent}; use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel}; -use script_traits::{ConstellationControlMsg, ScriptControlChan}; -use script_traits::ScriptTaskFactory; +use script_traits::{ConstellationControlMsg, ScriptTaskFactory}; use servo_msg::compositor_msg::ReadyState::{FinishedLoading, Loading, PerformingLayout}; -use servo_msg::compositor_msg::{LayerId, ScriptListener}; +use servo_msg::compositor_msg::{LayerId, ScriptToCompositorMsg}; use servo_msg::constellation_msg::{ConstellationChan}; use servo_msg::constellation_msg::{LoadData, NavigationDirection, PipelineId}; use servo_msg::constellation_msg::{Failure, Msg, WindowSizeData, Key, KeyState}; @@ -60,10 +59,11 @@ use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::{ResourceTask, ControlMsg}; use servo_net::resource_task::LoadData as NetLoadData; +use servo_net::server::{mod, SharedServerProxy}; use servo_net::storage_task::StorageTask; +use servo_util::ipc::IpcReceiver; use servo_util::geometry::to_frac_px; use servo_util::smallvec::SmallVec; -use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task_state; use geom::point::Point2D; @@ -82,8 +82,9 @@ use std::borrow::ToOwned; use std::cell::Cell; use std::comm::{channel, Sender, Receiver, Select}; use std::fmt::{mod, Show}; -use std::mem::replace; +use std::mem::{mod, replace}; use std::rc::Rc; +use std::task as std_task; use std::u32; use time::{Tm, strptime}; @@ -194,17 +195,17 @@ pub struct ScriptTask { /// events in the event queue. chan: NonWorkerScriptChan, - /// A channel to hand out to tasks that need to respond to a message from the script task. - control_chan: ScriptControlChan, + /// The port on which layout can communicate with the script task. + layout_to_script_port: Receiver, - /// The port on which the constellation and layout tasks can communicate with the - /// script task. - control_port: Receiver, + /// The port on which the constellation can communicate with the script task. + constellation_to_script_port: Receiver, /// For communicating load url messages to the constellation - constellation_chan: ConstellationChan, + constellation_chan: DOMRefCell, + /// A handle to the compositor for communicating ready state messages. - compositor: DOMRefCell>, + compositor: DOMRefCell>, /// For providing instructions to an optional devtools server. devtools_chan: Option, @@ -285,31 +286,32 @@ impl ScriptTaskFactory for ScriptTask { box pair.sender() as Box } - fn create(_phantom: Option<&mut ScriptTask>, - id: PipelineId, - compositor: C, - layout_chan: &OpaqueScriptLayoutChannel, - control_chan: ScriptControlChan, - control_port: Receiver, - constellation_chan: ConstellationChan, - failure_msg: Failure, - resource_task: ResourceTask, - storage_task: StorageTask, - image_cache_task: ImageCacheTask, - devtools_chan: Option, - window_size: WindowSizeData) - where C: ScriptListener + Send + 'static { + fn create(_phantom: Option<&mut ScriptTask>, + id: PipelineId, + compositor: SharedServerProxy, + layout_chan: &OpaqueScriptLayoutChannel, + constellation_to_script_port: IpcReceiver, + constellation_chan: ConstellationChan, + failure_msg: Failure, + layout_to_script_port: Receiver, + resource_task: ResourceTask, + storage_task: StorageTask, + image_cache_task: ImageCacheTask, + devtools_chan: Option, + window_size: WindowSizeData) { let ConstellationChan(const_chan) = constellation_chan.clone(); let (script_chan, script_port) = channel(); let layout_chan = LayoutChan(layout_chan.sender()); - spawn_named_with_send_on_failure("ScriptTask", task_state::SCRIPT, proc() { + server::spawn_named_with_send_to_server_on_failure("ScriptTask", + task_state::SCRIPT, + proc() { let script_task = ScriptTask::new(id, - box compositor as Box, + compositor, layout_chan, script_port, NonWorkerScriptChan(script_chan), - control_chan, - control_port, + layout_to_script_port, + constellation_to_script_port, constellation_chan, resource_task, storage_task, @@ -336,12 +338,12 @@ unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus) impl ScriptTask { /// Creates a new script task. pub fn new(id: PipelineId, - compositor: Box, + compositor: SharedServerProxy, layout_chan: LayoutChan, port: Receiver, chan: NonWorkerScriptChan, - control_chan: ScriptControlChan, - control_port: Receiver, + layout_to_script_port: Receiver, + constellation_to_script_port: IpcReceiver, constellation_chan: ConstellationChan, resource_task: ResourceTask, storage_task: StorageTask, @@ -371,6 +373,14 @@ impl ScriptTask { constellation_chan.clone(), js_context.clone()); + // Create a proxy to proxy messages received from the constellation over IPC to us. + let (proxy_constellation_to_script_chan, proxy_constellation_to_script_port) = channel(); + std_task::spawn(proc() { + while let Ok(msg) = constellation_to_script_port.recv_opt() { + proxy_constellation_to_script_chan.send(msg) + } + }); + let (devtools_sender, devtools_receiver) = channel(); ScriptTask { page: DOMRefCell::new(Rc::new(page)), @@ -380,9 +390,9 @@ impl ScriptTask { port: port, chan: chan, - control_chan: control_chan, - control_port: control_port, - constellation_chan: constellation_chan, + layout_to_script_port: layout_to_script_port, + constellation_to_script_port: proxy_constellation_to_script_port, + constellation_chan: DOMRefCell::new(constellation_chan), compositor: DOMRefCell::new(compositor), devtools_chan: devtools_chan, devtools_port: devtools_receiver, @@ -486,21 +496,25 @@ impl ScriptTask { let mut event = { let sel = Select::new(); let mut port1 = sel.handle(&self.port); - let mut port2 = sel.handle(&self.control_port); - let mut port3 = sel.handle(&self.devtools_port); + let mut port2 = sel.handle(&self.constellation_to_script_port); + let mut port3 = sel.handle(&self.layout_to_script_port); + let mut port4 = sel.handle(&self.devtools_port); unsafe { port1.add(); port2.add(); + port3.add(); if self.devtools_chan.is_some() { - port3.add(); + port4.add(); } } let ret = sel.wait(); if ret == port1.id() { MixedMessage::FromScript(self.port.recv()) } else if ret == port2.id() { - MixedMessage::FromConstellation(self.control_port.recv()) + MixedMessage::FromConstellation(self.constellation_to_script_port.recv()) } else if ret == port3.id() { + MixedMessage::FromConstellation(self.layout_to_script_port.recv()) + } else if ret == port4.id() { MixedMessage::FromDevtools(self.devtools_port.recv()) } else { panic!("unexpected select result") @@ -537,10 +551,13 @@ impl ScriptTask { // If any of our input sources has an event pending, we'll perform another iteration // and check for more resize events. If there are no events pending, we'll move // on and execute the sequential non-resize events we've seen. - match self.control_port.try_recv() { + match self.constellation_to_script_port.try_recv() { Err(_) => match self.port.try_recv() { Err(_) => match self.devtools_port.try_recv() { - Err(_) => break, + Err(_) => match self.layout_to_script_port.try_recv() { + Err(_) => break, + Ok(ev) => event = MixedMessage::FromConstellation(ev), + }, Ok(ev) => event = MixedMessage::FromDevtools(ev), }, Ok(ev) => event = MixedMessage::FromScript(ev), @@ -643,12 +660,17 @@ impl ScriptTask { task's page tree. This is a bug."); let new_page = { let window_size = parent_page.window_size.get(); + let layout_chan: Box = unsafe { + mem::transmute(layout_chan) + }; Page::new(new_pipeline_id, Some(subpage_id), - LayoutChan(layout_chan.downcast_ref::>().unwrap().clone()), + LayoutChan(layout_chan.downcast_ref::>() + .unwrap() + .clone()), window_size, parent_page.resource_task.clone(), parent_page.storage_task.clone(), - self.constellation_chan.clone(), + (*self.constellation_chan.borrow()).clone(), self.js_context.borrow().as_ref().unwrap().clone()) }; parent_page.children.borrow_mut().push(Rc::new(new_page)); @@ -677,13 +699,16 @@ impl ScriptTask { *layout_join_port = None; } - self.compositor.borrow_mut().set_ready_state(pipeline_id, FinishedLoading); + self.compositor + .borrow_mut() + .lock() + .send_async(ScriptToCompositorMsg::SetReadyState(pipeline_id, FinishedLoading)); } /// Handles a navigate forward or backward message. /// TODO(tkuehn): is it ever possible to navigate only on a subframe? fn handle_navigate_msg(&self, direction: NavigationDirection) { - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut *self.constellation_chan.borrow_mut(); chan.send(ConstellationMsg::Navigate(direction)); } @@ -706,11 +731,6 @@ impl ScriptTask { /// where layout is still accessing them. fn handle_exit_window_msg(&self, _: PipelineId) { debug!("script task handling exit window msg"); - - // TODO(tkuehn): currently there is only one window, - // so this can afford to be naive and just shut down the - // compositor. In the future it'll need to be smarter. - self.compositor.borrow_mut().close(); } /// Handles a request for the window title. @@ -782,8 +802,7 @@ impl ScriptTask { let window = Window::new(cx.ptr, page.clone(), self.chan.clone(), - self.control_chan.clone(), - self.compositor.borrow_mut().dup(), + self.compositor.borrow_mut().clone(), self.image_cache_task.clone()).root(); let doc_url = if is_javascript { let doc_url = last_url.unwrap_or_else(|| { @@ -800,7 +819,10 @@ impl ScriptTask { window.r().init_browser_context(document.r()); - self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading); + self.compositor + .borrow_mut() + .lock() + .send_async(ScriptToCompositorMsg::SetReadyState(pipeline_id, Loading)); { // Create the root frame. @@ -852,7 +874,10 @@ impl ScriptTask { parse_html(document.r(), parser_input, &final_url); document.r().set_ready_state(DocumentReadyState::Interactive); - self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout); + self.compositor + .borrow_mut() + .lock() + .send_async(ScriptToCompositorMsg::SetReadyState(pipeline_id, PerformingLayout)); // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {}", final_url); @@ -887,7 +912,7 @@ impl ScriptTask { *page.fragment_name.borrow_mut() = final_url.fragment.clone(); - let ConstellationChan(ref chan) = self.constellation_chan; + let chan = &mut *self.constellation_chan.borrow_mut(); chan.send(ConstellationMsg::LoadComplete); // Notify devtools that a new script global exists. @@ -913,16 +938,18 @@ impl ScriptTask { // Really what needs to happen is that this needs to go through layout to ask which // layer the element belongs to, and have it send the scroll message to the // compositor. - self.compositor.borrow_mut().scroll_fragment_point(pipeline_id, LayerId::null(), point); + self.compositor + .borrow_mut() + .lock() + .send_async(ScriptToCompositorMsg::ScrollFragmentPoint(pipeline_id, + LayerId::null(), + point)); } /// Reflows non-incrementally. fn force_reflow(&self, page: &Page) { page.dirty_all_nodes(); - page.reflow(ReflowGoal::ForDisplay, - self.control_chan.clone(), - &mut **self.compositor.borrow_mut(), - ReflowQueryType::NoQuery); + page.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -975,7 +1002,8 @@ impl ScriptTask { } /// The entry point for all key processing for web content - fn dispatch_key_event(&self, key: Key, + fn dispatch_key_event(&self, + key: Key, state: KeyState, modifiers: KeyModifiers, pipeline_id: PipelineId) { @@ -1033,7 +1061,10 @@ impl ScriptTask { } if !prevented { - self.compositor.borrow_mut().send_key_event(key, state, modifiers); + self.compositor + .borrow_mut() + .lock() + .send_async(ScriptToCompositorMsg::SendKeyEvent(key, state, modifiers)); } // This behavior is unspecced @@ -1059,8 +1090,8 @@ impl ScriptTask { /// The entry point for content to notify that a new load has been requested /// for the given pipeline. fn trigger_load(&self, pipeline_id: PipelineId, load_data: LoadData) { - let ConstellationChan(ref const_chan) = self.constellation_chan; - const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)); + let chan = &mut *self.constellation_chan.borrow_mut(); + chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)); } /// The entry point for content to notify that a fragment url has been requested diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index be2de70719a6..2ab8d210dd55 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -21,36 +21,63 @@ extern crate serialize; // that these modules won't have to depend on script. use devtools_traits::DevtoolsControlChan; -use libc::c_void; +use libc::{c_int, c_void}; +use servo_msg::compositor_msg::ScriptToCompositorMsg; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers}; use servo_msg::constellation_msg::PipelineExitType; -use servo_msg::compositor_msg::ScriptListener; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; +use servo_net::server::SharedServerProxy; use servo_net::storage_task::StorageTask; -use servo_util::smallvec::SmallVec1; +use servo_util::ipc::{IpcReceiver, IpcSender}; use std::any::Any; use geom::point::Point2D; use geom::rect::Rect; -use serialize::{Encodable, Encoder}; +use serialize::{Decodable, Decoder, Encodable, Encoder}; /// The address of a node. Layout sends these back. They must be validated via /// `from_untrusted_node_address` before they can be used, because we do not trust layout. #[allow(raw_pointer_deriving)] -#[deriving(Copy, Clone)] +#[deriving(Copy, Clone, Show)] pub struct UntrustedNodeAddress(pub *const c_void); +impl UntrustedNodeAddress { + #[inline] + pub fn to_uint(&self) -> uint { + let UntrustedNodeAddress(ptr) = *self; + ptr as uint + } +} + +impl> Encodable for UntrustedNodeAddress { + fn encode(&self, encoder: &mut S) -> Result<(),E> { + let UntrustedNodeAddress(ptr) = *self; + (ptr as uint).encode(encoder) + } +} + +impl> Decodable for UntrustedNodeAddress { + fn decode(decoder: &mut D) -> Result { + let ptr: uint = try!(Decodable::decode(decoder)); + Ok(UntrustedNodeAddress(ptr as *const c_void)) + } +} + + +#[deriving(Encodable, Decodable)] pub struct NewLayoutInfo { pub old_pipeline_id: PipelineId, pub new_pipeline_id: PipelineId, pub subpage_id: SubpageId, - pub layout_chan: Box, // opaque reference to a LayoutChannel + // FIXME(pcwalton): Terrible, pass an FD instead. + pub layout_chan: (uint, uint), } /// Messages sent from the constellation to the script task +#[deriving(Encodable, Decodable)] pub enum ConstellationControlMsg { /// Loads a new URL on the specified pipeline. Load(PipelineId, LoadData), @@ -73,9 +100,10 @@ pub enum ConstellationControlMsg { } /// Events from the compositor that the script task needs to know about +#[deriving(Encodable, Decodable)] pub enum CompositorEvent { ResizeEvent(WindowSizeData), - ReflowEvent(SmallVec1), + ReflowEvent(Vec), ClickEvent(uint, Point2D), MouseDownEvent(uint, Point2D), MouseUpEvent(uint, Point2D), @@ -89,29 +117,30 @@ pub struct OpaqueScriptLayoutChannel(pub (Box, Box)); /// Encapsulates external communication with the script task. #[deriving(Clone)] -pub struct ScriptControlChan(pub Sender); +pub struct ScriptControlChan(pub IpcSender); -impl, E> Encodable for ScriptControlChan { - fn encode(&self, _s: &mut S) -> Result<(), E> { - Ok(()) +impl ScriptControlChan { + #[inline] + pub fn fd(&self) -> c_int { + let ScriptControlChan(ref ipc_channel) = *self; + ipc_channel.fd() } } pub trait ScriptTaskFactory { - fn create(_phantom: Option<&mut Self>, - id: PipelineId, - compositor: C, - layout_chan: &OpaqueScriptLayoutChannel, - control_chan: ScriptControlChan, - control_port: Receiver, - constellation_msg: ConstellationChan, - failure_msg: Failure, - resource_task: ResourceTask, - storage_task: StorageTask, - image_cache_task: ImageCacheTask, - devtools_chan: Option, - window_size: WindowSizeData) - where C: ScriptListener + Send; + fn create(_phantom: Option<&mut Self>, + id: PipelineId, + compositor: SharedServerProxy, + layout_chan: &OpaqueScriptLayoutChannel, + contellation_to_script_receiver: IpcReceiver, + constellation_msg: ConstellationChan, + failure_msg: Failure, + layout_to_script_receiver: Receiver, + resource_task: ResourceTask, + storage_task: StorageTask, + image_cache_task: ImageCacheTask, + devtools_chan: Option, + window_size: WindowSizeData); fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel; fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) -> Box; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 166ab14a397a..fe3dd2bf0d24 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -514,6 +514,7 @@ dependencies = [ "hyper 0.0.1 (git+https://github.com/servo/hyper?ref=servo)", "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", + "net 0.0.1", "style 0.0.1", "url 0.2.4 (git+https://github.com/servo/rust-url)", "util 0.0.1", diff --git a/components/servo/content_process.rs b/components/servo/content_process.rs new file mode 100644 index 000000000000..aa29a69627db --- /dev/null +++ b/components/servo/content_process.rs @@ -0,0 +1,84 @@ +/* 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/. */ + +//! Content process initialization, for multiprocess mode. + +use platform::sandbox; + +use compositing::content_process::{BootstrapInfo, ContentProcess, ContentProcessIpc}; +use gfx::font_cache_task::FontCacheTask; +use gfx::paint_task::LayoutToPaintChan; +use layout::layout_task::LayoutTask; +use libc::c_int; +use script::script_task::ScriptTask; +use serialize::{Decodable, Encodable}; +use servo_msg::constellation_msg::ConstellationChan; +use servo_net::image_cache_task::ImageCacheTask; +use servo_net::resource_task; +use servo_net::server::ServerProxy; +use servo_net::storage_task::StorageTask; +use servo_util::ipc::{IpcReceiver, IpcSender}; +use servo_util::opts; +use servo_util::sbsf::{ServoDecoder, ServoEncoder}; +use servo_util::taskpool::TaskPool; +use servo_util::time::TimeProfiler; +use std::io::IoError; +use std::sync::{Arc, Mutex}; + +pub fn main(bootstrap_fd: c_int) { + let bootstrap_receiver = connect_ipc_receiver(bootstrap_fd); + let bootstrap_info: BootstrapInfo = bootstrap_receiver.recv(); + + // Must enter the sandbox *first* since on Linux seccomp only applies to the calling thread. + sandbox::enter(bootstrap_info.auxiliary_data.zone.clone()); + + opts::set_opts(bootstrap_info.opts); + + let shared_task_pool = TaskPool::new(8); + let resource_task = resource_task::new_resource_task(None); + let image_cache_task = ImageCacheTask::new(resource_task.clone(), shared_task_pool); + let time_profiler_chan = TimeProfiler::create(None); + let content_process: ContentProcess = ContentProcess { + ipc: ContentProcessIpc { + script_to_compositor_client: Arc::new(Mutex::new(connect_ipc_server( + bootstrap_info.script_to_compositor_client))), + script_port: connect_ipc_receiver(bootstrap_info.script_port), + constellation_chan: ConstellationChan::from_server_proxy(Arc::new(Mutex::new( + connect_ipc_server(bootstrap_info.constellation_chan)))), + storage_task: StorageTask::from_client(Arc::new(Mutex::new( + connect_ipc_server(bootstrap_info.storage_task)))), + pipeline_to_layout_port: connect_ipc_receiver(bootstrap_info.pipeline_to_layout_port), + layout_to_paint_chan: LayoutToPaintChan::from_channel( + connect_ipc_sender(bootstrap_info.layout_to_paint_chan)), + font_cache_task: FontCacheTask::from_client(Arc::new(Mutex::new( + connect_ipc_server(bootstrap_info.font_cache_task)))), + }, + resource_task: resource_task, + image_cache_task: image_cache_task, + time_profiler_chan: time_profiler_chan, + }; + + content_process.create_script_and_layout_threads(bootstrap_info.auxiliary_data); +} + +fn connect_ipc_receiver(fd: c_int) + -> IpcReceiver + where T: for<'a> Decodable,IoError> { + IpcReceiver::from_fd(fd) +} + +fn connect_ipc_sender(fd: c_int) + -> IpcSender + where T: for<'a> Encodable,IoError> { + IpcSender::from_fd(fd) +} + +fn connect_ipc_server((sender_fd, receiver_fd): (c_int, c_int)) -> ServerProxy + where M: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError>, + R: for<'a> Decodable,IoError> + + for<'a> Encodable,IoError> { + ServerProxy::from_fds(sender_fd, receiver_fd) +} + diff --git a/components/servo/lib.rs b/components/servo/lib.rs index c64e7a10fc56..3b5c9e7381cf 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -32,8 +32,6 @@ use compositing::{CompositorProxy, CompositorTask, Constellation}; #[cfg(not(test))] use servo_msg::constellation_msg::Msg as ConstellationMsg; #[cfg(not(test))] -use servo_msg::constellation_msg::ConstellationChan; -#[cfg(not(test))] use script::dom::bindings::codegen::RegisterBindings; #[cfg(not(test))] @@ -75,6 +73,8 @@ impl Browser where Window: WindowMethods + 'static { let (compositor_proxy, compositor_receiver) = WindowMethods::create_compositor_channel(&window); + let (compositor_server, compositor_server_proxy) = + CompositorTask::create_compositor_server_channel(); let time_profiler_chan = TimeProfiler::create(opts.time_profiler_period); let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period); let devtools_chan = opts.devtools_port.map(|port| { @@ -103,13 +103,14 @@ impl Browser where Window: WindowMethods + 'static { let storage_task = StorageTaskFactory::new(); let constellation_chan = Constellation::::start( - compositor_proxy_for_constellation, - resource_task, - image_cache_task, - font_cache_task, - time_profiler_chan_clone, - devtools_chan, - storage_task); + compositor_proxy_for_constellation, + compositor_server_proxy, + resource_task, + image_cache_task, + font_cache_task, + time_profiler_chan_clone, + devtools_chan, + storage_task); // Send the URL command to the constellation. let cwd = os::getcwd().unwrap(); @@ -121,8 +122,7 @@ impl Browser where Window: WindowMethods + 'static { Err(_) => panic!("URL parsing failed"), }; - let ConstellationChan(ref chan) = constellation_chan; - chan.send(ConstellationMsg::InitLoadUrl(url)); + constellation_chan.send(ConstellationMsg::InitLoadUrl(url)); } // Send the constallation Chan as the result @@ -135,6 +135,7 @@ impl Browser where Window: WindowMethods + 'static { let compositor = CompositorTask::create(window, compositor_proxy, compositor_receiver, + compositor_server, constellation_chan, time_profiler_chan, memory_profiler_chan); @@ -156,7 +157,7 @@ impl Browser where Window: WindowMethods + 'static { self.compositor.pinch_zoom_level() } - pub fn get_title_for_main_frame(&self) { + pub fn get_title_for_main_frame(&mut self) { self.compositor.get_title_for_main_frame() } diff --git a/components/servo/main.rs b/components/servo/main.rs index f2fbf67dfb37..dbbad06a14c9 100644 --- a/components/servo/main.rs +++ b/components/servo/main.rs @@ -2,14 +2,17 @@ * 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/. */ -#![feature(phase)] +#![feature(phase, macro_rules)] #![deny(unused_imports)] #![deny(unused_variables)] -#[cfg(target_os="android")] -extern crate libc; - +extern crate gfx; +extern crate layout; +extern crate "msg" as servo_msg; +extern crate "net" as servo_net; +extern crate script; +extern crate serialize; extern crate servo; extern crate time; extern crate "util" as servo_util; @@ -22,6 +25,8 @@ extern crate "glfw_app" as app; #[cfg(not(test))] extern crate compositing; +extern crate libc; + #[cfg(target_os="android")] #[phase(plugin, link)] extern crate android_glue; @@ -43,6 +48,26 @@ use std::borrow::ToOwned; #[cfg(not(any(test,target_os="android")))] use std::os; +use std::str::FromStr; + +mod content_process; + +pub mod platform { + #[cfg(target_os="macos")] + pub use self::macos::sandbox; + #[cfg(not(target_os="macos"))] + pub use self::linux::sandbox; + + #[cfg(target_os="macos")] + pub mod macos { + pub mod sandbox; + } + #[cfg(not(target_os="macos"))] + pub mod linux { + pub mod sandbox; + } +} + #[cfg(not(test))] struct BrowserWrapper { browser: Browser, @@ -107,6 +132,10 @@ fn setup_logging() { } fn main() { + if let Some(bootstrap_fd_string) = os::getenv("SERVO_CONTENT_PROCESS") { + return content_process::main(FromStr::from_str(bootstrap_fd_string.as_slice()).unwrap()) + } + if opts::from_cmdline_args(get_args().as_slice()) { setup_logging(); diff --git a/components/servo/platform/linux/sandbox.rs b/components/servo/platform/linux/sandbox.rs new file mode 100644 index 000000000000..ef53cf5698cd --- /dev/null +++ b/components/servo/platform/linux/sandbox.rs @@ -0,0 +1,330 @@ +/* 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/. */ + +//! A Linux-specific sandbox using `seccomp-bpf`. + +#![allow(dead_code, non_camel_case_types, non_upper_case_globals)] + +use compositing::content_process::Zone; + +use libc::{AF_INET, AF_INET6, AF_UNIX, O_NONBLOCK, O_RDONLY, c_int, c_ulong, c_ushort}; + +pub type rlim_t = u64; + +const AF_NETLINK: c_int = 16; + +const CLONE_VM: c_int = 0x00000100; +const CLONE_FS: c_int = 0x00000200; +const CLONE_FILES: c_int = 0x00000400; +const CLONE_SIGHAND: c_int = 0x00000800; +const CLONE_THREAD: c_int = 0x00010000; +const CLONE_SYSVSEM: c_int = 0x00040000; +const CLONE_SETTLS: c_int = 0x00080000; +const CLONE_PARENT_SETTID: c_int = 0x00100000; +const CLONE_CHILD_CLEARTID: c_int = 0x00200000; + +const O_NOCTTY: c_int = 256; +const O_CLOEXEC: c_int = 524288; + +const FIONREAD: c_int = 0x541b; +const NETLINK_ROUTE: c_int = 0; + +#[repr(C)] +struct rlimit { + rlim_cur: rlim_t, + rlim_max: rlim_t, +} + +const RLIMIT_FSIZE: c_int = 1; + +const EM_X86_64: u32 = 62; + +const NR_read: u32 = 0; +const NR_write: u32 = 1; +const NR_open: u32 = 2; +const NR_close: u32 = 3; +const NR_stat: u32 = 4; +const NR_fstat: u32 = 5; +const NR_poll: u32 = 7; +const NR_lseek: u32 = 8; +const NR_mmap: u32 = 9; +const NR_mprotect: u32 = 10; +const NR_munmap: u32 = 11; +const NR_brk: u32 = 12; +const NR_rt_sigreturn: u32 = 15; +const NR_ioctl: u32 = 16; +const NR_access: u32 = 21; +const NR_madvise: u32 = 28; +const NR_socket: u32 = 41; +const NR_connect: u32 = 42; +const NR_sendto: u32 = 44; +const NR_recvfrom: u32 = 45; +const NR_recvmsg: u32 = 47; +const NR_bind: u32 = 49; +const NR_getsockname: u32 = 51; +const NR_clone: u32 = 56; +const NR_exit: u32 = 60; +const NR_readlink: u32 = 89; +const NR_getuid: u32 = 102; +const NR_sigaltstack: u32 = 131; +const NR_futex: u32 = 202; +const NR_sched_getaffinity: u32 = 204; +const NR_exit_group: u32 = 231; +const NR_set_robust_list: u32 = 273; +const NR_sendmmsg: u32 = 307; +const NR_unknown_318: u32 = 318; + +const __AUDIT_ARCH_64BIT: u32 = 0x80000000; +const __AUDIT_ARCH_LE: u32 = 0x40000000; +const AUDIT_ARCH_X86_64: u32 = EM_X86_64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE; + +const PR_SET_SECCOMP: c_int = 22; +const PR_SET_NO_NEW_PRIVS: c_int = 38; + +const SECCOMP_MODE_FILTER: c_ulong = 2; + +#[repr(C)] +struct sock_filter { + code: u16, + jt: u8, + jf: u8, + k: u32, +} + +#[repr(C)] +struct sock_fprog { + len: c_ushort, + filter: *const sock_filter, +} + +const BPF_LD: u16 = 0x00; +const BPF_JMP: u16 = 0x05; +const BPF_RET: u16 = 0x06; + +const BPF_W: u16 = 0x00; +const BPF_ABS: u16 = 0x20; + +const BPF_JA: u16 = 0x00; +const BPF_JEQ: u16 = 0x10; +const BPF_JGT: u16 = 0x20; +const BPF_JGE: u16 = 0x30; +const BPF_JSET: u16 = 0x40; + +const BPF_K: u16 = 0x00; + +// The syscall structure: +const SYSCALL_NR_OFFSET: u32 = 0; +const ARCH_NR_OFFSET: u32 = 4; +const IP_OFFSET: u32 = 8; +const ARG_0_OFFSET: u32 = 16; +const ARG_1_OFFSET: u32 = 24; +const ARG_2_OFFSET: u32 = 32; + +const ARCH_NR: u32 = AUDIT_ARCH_X86_64; + +const SECCOMP_RET_KILL: u32 = 0; +const SECCOMP_RET_ALLOW: u32 = 0x7fff0000; + +macro_rules! bpf_stmt { + ($code:expr, $k:expr) => ( + sock_filter { + code: $code, + jt: 0, + jf: 0, + k: $k, + } + ) +} + +macro_rules! bpf_jump { + ($code:expr, $k:expr, $jt:expr, $jf:expr) => ( + sock_filter { + code: $code, + jt: $jt, + jf: $jf, + k: $k, + } + ) +} + +const BPF_VALIDATE_ARCHITECTURE_0: sock_filter = + bpf_stmt!(BPF_LD+BPF_W+BPF_ABS, ARCH_NR_OFFSET); +const BPF_VALIDATE_ARCHITECTURE_1: sock_filter = + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0); +const BPF_VALIDATE_ARCHITECTURE_2: sock_filter = + bpf_stmt!(BPF_RET+BPF_K, SECCOMP_RET_KILL); + +const BPF_EXAMINE_SYSCALL: sock_filter = + bpf_stmt!(BPF_LD+BPF_W+BPF_ABS, SYSCALL_NR_OFFSET); +const BPF_EXAMINE_ARG_0: sock_filter = + bpf_stmt!(BPF_LD+BPF_W+BPF_ABS, ARG_0_OFFSET); +const BPF_EXAMINE_ARG_1: sock_filter = + bpf_stmt!(BPF_LD+BPF_W+BPF_ABS, ARG_1_OFFSET); +const BPF_EXAMINE_ARG_2: sock_filter = + bpf_stmt!(BPF_LD+BPF_W+BPF_ABS, ARG_2_OFFSET); + +macro_rules! bpf_allow_syscall_if { + ($id:ident) => ( + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, $id, 0, 1) + ) +} + +const BPF_ALLOW_SYSCALL: sock_filter = + bpf_stmt!(BPF_RET+BPF_K, SECCOMP_RET_ALLOW); + +const BPF_KILL_PROCESS: sock_filter = + bpf_stmt!(BPF_RET+BPF_K, SECCOMP_RET_KILL); + +// TODO(pcwalton): When the resource task is rewritten, remove network access. +static FILTER: [sock_filter, ..93] = [ + BPF_VALIDATE_ARCHITECTURE_0, + BPF_VALIDATE_ARCHITECTURE_1, + BPF_VALIDATE_ARCHITECTURE_2, + + // Special handling for open(2): only allow file reading. + BPF_EXAMINE_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, NR_open, 0, 3), + BPF_EXAMINE_ARG_1, + bpf_jump!(BPF_JMP+BPF_JSET+BPF_K, + !(O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK) as u32, + 1, + 0), + BPF_ALLOW_SYSCALL, + + // Special handling for socket(2): only allow `AF_UNIX`, `AF_INET`, `AF_INET6`, or + // `PF_NETLINK` with `NETLINK_ROUTE` protocol. + BPF_EXAMINE_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, NR_socket, 0, 11), + BPF_EXAMINE_ARG_0, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, AF_UNIX as u32, 0, 1), + BPF_ALLOW_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, AF_INET as u32, 0, 1), + BPF_ALLOW_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, AF_INET6 as u32, 0, 1), + BPF_ALLOW_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, AF_NETLINK as u32, 0, 3), + BPF_EXAMINE_ARG_2, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, NETLINK_ROUTE as u32, 0, 1), + BPF_ALLOW_SYSCALL, + + // Special handling for ioctl(2): only allow `FIONREAD`. + BPF_EXAMINE_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, NR_ioctl, 0, 3), + BPF_EXAMINE_ARG_1, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, FIONREAD as u32, 0, 1), + BPF_ALLOW_SYSCALL, + + // Special handling for clone(2): only allow normal threads to be created. + BPF_EXAMINE_SYSCALL, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, NR_clone, 0, 3), + BPF_EXAMINE_ARG_0, + bpf_jump!(BPF_JMP+BPF_JEQ+BPF_K, + (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | + CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID) as u32, + 0, + 1), + BPF_ALLOW_SYSCALL, + + BPF_EXAMINE_SYSCALL, + bpf_allow_syscall_if!(NR_rt_sigreturn), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_exit_group), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_exit), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_read), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_write), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_mmap), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_mprotect), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_set_robust_list), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_close), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_sigaltstack), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_futex), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_recvmsg), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_sched_getaffinity), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_munmap), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_recvfrom), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_readlink), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_stat), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_madvise), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_fstat), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_lseek), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_sendto), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_unknown_318), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_brk), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_bind), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_getsockname), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_connect), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_access), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_poll), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_sendmmsg), + BPF_ALLOW_SYSCALL, + bpf_allow_syscall_if!(NR_getuid), + BPF_ALLOW_SYSCALL, + BPF_KILL_PROCESS, +]; + +/// Enters the Linux sandbox. +/// +/// The Zone doesn't do anything here, because I don't know any way to restrict which files can be +/// opened on Linux without `chroot`, and that's a privileged syscall. +pub fn enter(_: Zone) { + unsafe { + // Disallow writing by setting the max writable size to 0. + let rlimit = rlimit { + rlim_cur: 0, + rlim_max: 0, + }; + if setrlimit(RLIMIT_FSIZE, &rlimit) != 0 { + panic!("setrlimit(RLIMIT_FSIZE) failed") + } + + if prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0 { + panic!("prctl(PR_SET_NO_NEW_PRIVS) failed") + } + + let program = sock_fprog { + len: FILTER.len() as c_ushort, + filter: FILTER.as_ptr(), + }; + if prctl(PR_SET_SECCOMP, + SECCOMP_MODE_FILTER, + &program as *const sock_fprog as uint as c_ulong, + -1, + 0) != 0 { + panic!("prctl(PR_SET_SECCOMP) failed") + } + } +} + +extern { + fn prctl(option: c_int, arg2: c_ulong, arg3: c_ulong, arg4: c_ulong, arg5: c_ulong) -> c_int; + fn setrlimit(resource: c_int, rlim: *const rlimit) -> c_int; +} + diff --git a/components/servo/platform/macos/sandbox.rs b/components/servo/platform/macos/sandbox.rs new file mode 100644 index 000000000000..41bc16b39bb3 --- /dev/null +++ b/components/servo/platform/macos/sandbox.rs @@ -0,0 +1,76 @@ +/* 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/. */ + +//! A Mac OS-specific sandbox using Seatbelt (a.k.a. sandbox(7)). + +use compositing::content_process::Zone; +use libc::{c_char, c_int}; +use servo_util::resource_files; +use std::ptr; + +// TODO(pcwalton): Lock down file reading and network access once the resource task is rewritten +// (should be soon). +static PROFILE_COMMON: &'static str = " +(version 1) +(deny default) +(allow mach-lookup + (global-name \"com.apple.CoreServices.coreservicesd\") + (global-name \"com.apple.FontServer\")) +(allow network-outbound + (literal \"/private/var/run/mDNSResponder\") + (remote tcp \"*:443\") + (remote tcp \"*:80\")) +(allow sysctl-read) +(allow system-socket) +"; + +static PROFILE_LOCAL: &'static str = " +(allow file-read* + (subpath \"/\")) +"; + +static PROFILE_REMOTE: &'static str = " +(allow file-read* + (subpath \"/System/Library/Frameworks/ApplicationServices.framework\") + (subpath \"/System/Library/Fonts\") + (subpath \"/Library/Fonts\") + (subpath \"/usr/share/zoneinfo\") + (subpath \"%RESOURCES%\") + (literal \"/dev/urandom\") + (literal \"/private/etc/hosts\")) +(allow file-read-metadata + (literal \"/private/etc/localtime\") + (literal \"/etc\") + (literal \"/var\")) +"; + +pub fn enter(zone: Zone) { + let mut err = ptr::null_mut(); + let profile = format!("{}{}", + PROFILE_COMMON, + match zone { + Zone::Local => PROFILE_LOCAL, + Zone::Remote => PROFILE_REMOTE, + }); + + // Substitute `%RESOURCES%`, being careful not to allow for silly SQL injection-like stuff. + let resources_path = resource_files::resources_dir_path(); + if resources_path.display().as_cow().contains_char('"') { + // TODO(pcwalton): Figure out the Scheme dialect syntax for this... + panic!("can't sandbox on Mac OS X when the resource path contains a double quote in it") + } + let profile = profile.replace("%RESOURCES%", resources_path.display().as_cow().as_slice()); + + let profile = profile.to_c_str(); + unsafe { + if sandbox_init(profile.as_ptr() as *const c_char, 0, &mut err) != 0 { + panic!("failed to enter sandbox") + } + } +} + +extern { + fn sandbox_init(profile: *const c_char, flags: u64, errorbuf: *mut *mut c_char) -> c_int; +} + diff --git a/components/style/font_face.rs b/components/style/font_face.rs index e18bcb32f015..bab92677c7d8 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -31,13 +31,13 @@ pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device, } } -#[deriving(Clone, Show, PartialEq, Eq)] +#[deriving(Clone, Show, PartialEq, Eq, Encodable, Decodable)] pub enum Source { Url(UrlSource), Local(String), } -#[deriving(Clone, Show, PartialEq, Eq)] +#[deriving(Clone, Show, PartialEq, Eq, Encodable, Decodable)] pub struct UrlSource { pub url: Url, pub format_hints: Vec, diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 77d60cf85165..c1bbbe63b5df 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -1043,7 +1043,7 @@ pub mod longhands { } pub mod computed_value { use std::fmt; - #[deriving(PartialEq, Eq, Copy, Clone)] + #[deriving(PartialEq, Eq, Copy, Clone, Decodable, Encodable)] pub enum T { % for weight in range(100, 901, 100): Weight${weight}, @@ -1706,7 +1706,7 @@ pub mod longhands { use values::CSSFloat; // TODO(pcwalton): `blur`, `drop-shadow` - #[deriving(Clone, PartialEq, Show)] + #[deriving(Clone, PartialEq, Show, Decodable, Encodable)] pub enum Filter { Brightness(CSSFloat), Contrast(CSSFloat), @@ -1718,7 +1718,7 @@ pub mod longhands { Sepia(CSSFloat), } - #[deriving(Clone, PartialEq, Show)] + #[deriving(Clone, PartialEq, Show, Decodable, Encodable)] pub struct T { pub filters: Vec, } diff --git a/components/style/values.rs b/components/style/values.rs index fb8806c6c388..644c9ba041f3 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -12,7 +12,7 @@ macro_rules! define_css_keyword_enum { }; ($name: ident: $( $css: expr => $variant: ident ),+) => { #[allow(non_camel_case_types)] - #[deriving(Clone, Eq, PartialEq, FromPrimitive, Copy)] + #[deriving(Clone, Eq, PartialEq, FromPrimitive, Copy, Decodable, Encodable)] pub enum $name { $( $variant ),+ } @@ -405,7 +405,7 @@ pub mod specified { } } - #[deriving(Clone, PartialEq, PartialOrd, Copy)] + #[deriving(Clone, PartialEq, PartialOrd, Copy, Decodable, Encodable)] pub struct Angle(pub CSSFloat); impl fmt::Show for Angle { diff --git a/components/util/cursor.rs b/components/util/cursor.rs index 23ca2c0af4ae..9809bca5f0af 100644 --- a/components/util/cursor.rs +++ b/components/util/cursor.rs @@ -11,7 +11,7 @@ use text_writer::TextWriter; macro_rules! define_cursor { ($( $css: expr => $variant: ident = $value: expr, )+) => { - #[deriving(Clone, Copy, PartialEq, Eq, FromPrimitive, Show)] + #[deriving(Clone, Copy, PartialEq, Eq, FromPrimitive, Show, Decodable, Encodable)] #[repr(u8)] pub enum Cursor { $( $variant = $value ),+ diff --git a/components/util/dlist.rs b/components/util/dlist.rs index f22c07268bad..becce8fa7d0e 100644 --- a/components/util/dlist.rs +++ b/components/util/dlist.rs @@ -4,6 +4,7 @@ //! Utility functions for doubly-linked lists. +use serialize::{Decodable, Decoder, Encodable, Encoder}; use std::collections::DList; use std::mem; use std::ptr; @@ -123,3 +124,42 @@ pub fn prepend_from(this: &mut DList, other: &mut DList) { } } +/// Encodes a doubly-linked list. +pub fn encode_dlist(s: &mut S, dlist: &DList) + -> Result<(),E> + where T: Encodable, S: Encoder { + s.emit_seq(dlist.len(), |s| { + let mut result = Ok(()); + for (index, value) in dlist.iter().enumerate() { + if let Err(err) = s.emit_seq_elt(index, |s| value.encode(s)) { + result = Err(err); + break + } + } + result + }) +} + +/// Decodes a doubly-linked list. +pub fn decode_dlist(d: &mut D) + -> Result,E> + where T: Decodable, D: Decoder { + d.read_seq(|d, size| { + let mut error = None; + let mut dlist = DList::new(); + for i in range(0, size) { + match d.read_seq_elt(i, |d| Decodable::decode(d)) { + Ok(value) => dlist.push_back(value), + Err(err) => { + error = Some(err); + break + } + } + } + match error { + None => Ok(dlist), + Some(err) => Err(err), + } + }) +} + diff --git a/components/util/geometry.rs b/components/util/geometry.rs index 5c3ee808b43f..e1b6a038464f 100644 --- a/components/util/geometry.rs +++ b/components/util/geometry.rs @@ -8,7 +8,7 @@ use geom::rect::Rect; use geom::size::Size2D; use geom::num::Zero; -use serialize::{Encodable, Encoder}; +use serialize::{Decodable, Decoder, Encodable, Encoder}; use std::default::Default; use std::i32; use std::num::{Float, NumCast}; @@ -29,7 +29,7 @@ use std::fmt; /// /// The ratio between ScreenPx and DevicePixel for a given display be found by calling /// `servo::windowing::WindowMethods::hidpi_factor`. -#[deriving(Show, Copy)] +#[deriving(Show, Copy, Encodable, Decodable)] pub enum ScreenPx {} /// One CSS "px" in the coordinate system of the "initial viewport": @@ -41,7 +41,7 @@ pub enum ScreenPx {} /// /// At the default zoom level of 100%, one PagePx is equal to one ScreenPx. However, if the /// document is zoomed in or out then this scale may be larger or smaller. -#[deriving(Encodable, Show, Copy)] +#[deriving(Encodable, Decodable, Show, Copy)] pub enum ViewportPx {} /// One CSS "px" in the root coordinate system for the content document. @@ -50,7 +50,7 @@ pub enum ViewportPx {} /// This is the mobile-style "pinch zoom" that enlarges content without reflowing it. When the /// viewport zoom is not equal to 1.0, then the layout viewport is no longer the same physical size /// as the viewable area. -#[deriving(Encodable, Show, Copy)] +#[deriving(Encodable, Decodable, Show, Copy)] pub enum PagePx {} // In summary, the hierarchy of pixel units and the factors to convert from one to the next: @@ -118,6 +118,13 @@ impl> Encodable for Au { } } +impl> Decodable for Au { + fn decode(d: &mut D) -> Result { + let value: f64 = try!(Decodable::decode(d)); + Ok(Au::from_frac_px(value)) + } +} + impl fmt::Show for Au { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}px", to_frac_px(*self)) diff --git a/components/util/ipc.rs b/components/util/ipc.rs new file mode 100644 index 000000000000..92859e6cbcf8 --- /dev/null +++ b/components/util/ipc.rs @@ -0,0 +1,135 @@ +/* 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 platform::unix::ipc::ServoUnixSocket; +use libc::c_int; +use sbsf::{ServoDecoder, ServoEncoder}; +use serialize::{Decodable, Encodable}; +use std::io::{IoError, MemReader, MemWriter}; +use std::sync::{Arc, Mutex, MutexGuard}; + +pub struct IpcReceiver { + pipe: Arc>, +} + +impl Clone for IpcReceiver { + fn clone(&self) -> IpcReceiver { + IpcReceiver { + pipe: self.pipe.clone(), + } + } +} + +pub struct IpcSender { + pipe: Arc>, +} + +impl Clone for IpcSender { + fn clone(&self) -> IpcSender { + IpcSender { + pipe: self.pipe.clone(), + } + } +} + +/// Creates a new IPC channel and returns the receiving and sending ends of it respectively. +pub fn channel() -> (IpcReceiver, IpcSender) { + let (first, second) = ServoUnixSocket::pair().unwrap(); + (IpcReceiver { + pipe: Arc::new(Mutex::new(first)), + }, IpcSender { + pipe: Arc::new(Mutex::new(second)), + }) +} + +impl IpcReceiver where T: for<'a> Decodable,IoError> { + /// Constructs one end of an IPC channel from a file descriptor. + pub fn from_fd(fd: c_int) -> IpcReceiver { + IpcReceiver { + pipe: Arc::new(Mutex::new(ServoUnixSocket::from_fd(fd))), + } + } + + /// Constructs an IPC receiver from a raw Unix socket. + pub fn from_socket(socket: ServoUnixSocket) -> IpcReceiver { + IpcReceiver { + pipe: Arc::new(Mutex::new(socket)), + } + } + + /// Returns the raw file descriptor backing this IPC receiver. + pub fn fd(&self) -> c_int { + self.pipe.lock().fd() + } + + /// Returns the raw Unix socket backing this IPC receiver. + pub fn socket<'b>(&'b self) -> MutexGuard<'b,ServoUnixSocket> { + self.pipe.lock() + } + + pub fn recv(&self) -> T { + match self.recv_opt() { + Ok(msg) => msg, + Err(err) => panic!("failed to receive over IPC: {}", err), + } + } + + pub fn recv_opt(&self) -> Result { + let mut pipe = self.pipe.lock(); + let size = try!(pipe.read_le_uint()); + let bytes = try!(pipe.read_exact(size)); + let mut reader = MemReader::new(bytes); + let mut decoder = ServoDecoder { + reader: &mut reader, + }; + Decodable::decode(&mut decoder) + } +} + +impl IpcSender where T: for<'a> Encodable,IoError> { + /// Constructs one end of an IPC channel from a file descriptor. + pub fn from_fd(fd: c_int) -> IpcSender { + IpcSender { + pipe: Arc::new(Mutex::new(ServoUnixSocket::from_fd(fd))), + } + } + + /// Constructs an IPC sender from a raw Unix socket. + pub fn from_socket(socket: ServoUnixSocket) -> IpcSender { + IpcSender { + pipe: Arc::new(Mutex::new(socket)), + } + } + + /// Returns the raw file descriptor backing this IPC sender. + pub fn fd(&self) -> c_int { + self.pipe.lock().fd() + } + + /// Returns the raw Unix socket backing this IPC sender. + pub fn socket<'b>(&'b self) -> MutexGuard<'b,ServoUnixSocket> { + self.pipe.lock() + } + + pub fn send(&self, msg: T) { + match self.send_opt(msg) { + Ok(()) => {} + Err(err) => panic!("failed to send over IPC: {}", err), + } + } + + pub fn send_opt(&self, msg: T) -> Result<(),IoError> { + let mut writer = MemWriter::new(); + { + let mut encoder = ServoEncoder { + writer: &mut writer, + }; + try!(msg.encode(&mut encoder)); + } + let mut pipe = self.pipe.lock(); + try!(pipe.write_le_uint(writer.get_ref().len())); + pipe.write(writer.get_ref()) + } +} + diff --git a/components/util/lib.rs b/components/util/lib.rs index 9a97bdbbf788..37d7e92520f3 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -36,6 +36,12 @@ extern crate url; use std::sync::Arc; +pub mod platform { + pub mod unix { + pub mod ipc; + } +} + pub mod bloom; pub mod cache; pub mod cursor; @@ -44,6 +50,7 @@ pub mod deque; pub mod dlist; pub mod fnv; pub mod geometry; +pub mod ipc; pub mod logical_geometry; pub mod memory; pub mod namespace; @@ -51,6 +58,7 @@ pub mod opts; pub mod persistent_list; pub mod range; pub mod resource_files; +pub mod sbsf; pub mod smallvec; pub mod sort; pub mod str; diff --git a/components/util/opts.rs b/components/util/opts.rs index 1ed2eae06081..1126520e7222 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -12,15 +12,15 @@ use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use getopts; use std::collections::HashSet; -use std::cmp; +//use std::cmp; use std::io; use std::mem; use std::os; use std::ptr; -use std::rt; +//use std::rt; /// Global flags for Servo, currently set on the command line. -#[deriving(Clone)] +#[deriving(Clone, Decodable, Encodable)] pub struct Opts { /// The initial URLs to load. pub urls: Vec, @@ -52,8 +52,7 @@ pub struct Opts { /// Enable experimental web features (`-e`). pub enable_experimental: bool, - /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive - /// sequential algorithm. + /// The number of threads to use for layout (`-y`). Defaults to 3/2 the number of cores. pub layout_threads: uint, pub nonincremental_layout: bool, @@ -108,6 +107,13 @@ pub struct Opts { /// A specific path to find required resources (such as user-agent.css). pub resources_path: Option, + + /// Whether we are running in multiprocess mode. + pub multiprocess: bool, + + /// True if we should serialize display lists even in single-process mode. This is disabled by + /// default because it is currently very slow. + pub force_display_list_serialization: bool, } fn print_usage(app: &str, opts: &[getopts::OptGroup]) { @@ -131,6 +137,8 @@ pub fn print_debug_usage(app: &str) { print_option("trace-layout", "Write layout trace to an external file for debugging."); print_option("validate-display-list-geometry", "Display an error when display list geometry escapes overflow region."); + print_option("serialize-display-lists", + "Serialize display lists even in single-process mode."); println!(""); } @@ -175,6 +183,8 @@ pub fn default_opts() -> Opts { validate_display_list_geometry: false, profile_tasks: false, resources_path: None, + multiprocess: false, + force_display_list_serialization: false, } } @@ -204,6 +214,7 @@ pub fn from_cmdline_args(args: &[String]) -> bool { getopts::optflag("h", "help", "Print this message"), getopts::optopt("r", "render-api", "Set the rendering API to use", "gl|mesa"), getopts::optopt("", "resources-path", "Path to find static resources", "/home/servo/resources"), + getopts::optflag("", "multiprocess", "Run in multiprocess mode"), ); let opt_match = match getopts::getopts(args, opts.as_slice()) { @@ -266,7 +277,8 @@ pub fn from_cmdline_args(args: &[String]) -> bool { let mut layout_threads: uint = match opt_match.opt_str("y") { Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(), - None => cmp::max(rt::default_sched_threads() * 3 / 4, 1), + //None => cmp::max(rt::default_sched_threads() * 3 / 4, 1), + None => 1, }; let nonincremental_layout = opt_match.opt_present("i"); @@ -319,6 +331,9 @@ pub fn from_cmdline_args(args: &[String]) -> bool { dump_flow_tree: debug_options.contains(&"dump-flow-tree"), validate_display_list_geometry: debug_options.contains(&"validate-display-list-geometry"), resources_path: opt_match.opt_str("resources-path"), + multiprocess: opt_match.opt_present("multiprocess"), + force_display_list_serialization: + debug_options.contains(&"force-display-list-serialization"), }; set_opts(opts); diff --git a/components/util/platform/unix/ipc.rs b/components/util/platform/unix/ipc.rs new file mode 100644 index 000000000000..435ab825f783 --- /dev/null +++ b/components/util/platform/unix/ipc.rs @@ -0,0 +1,281 @@ +/* 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/. */ + +//! IPC over Unix sockets. + +use alloc::heap; +use libc::{mod, c_char, c_int, c_short, c_uint, c_void, size_t, socklen_t, ssize_t}; +use std::io::{IoError, IoResult}; +use std::mem; +use std::ptr; + +pub struct ServoUnixSocket { + fd: c_int, +} + +impl ServoUnixSocket { + #[inline] + pub fn pair() -> IoResult<(ServoUnixSocket, ServoUnixSocket)> { + let mut results = [0, 0]; + unsafe { + if socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut results[0]) >= 0 { + Ok((ServoUnixSocket::from_fd(results[0]), ServoUnixSocket::from_fd(results[1]))) + } else { + Err(IoError::last_error()) + } + } + } + + #[inline] + pub fn from_fd(fd: c_int) -> ServoUnixSocket { + ServoUnixSocket { + fd: fd, + } + } + + #[inline] + pub fn fd(&self) -> c_int { + self.fd + } + + pub fn close(&mut self) { + unsafe { + libc::close(self.fd); + } + self.fd = -1; + } + + pub fn forget(&mut self) { + self.fd = -1; + } + + pub fn dup(&self) -> ServoUnixSocket { + unsafe { + let new_fd = libc::dup(self.fd); + ServoUnixSocket::from_fd(new_fd) + } + } + + pub fn send_fds(&self, fds: &[c_int]) -> Result<(),IoError> { + let cmsg_len = mem::size_of::() + fds.len() * mem::size_of::(); + let cmsg_buf = unsafe { + heap::allocate(cmsg_len, mem::min_align_of::()) + }; + let cmsg = cmsg_buf as *mut u8 as *mut cmsghdr; + unsafe { + (*cmsg).cmsg_len = uint_to_cmsglen(cmsg_len); + (*cmsg).cmsg_level = libc::SOL_SOCKET; + (*cmsg).cmsg_type = SCM_RIGHTS; + ptr::copy_nonoverlapping_memory(cmsg.offset(1) as *mut u8 as *mut c_int, + fds.as_ptr(), + fds.len()); + } + + let mut dummy_data: c_char = 0; + let mut iovec = iovec { + iov_base: &mut dummy_data, + iov_len: 1, + }; + + let msghdr = msghdr { + msg_name: ptr::null_mut(), + msg_namelen: 0, + msg_iov: &mut iovec, + msg_iovlen: 1, + msg_control: cmsg as *mut c_void, + msg_controllen: uint_to_msg_controllen(cmsg_len), + msg_flags: 0, + }; + + let result; + unsafe { + result = sendmsg(self.fd, &msghdr, 0); + heap::deallocate(cmsg_buf, cmsg_len, mem::min_align_of::()); + } + match result { + length if length > 0 => Ok(()), + _ => { + error!("FD send failed"); + Err(IoError::last_error()) + } + } + } + + pub fn recv_fds(&self, fds: &mut [c_int]) -> Result { + let cmsg_len = mem::size_of::() + fds.len() * mem::size_of::(); + let cmsg_buf = unsafe { + heap::allocate(cmsg_len, mem::align_of::()) + }; + let cmsg = cmsg_buf as *mut u8 as *mut cmsghdr; + + let mut dummy_data: c_char = 0; + let mut iovec = iovec { + iov_base: &mut dummy_data, + iov_len: 1, + }; + + let mut msghdr = msghdr { + msg_name: ptr::null_mut(), + msg_namelen: 0, + msg_iov: &mut iovec, + msg_iovlen: 1, + msg_control: cmsg as *mut c_void, + msg_controllen: uint_to_msg_controllen(cmsg_len), + msg_flags: 0, + }; + + unsafe { + let result = recvmsg(self.fd, &mut msghdr, 0); + heap::deallocate(cmsg_buf, cmsg_len, mem::min_align_of::()); + match result { + length if length > 0 => {} + _ => { + error!("FD receive failed"); + return Err(IoError::last_error()) + } + } + + let mut fd_count = ((*cmsg).cmsg_len as uint - mem::size_of::()) / + mem::size_of::(); + if fd_count > fds.len() { + // FIXME(pcwalton): Should probably close any extraneous FDs that we got. + fd_count = fds.len() + } + ptr::copy_nonoverlapping_memory(fds.as_mut_ptr(), + cmsg.offset(1) as *const u8 as *const c_int, + fds.len()); + Ok(fd_count as c_uint) + } + } +} +impl Drop for ServoUnixSocket { + #[inline(never)] + fn drop(&mut self) { + self.close() + } +} + +impl Writer for ServoUnixSocket { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + unsafe { + let result = libc::send(self.fd, buf.as_ptr() as *const c_void, buf.len() as u64, 0); + if result == buf.len() as i64 { + Ok(()) + } else { + Err(IoError::last_error()) + } + } + } +} + + +impl Reader for ServoUnixSocket { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + unsafe { + match libc::recv(self.fd, buf.as_mut_ptr() as *mut c_void, buf.len() as u64, 0) { + length if length > 0 => Ok(length as uint), + 0 => Err(IoError::from_errno(54, false)), + _ => { + error!("read failed!"); + Err(IoError::last_error()) + } + } + } + } +} + +/// Polls the given set of file descriptors, exactly as `poll(2)` does. +pub fn poll_fds(pollfds: &mut [pollfd], timeout: Option) -> Result<(),IoError> { + unsafe { + if poll(pollfds.as_mut_ptr(), pollfds.len() as c_uint, timeout.unwrap_or(-1)) < -1 { + Err(IoError::last_error()) + } else { + Ok(()) + } + } +} + +#[cfg(target_os="macos")] +fn uint_to_cmsglen(cmsglen: uint) -> c_uint { + cmsglen as c_uint +} +#[cfg(target_os="linux")] +fn uint_to_cmsglen(cmsglen: uint) -> size_t { + cmsglen as size_t +} +#[cfg(target_os="macos")] +fn uint_to_msg_controllen(msg_controllen: uint) -> socklen_t { + msg_controllen as socklen_t +} +#[cfg(target_os="linux")] +fn uint_to_msg_controllen(msg_controllen: uint) -> size_t { + msg_controllen as size_t +} + +// FFI stuff follows: + +extern { + fn poll(fds: *mut pollfd, nfds: c_uint, timeout: c_int) -> c_int; + fn recvmsg(socket: c_int, message: *mut msghdr, flags: c_int) -> ssize_t; + fn sendmsg(socket: c_int, message: *const msghdr, flags: c_int) -> ssize_t; + fn socketpair(domain: c_int, socket_type: c_int, protocol: c_int, sv: *mut c_int) -> c_int; +} + +pub const POLLRDNORM: c_short = 0x0040; +pub const POLLRDBAND: c_short = 0x0080; +const SCM_RIGHTS: c_int = 0x01; + +#[cfg(target_os="macos")] +#[repr(C)] +struct msghdr { + msg_name: *mut c_void, + msg_namelen: socklen_t, + msg_iov: *mut iovec, + msg_iovlen: c_int, + msg_control: *mut c_void, + msg_controllen: socklen_t, + msg_flags: c_int, +} + +#[cfg(target_os="linux")] +#[repr(C)] +struct msghdr { + msg_name: *mut c_void, + msg_namelen: socklen_t, + msg_iov: *mut iovec, + msg_iovlen: size_t, + msg_control: *mut c_void, + msg_controllen: size_t, + msg_flags: c_int, +} + +#[repr(C)] +struct iovec { + iov_base: *mut c_char, + iov_len: size_t, +} + +#[cfg(target_os="macos")] +#[repr(C)] +struct cmsghdr { + cmsg_len: c_uint, + cmsg_level: c_int, + cmsg_type: c_int, +} + +#[cfg(target_os="linux")] +#[repr(C)] +struct cmsghdr { + cmsg_len: size_t, + cmsg_level: c_int, + cmsg_type: c_int, +} + +#[repr(C)] +pub struct pollfd { + pub fd: c_int, + pub events: c_short, + pub revents: c_short, +} + diff --git a/components/util/range.rs b/components/util/range.rs index ef6e7e0ff473..3c9452f7b261 100644 --- a/components/util/range.rs +++ b/components/util/range.rs @@ -175,7 +175,7 @@ macro_rules! int_range_index { } /// A range of indices -#[deriving(Clone, Encodable, Copy)] +#[deriving(Clone, Encodable, Decodable, Copy)] pub struct Range { begin: I, length: I, diff --git a/components/util/sbsf.rs b/components/util/sbsf.rs new file mode 100644 index 000000000000..9a4461ef334b --- /dev/null +++ b/components/util/sbsf.rs @@ -0,0 +1,386 @@ +/* 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/. */ + +//! The Servo Binary Serialization Format: an extremely simple serialization format that is +//! optimized for speed above all else. + +use serialize::{Decoder, Encoder}; +use std::char; +use std::io::{IoError, Reader, Writer}; + +pub struct ServoEncoder<'a> { + pub writer: &'a mut (Writer + 'a), +} + +impl<'a> Encoder for ServoEncoder<'a> { + #[inline] + fn emit_nil(&mut self) -> Result<(),IoError> { + Ok(()) + } + #[inline] + fn emit_uint(&mut self, value: uint) -> Result<(),IoError> { + self.writer.write_le_uint(value) + } + #[inline] + fn emit_u64(&mut self, value: u64) -> Result<(),IoError> { + self.writer.write_le_u64(value) + } + #[inline] + fn emit_u32(&mut self, value: u32) -> Result<(),IoError> { + self.writer.write_le_u32(value) + } + #[inline] + fn emit_u16(&mut self, value: u16) -> Result<(),IoError> { + self.writer.write_le_u16(value) + } + #[inline] + fn emit_u8(&mut self, value: u8) -> Result<(),IoError> { + self.writer.write_u8(value) + } + #[inline] + fn emit_int(&mut self, value: int) -> Result<(),IoError> { + self.writer.write_le_int(value) + } + #[inline] + fn emit_i64(&mut self, value: i64) -> Result<(),IoError> { + self.writer.write_le_i64(value) + } + #[inline] + fn emit_i32(&mut self, value: i32) -> Result<(),IoError> { + self.writer.write_le_i32(value) + } + #[inline] + fn emit_i16(&mut self, value: i16) -> Result<(),IoError> { + self.writer.write_le_i16(value) + } + #[inline] + fn emit_i8(&mut self, value: i8) -> Result<(),IoError> { + self.writer.write_i8(value) + } + #[inline] + fn emit_bool(&mut self, value: bool) -> Result<(),IoError> { + self.writer.write_u8(value as u8) + } + #[inline] + fn emit_f64(&mut self, value: f64) -> Result<(),IoError> { + self.writer.write_le_f64(value) + } + #[inline] + fn emit_f32(&mut self, value: f32) -> Result<(),IoError> { + self.writer.write_le_f32(value) + } + #[inline] + fn emit_char(&mut self, value: char) -> Result<(),IoError> { + self.writer.write_le_u32(value as u32) + } + #[inline] + fn emit_str(&mut self, value: &str) -> Result<(),IoError> { + try!(self.writer.write_le_uint(value.len())); + self.writer.write_str(value) + } + #[inline] + fn emit_enum(&mut self, _: &str, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_enum_variant(&mut self, + _: &str, + variant_id: uint, + _: uint, + f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + try!(self.writer.write_le_u16(variant_id as u16)); + f(self) + } + #[inline] + fn emit_enum_variant_arg(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_enum_struct_variant(&mut self, + _: &str, + variant_id: uint, + _: uint, + f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + try!(self.writer.write_le_u16(variant_id as u16)); + f(self) + } + #[inline] + fn emit_enum_struct_variant_field(&mut self, + _: &str, + _: uint, + f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_struct(&mut self, _: &str, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_struct_field(&mut self, + _: &str, + _: uint, + f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_tuple(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_tuple_arg(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_tuple_struct(&mut self, + _: &str, + _: uint, + f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_tuple_struct_arg(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_option(&mut self, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_option_none(&mut self) -> Result<(),IoError> { + self.writer.write_u8(0) + } + #[inline] + fn emit_option_some(&mut self, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + try!(self.writer.write_u8(1)); + f(self) + } + #[inline] + fn emit_seq(&mut self, len: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + try!(self.writer.write_le_uint(len)); + f(self) + } + #[inline] + fn emit_seq_elt(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_map(&mut self, len: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + try!(self.writer.write_le_uint(len)); + f(self) + } + #[inline] + fn emit_map_elt_key(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } + #[inline] + fn emit_map_elt_val(&mut self, _: uint, f: |&mut ServoEncoder<'a>| -> Result<(),IoError>) + -> Result<(),IoError> { + f(self) + } +} + +pub struct ServoDecoder<'a> { + pub reader: &'a mut (Reader + 'a), +} + +impl<'a> Decoder for ServoDecoder<'a> { + #[inline] + fn read_nil(&mut self) -> Result<(),IoError> { + Ok(()) + } + #[inline] + fn read_uint(&mut self) -> Result { + self.reader.read_le_uint() + } + #[inline] + fn read_u64(&mut self) -> Result { + self.reader.read_le_u64() + } + #[inline] + fn read_u32(&mut self) -> Result { + self.reader.read_le_u32() + } + #[inline] + fn read_u16(&mut self) -> Result { + self.reader.read_le_u16() + } + #[inline] + fn read_u8(&mut self) -> Result { + self.reader.read_u8() + } + #[inline] + fn read_int(&mut self) -> Result { + self.reader.read_le_int() + } + #[inline] + fn read_i64(&mut self) -> Result { + self.reader.read_le_i64() + } + #[inline] + fn read_i32(&mut self) -> Result { + self.reader.read_le_i32() + } + #[inline] + fn read_i16(&mut self) -> Result { + self.reader.read_le_i16() + } + #[inline] + fn read_i8(&mut self) -> Result { + self.reader.read_i8() + } + #[inline] + fn read_bool(&mut self) -> Result { + Ok(try!(self.reader.read_u8()) != 0) + } + #[inline] + fn read_f64(&mut self) -> Result { + self.reader.read_le_f64() + } + #[inline] + fn read_f32(&mut self) -> Result { + self.reader.read_le_f32() + } + #[inline] + fn read_char(&mut self) -> Result { + Ok(char::from_u32(try!(self.reader.read_le_u32())).unwrap()) + } + #[inline] + fn read_str(&mut self) -> Result { + let len = try!(self.reader.read_le_uint()); + let bytes = try!(self.reader.read_exact(len)); + Ok(String::from_utf8(bytes).unwrap()) + } + #[inline] + fn read_enum(&mut self, _: &str, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_enum_variant(&mut self, + _: &[&str], + f: |&mut ServoDecoder<'a>, uint| -> Result) + -> Result { + let index = try!(self.reader.read_le_u16()); + f(self, index as uint) + } + #[inline] + fn read_enum_variant_arg(&mut self, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_enum_struct_variant(&mut self, + _: &[&str], + f: |&mut ServoDecoder<'a>, uint| -> Result) + -> Result { + let index = try!(self.reader.read_le_u16()); + f(self, index as uint) + } + #[inline] + fn read_enum_struct_variant_field(&mut self, + _: &str, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_struct(&mut self, + _: &str, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_struct_field(&mut self, + _: &str, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_tuple(&mut self, _: uint, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_tuple_arg(&mut self, _: uint, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_tuple_struct(&mut self, + _: &str, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_tuple_struct_arg(&mut self, + _: uint, + f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_option(&mut self, f: |&mut ServoDecoder<'a>, bool| -> Result) + -> Result { + let is_some = try!(self.reader.read_u8()) != 0; + f(self, is_some) + } + #[inline] + fn read_seq(&mut self, f: |&mut ServoDecoder<'a>, uint| -> Result) + -> Result { + let len = try!(self.reader.read_le_uint()); + f(self, len) + } + #[inline] + fn read_seq_elt(&mut self, _: uint, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_map(&mut self, f: |&mut ServoDecoder<'a>, uint| -> Result) + -> Result { + let len = try!(self.reader.read_le_uint()); + f(self, len) + } + #[inline] + fn read_map_elt_key(&mut self, _: uint, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn read_map_elt_val(&mut self, _: uint, f: |&mut ServoDecoder<'a>| -> Result) + -> Result { + f(self) + } + #[inline] + fn error(&mut self, _: &str) -> IoError { + IoError::from_errno(0, false) + } +} + diff --git a/components/util/task.rs b/components/util/task.rs index 44c2ff284dc0..1d8fce6fff3c 100644 --- a/components/util/task.rs +++ b/components/util/task.rs @@ -3,9 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; -use std::task; use std::comm::Sender; -use std::task::TaskBuilder; +use std::task::{mod, TaskBuilder}; use task_state; pub fn spawn_named(name: String, f: proc():Send) { @@ -38,3 +37,4 @@ pub fn spawn_named_with_send_on_failure(name: &'static str, } }); } + diff --git a/components/util/workqueue.rs b/components/util/workqueue.rs index ee14a4d1a506..9b2f9f7a0b66 100644 --- a/components/util/workqueue.rs +++ b/components/util/workqueue.rs @@ -283,7 +283,9 @@ impl WorkQueue { // Tell the workers to start. let mut work_count = AtomicUint::new(self.work_count); for worker in self.workers.iter_mut() { - worker.chan.send(WorkerMsg::Start(worker.deque.take().unwrap(), &mut work_count, &self.data)) + worker.chan.send(WorkerMsg::Start(worker.deque.take().unwrap(), + &mut work_count, + &self.data)) } // Wait for the work to finish.