From fd175b6dfdb555a553c8df92ffe485ec24e11dad Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 14:58:48 -0800 Subject: [PATCH 1/7] util: Introduce a new IPC abstraction based on Unix domain sockets --- components/util/ipc.rs | 135 +++++++++++++ components/util/platform/unix/ipc.rs | 281 +++++++++++++++++++++++++++ 2 files changed, 416 insertions(+) create mode 100644 components/util/ipc.rs create mode 100644 components/util/platform/unix/ipc.rs 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/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, +} + From ff6e9fa40365d905f65991a1b4bea362f880c24a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 14:59:06 -0800 Subject: [PATCH 2/7] util: Introduce a new, simple serialization format --- components/util/sbsf.rs | 386 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 components/util/sbsf.rs 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) + } +} + From d4e84b030fb499f12b2db3c3060ea3c4ef36c34c Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 14:59:20 -0800 Subject: [PATCH 3/7] net: Introduce a "server" abstraction that makes message passing simpler and easier for IPC --- components/net/server.rs | 271 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 components/net/server.rs 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); + } + }); +} + From e75ea7d4e0919b92cadb66de3174ff11f25677b7 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 14:59:59 -0800 Subject: [PATCH 4/7] compositing: Port Servo to use IPC --- components/compositing/compositor.rs | 27 +-- components/compositing/compositor_task.rs | 83 ++++---- components/compositing/constellation.rs | 61 +++--- components/compositing/headless.rs | 20 +- components/compositing/lib.rs | 2 + components/compositing/pipeline.rs | 106 +++++----- components/gfx/display_list/mod.rs | 93 +++++++-- components/gfx/font.rs | 2 +- components/gfx/font_cache_task.rs | 169 +++++++-------- components/gfx/font_context.rs | 22 +- components/gfx/font_template.rs | 2 +- components/gfx/paint_task.rs | 196 ++++++++++++------ .../gfx/platform/freetype/font_template.rs | 1 + .../gfx/platform/macos/font_template.rs | 20 +- components/gfx/text/glyph.rs | 10 +- components/gfx/text/text_run.rs | 4 +- components/layout/construct.rs | 10 +- components/layout/context.rs | 8 +- components/layout/display_list_builder.rs | 13 +- components/layout/fragment.rs | 6 +- components/layout/inline.rs | 8 +- components/layout/layout_task.rs | 169 ++++++++------- components/layout/parallel.rs | 21 +- components/layout/text.rs | 17 +- components/layout_traits/lib.rs | 19 +- components/msg/Cargo.toml | 3 + components/msg/compositor_msg.rs | 25 +-- components/msg/constellation_msg.rs | 54 +++-- components/msg/lib.rs | 3 + components/net/image_cache_task.rs | 7 - components/net/lib.rs | 4 +- components/net/storage_task.rs | 164 +++++++++------ components/script/dom/bindings/trace.rs | 38 +++- components/script/dom/htmliframeelement.rs | 14 +- components/script/dom/node.rs | 2 +- components/script/dom/storage.rs | 58 +++--- components/script/dom/window.rs | 17 +- components/script/layout_interface.rs | 4 +- components/script/page.rs | 28 ++- components/script/script_task.rs | 165 +++++++++------ components/script_traits/lib.rs | 79 ++++--- components/servo/Cargo.lock | 1 + components/servo/lib.rs | 25 +-- components/servo/main.rs | 37 +++- components/style/font_face.rs | 4 +- components/style/properties/mod.rs.mako | 6 +- components/style/values.rs | 4 +- components/util/cursor.rs | 2 +- components/util/dlist.rs | 40 ++++ components/util/geometry.rs | 15 +- components/util/lib.rs | 8 + components/util/opts.rs | 27 ++- components/util/range.rs | 2 +- components/util/task.rs | 4 +- components/util/workqueue.rs | 4 +- 55 files changed, 1179 insertions(+), 754 deletions(-) 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/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/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/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/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/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/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/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. From 0ae76a49564bcd930e6bebe32e5d2c7418689612 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 15:00:43 -0800 Subject: [PATCH 5/7] compositing: Introduce content process handling for multiprocess mode --- components/compositing/content_process.rs | 165 ++++++++++++++++++++++ components/servo/content_process.rs | 84 +++++++++++ 2 files changed, 249 insertions(+) create mode 100644 components/compositing/content_process.rs create mode 100644 components/servo/content_process.rs 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/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) +} + From 39d53a8e4eeca106c7aab05a20735fb64cf0a107 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 15:01:06 -0800 Subject: [PATCH 6/7] servo: Introduce simple sandboxing via Seatbelt on Mac OS X. Until we rewrite the resource task, we have to allow outbound network connections. This should be done soon, however. --- components/servo/platform/macos/sandbox.rs | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 components/servo/platform/macos/sandbox.rs 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; +} + From ec0c0295868e860274e7fe996538f39997e4dbcb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 27 Jan 2015 15:01:41 -0800 Subject: [PATCH 7/7] servo: Introduce simple sandboxing via `seccomp-bpf` on Linux. Until we rewrite the resource task, we have to allow outbound network connections. This should be done soon, however. I'm not sure how to restrict access to specific files on the filesystem without root privileges. --- components/servo/platform/linux/sandbox.rs | 330 +++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 components/servo/platform/linux/sandbox.rs 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; +} +