<commit_before>/* 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/. */

//! Servo, the mighty web browser engine from the future.
//!
//! This is a very simple library that wires all of Servo's components
//! together as type `Browser`, along with a generic client
//! implementing the `WindowMethods` trait, to create a working web
//! browser.
//!
//! The `Browser` type is responsible for configuring a
//! `Constellation`, which does the heavy lifting of coordinating all
//! of Servo's internal subsystems, including the `ScriptThread` and the
//! `LayoutThread`, as well maintains the navigation context.
//!
//! The `Browser` is fed events from a generic type that implements the
//! `WindowMethods` trait.

extern crate env_logger;
#[cfg(not(target_os = "windows"))]
extern crate gaol;
#[macro_use]
extern crate gleam;
extern crate log;

pub extern crate bluetooth;
pub extern crate bluetooth_traits;
pub extern crate canvas;
pub extern crate canvas_traits;
pub extern crate compositing;
pub extern crate constellation;
pub extern crate debugger;
pub extern crate devtools;
pub extern crate devtools_traits;
pub extern crate euclid;
pub extern crate gfx;
pub extern crate ipc_channel;
pub extern crate layout_thread;
pub extern crate msg;
pub extern crate net;
pub extern crate net_traits;
pub extern crate profile;
pub extern crate profile_traits;
pub extern crate script;
pub extern crate script_traits;
pub extern crate script_layout_interface;
pub extern crate style;
pub extern crate url;
pub extern crate util;

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

extern crate webrender;
extern crate webrender_traits;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use compositing::{CompositorProxy, IOCompositor};
use compositing::compositor_thread::InitialCompositorState;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use constellation::{Constellation, InitialConstellationState, UnprivilegedPipelineContent};
use constellation::{FromCompositorLogger, FromScriptLogger};
#[cfg(not(target_os = "windows"))]
use constellation::content_process_sandbox_profile;
use env_logger::Logger as EnvLogger;
#[cfg(not(target_os = "windows"))]
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcSender};
use log::{Log, LogMetadata, LogRecord};
use net::image_cache_thread::new_image_cache_thread;
use net::resource_thread::new_resource_threads;
use net_traits::IpcSend;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use script_traits::{ConstellationMsg, SWManagerSenders, ScriptMsg};
use std::cmp::max;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use util::opts;
use util::prefs::PREFS;
use util::resource_files::resources_dir_path;

pub use gleam::gl;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Browser<Window: WindowMethods + 'static> {
    compositor: IOCompositor<Window>,
    constellation_chan: Sender<ConstellationMsg>,
}

impl<Window> Browser<Window> where Window: WindowMethods + 'static {
    pub fn new(window: Rc<Window>) -> Browser<Window> {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            window.create_compositor_channel();
        let supports_clipboard = window.supports_clipboard();
        let time_profiler_chan = profile_time::Profiler::create(&opts.time_profiling,
                                                                opts.time_profiler_trace_path.clone());
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let debugger_chan = opts.debugger_port.map(|port| {
            debugger::start_server(port)
        });
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        let mut resource_path = resources_dir_path().unwrap();
        resource_path.push("shaders");

        let (webrender, webrender_api_sender) = {
            // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up!
            let scale_factor = window.scale_factor().get();
            let device_pixel_ratio = match opts.device_pixels_per_px {
                Some(device_pixels_per_px) => device_pixels_per_px,
                None => match opts.output_file {
                    Some(_) => 1.0,
                    None => scale_factor,
                }
            };

            let renderer_kind = if opts::get().should_use_osmesa() {
                webrender_traits::RendererKind::OSMesa
            } else {
                webrender_traits::RendererKind::Native
            };

            webrender::Renderer::new(webrender::RendererOptions {
                device_pixel_ratio: device_pixel_ratio,
                resource_path: resource_path,
                enable_aa: opts.enable_text_antialiasing,
                enable_msaa: opts.use_msaa,
                enable_profiler: opts.webrender_stats,
                debug: opts.webrender_debug,
                enable_recording: false,
                precache_shaders: opts.precache_shaders,
                enable_scrollbars: opts.output_file.is_none(),
                renderer_kind: renderer_kind,
                enable_subpixel_aa: opts.enable_subpixel_text_antialiasing,
            })
        };

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let (constellation_chan, sw_senders) = create_constellation(opts.clone(),
                                                                    compositor_proxy.clone_compositor_proxy(),
                                                                    time_profiler_chan.clone(),
                                                                    mem_profiler_chan.clone(),
                                                                    debugger_chan,
                                                                    devtools_chan,
                                                                    supports_clipboard,
                                                                    webrender_api_sender.clone());

        // Send the constellation's swmanager sender to service worker manager thread
        script::init(sw_senders);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = IOCompositor::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan.clone(),
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
            webrender: webrender,
            webrender_api_sender: webrender_api_sender,
        });

        Browser {
            compositor: compositor,
            constellation_chan: constellation_chan,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }

    pub fn setup_logging(&self) {
        let constellation_chan = self.constellation_chan.clone();
        log::set_logger(|max_log_level| {
            let env_logger = EnvLogger::new();
            let con_logger = FromCompositorLogger::new(constellation_chan);
            let filter = max(env_logger.filter(), con_logger.filter());
            let logger = BothLogger(env_logger, con_logger);
            max_log_level.set(filter);
            Box::new(logger)
        }).expect("Failed to set logger.")
    }
}

fn create_constellation(opts: opts::Opts,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        debugger_chan: Option<debugger::Sender>,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool,
                        webrender_api_sender: webrender_traits::RenderApiSender)
                        -> (Sender<ConstellationMsg>, SWManagerSenders) {
    let bluetooth_thread: IpcSender<BluetoothRequest> = BluetoothThreadFactory::new();

    let (public_resource_threads, private_resource_threads) =
        new_resource_threads(opts.user_agent,
                             devtools_chan.clone(),
                             time_profiler_chan.clone(),
                             opts.config_dir.map(Into::into));
    let image_cache_thread = new_image_cache_thread(public_resource_threads.sender(),
                                                    webrender_api_sender.create_api());
    let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
                                                 Some(webrender_api_sender.create_api()));

    let resource_sender = public_resource_threads.sender();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        debugger_chan: debugger_chan,
        devtools_chan: devtools_chan,
        bluetooth_thread: bluetooth_thread,
        image_cache_thread: image_cache_thread,
        font_cache_thread: font_cache_thread,
        public_resource_threads: public_resource_threads,
        private_resource_threads: private_resource_threads,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
        webrender_api_sender: webrender_api_sender,
    };
    let (constellation_chan, from_swmanager_sender) =
        Constellation::<script_layout_interface::message::Msg,
                        layout_thread::LayoutThread,
                        script::script_thread::ScriptThread>::start(initial_state);

    if let Some(url) = opts.url {
        constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
    };

    // channels to communicate with Service Worker Manager
    let sw_senders = SWManagerSenders {
        swmanager_sender: from_swmanager_sender,
        resource_sender: resource_sender
    };

    (constellation_chan, sw_senders)
}

// A logger that logs to two downstream loggers.
// This should probably be in the log crate.
struct BothLogger<Log1, Log2>(Log1, Log2);

impl<Log1, Log2> Log for BothLogger<Log1, Log2> where Log1: Log, Log2: Log {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        self.0.enabled(metadata) || self.1.enabled(metadata)
    }

    fn log(&self, record: &LogRecord) {
        self.0.log(record);
        self.1.log(record);
    }
}

pub fn set_logger(constellation_chan: IpcSender<ScriptMsg>) {
    log::set_logger(|max_log_level| {
        let env_logger = EnvLogger::new();
        let con_logger = FromScriptLogger::new(constellation_chan);
        let filter = max(env_logger.filter(), con_logger.filter());
        let logger = BothLogger(env_logger, con_logger);
        max_log_level.set(filter);
        Box::new(logger)
    }).expect("Failed to set logger.")
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());
    PREFS.extend(unprivileged_content.prefs());
    set_logger(unprivileged_content.constellation_chan());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
       create_sandbox();
    }

    // send the required channels to the service worker manager
    let sw_senders = unprivileged_content.swmanager_senders();
    script::init(sw_senders);

    unprivileged_content.start_all::<script_layout_interface::message::Msg,
                                     layout_thread::LayoutThread,
                                     script::script_thread::ScriptThread>(true);
}

// This is a workaround for https://github.com/rust-lang/rust/pull/30175 until
// https://github.com/lfairy/rust-errno/pull/5 lands, and should be removed once
// we update Servo with the rust-errno crate.
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern fn __errno_location() -> *mut i32 {
    extern { fn __errno() -> *mut i32; }
    __errno()
}

#[cfg(not(target_os = "windows"))]
fn create_sandbox() {
    ChildSandbox::new(content_process_sandbox_profile()).activate()
        .expect("Failed to activate sandbox!");
}

#[cfg(target_os = "windows")]
fn create_sandbox() {
    panic!("Sandboxing is not supported on Windows.");
}
<commit_msg>Don't unnecessarily clone entire `opts::Opt` structure.<commit_after>/* 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/. */

//! Servo, the mighty web browser engine from the future.
//!
//! This is a very simple library that wires all of Servo's components
//! together as type `Browser`, along with a generic client
//! implementing the `WindowMethods` trait, to create a working web
//! browser.
//!
//! The `Browser` type is responsible for configuring a
//! `Constellation`, which does the heavy lifting of coordinating all
//! of Servo's internal subsystems, including the `ScriptThread` and the
//! `LayoutThread`, as well maintains the navigation context.
//!
//! The `Browser` is fed events from a generic type that implements the
//! `WindowMethods` trait.

extern crate env_logger;
#[cfg(not(target_os = "windows"))]
extern crate gaol;
#[macro_use]
extern crate gleam;
extern crate log;

pub extern crate bluetooth;
pub extern crate bluetooth_traits;
pub extern crate canvas;
pub extern crate canvas_traits;
pub extern crate compositing;
pub extern crate constellation;
pub extern crate debugger;
pub extern crate devtools;
pub extern crate devtools_traits;
pub extern crate euclid;
pub extern crate gfx;
pub extern crate ipc_channel;
pub extern crate layout_thread;
pub extern crate msg;
pub extern crate net;
pub extern crate net_traits;
pub extern crate profile;
pub extern crate profile_traits;
pub extern crate script;
pub extern crate script_traits;
pub extern crate script_layout_interface;
pub extern crate style;
pub extern crate url;
pub extern crate util;

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

extern crate webrender;
extern crate webrender_traits;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use compositing::{CompositorProxy, IOCompositor};
use compositing::compositor_thread::InitialCompositorState;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use constellation::{Constellation, InitialConstellationState, UnprivilegedPipelineContent};
use constellation::{FromCompositorLogger, FromScriptLogger};
#[cfg(not(target_os = "windows"))]
use constellation::content_process_sandbox_profile;
use env_logger::Logger as EnvLogger;
#[cfg(not(target_os = "windows"))]
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcSender};
use log::{Log, LogMetadata, LogRecord};
use net::image_cache_thread::new_image_cache_thread;
use net::resource_thread::new_resource_threads;
use net_traits::IpcSend;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use script_traits::{ConstellationMsg, SWManagerSenders, ScriptMsg};
use std::borrow::Cow;
use std::cmp::max;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use url::Url;
use util::opts;
use util::prefs::PREFS;
use util::resource_files::resources_dir_path;

pub use gleam::gl;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Browser<Window: WindowMethods + 'static> {
    compositor: IOCompositor<Window>,
    constellation_chan: Sender<ConstellationMsg>,
}

impl<Window> Browser<Window> where Window: WindowMethods + 'static {
    pub fn new(window: Rc<Window>) -> Browser<Window> {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            window.create_compositor_channel();
        let supports_clipboard = window.supports_clipboard();
        let time_profiler_chan = profile_time::Profiler::create(&opts.time_profiling,
                                                                opts.time_profiler_trace_path.clone());
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let debugger_chan = opts.debugger_port.map(|port| {
            debugger::start_server(port)
        });
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        let mut resource_path = resources_dir_path().unwrap();
        resource_path.push("shaders");

        let (webrender, webrender_api_sender) = {
            // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up!
            let scale_factor = window.scale_factor().get();
            let device_pixel_ratio = match opts.device_pixels_per_px {
                Some(device_pixels_per_px) => device_pixels_per_px,
                None => match opts.output_file {
                    Some(_) => 1.0,
                    None => scale_factor,
                }
            };

            let renderer_kind = if opts::get().should_use_osmesa() {
                webrender_traits::RendererKind::OSMesa
            } else {
                webrender_traits::RendererKind::Native
            };

            webrender::Renderer::new(webrender::RendererOptions {
                device_pixel_ratio: device_pixel_ratio,
                resource_path: resource_path,
                enable_aa: opts.enable_text_antialiasing,
                enable_msaa: opts.use_msaa,
                enable_profiler: opts.webrender_stats,
                debug: opts.webrender_debug,
                enable_recording: false,
                precache_shaders: opts.precache_shaders,
                enable_scrollbars: opts.output_file.is_none(),
                renderer_kind: renderer_kind,
                enable_subpixel_aa: opts.enable_subpixel_text_antialiasing,
            })
        };

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let (constellation_chan, sw_senders) = create_constellation(opts.user_agent.clone(),
                                                                    opts.config_dir.clone(),
                                                                    opts.url.clone(),
                                                                    compositor_proxy.clone_compositor_proxy(),
                                                                    time_profiler_chan.clone(),
                                                                    mem_profiler_chan.clone(),
                                                                    debugger_chan,
                                                                    devtools_chan,
                                                                    supports_clipboard,
                                                                    webrender_api_sender.clone());

        // Send the constellation's swmanager sender to service worker manager thread
        script::init(sw_senders);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = IOCompositor::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan.clone(),
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
            webrender: webrender,
            webrender_api_sender: webrender_api_sender,
        });

        Browser {
            compositor: compositor,
            constellation_chan: constellation_chan,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }

    pub fn setup_logging(&self) {
        let constellation_chan = self.constellation_chan.clone();
        log::set_logger(|max_log_level| {
            let env_logger = EnvLogger::new();
            let con_logger = FromCompositorLogger::new(constellation_chan);
            let filter = max(env_logger.filter(), con_logger.filter());
            let logger = BothLogger(env_logger, con_logger);
            max_log_level.set(filter);
            Box::new(logger)
        }).expect("Failed to set logger.")
    }
}

fn create_constellation(user_agent: Cow<'static, str>,
                        config_dir: Option<String>,
                        url: Option<Url>,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        debugger_chan: Option<debugger::Sender>,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool,
                        webrender_api_sender: webrender_traits::RenderApiSender)
                        -> (Sender<ConstellationMsg>, SWManagerSenders) {
    let bluetooth_thread: IpcSender<BluetoothRequest> = BluetoothThreadFactory::new();

    let (public_resource_threads, private_resource_threads) =
        new_resource_threads(user_agent,
                             devtools_chan.clone(),
                             time_profiler_chan.clone(),
                             config_dir.map(Into::into));
    let image_cache_thread = new_image_cache_thread(public_resource_threads.sender(),
                                                    webrender_api_sender.create_api());
    let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
                                                 Some(webrender_api_sender.create_api()));

    let resource_sender = public_resource_threads.sender();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        debugger_chan: debugger_chan,
        devtools_chan: devtools_chan,
        bluetooth_thread: bluetooth_thread,
        image_cache_thread: image_cache_thread,
        font_cache_thread: font_cache_thread,
        public_resource_threads: public_resource_threads,
        private_resource_threads: private_resource_threads,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
        webrender_api_sender: webrender_api_sender,
    };
    let (constellation_chan, from_swmanager_sender) =
        Constellation::<script_layout_interface::message::Msg,
                        layout_thread::LayoutThread,
                        script::script_thread::ScriptThread>::start(initial_state);

    if let Some(url) = url {
        constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
    };

    // channels to communicate with Service Worker Manager
    let sw_senders = SWManagerSenders {
        swmanager_sender: from_swmanager_sender,
        resource_sender: resource_sender
    };

    (constellation_chan, sw_senders)
}

// A logger that logs to two downstream loggers.
// This should probably be in the log crate.
struct BothLogger<Log1, Log2>(Log1, Log2);

impl<Log1, Log2> Log for BothLogger<Log1, Log2> where Log1: Log, Log2: Log {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        self.0.enabled(metadata) || self.1.enabled(metadata)
    }

    fn log(&self, record: &LogRecord) {
        self.0.log(record);
        self.1.log(record);
    }
}

pub fn set_logger(constellation_chan: IpcSender<ScriptMsg>) {
    log::set_logger(|max_log_level| {
        let env_logger = EnvLogger::new();
        let con_logger = FromScriptLogger::new(constellation_chan);
        let filter = max(env_logger.filter(), con_logger.filter());
        let logger = BothLogger(env_logger, con_logger);
        max_log_level.set(filter);
        Box::new(logger)
    }).expect("Failed to set logger.")
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());
    PREFS.extend(unprivileged_content.prefs());
    set_logger(unprivileged_content.constellation_chan());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
       create_sandbox();
    }

    // send the required channels to the service worker manager
    let sw_senders = unprivileged_content.swmanager_senders();
    script::init(sw_senders);

    unprivileged_content.start_all::<script_layout_interface::message::Msg,
                                     layout_thread::LayoutThread,
                                     script::script_thread::ScriptThread>(true);
}

// This is a workaround for https://github.com/rust-lang/rust/pull/30175 until
// https://github.com/lfairy/rust-errno/pull/5 lands, and should be removed once
// we update Servo with the rust-errno crate.
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern fn __errno_location() -> *mut i32 {
    extern { fn __errno() -> *mut i32; }
    __errno()
}

#[cfg(not(target_os = "windows"))]
fn create_sandbox() {
    ChildSandbox::new(content_process_sandbox_profile()).activate()
        .expect("Failed to activate sandbox!");
}

#[cfg(target_os = "windows")]
fn create_sandbox() {
    panic!("Sandboxing is not supported on Windows.");
}
<|endoftext|>


<commit_before>/* 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/. */

/// Servo, the mighty web browser engine from the future.
///
/// This is a very simple library that wires all of Servo's components
/// together as type `Browser`, along with a generic client
/// implementing the `WindowMethods` trait, to create a working web
/// browser.
///
/// The `Browser` type is responsible for configuring a
/// `Constellation`, which does the heavy lifting of coordinating all
/// of Servo's internal subsystems, including the `ScriptTask` and the
/// `LayoutTask`, as well maintains the navigation context.
///
/// The `Browser` is fed events from a generic type that implements the
/// `WindowMethods` trait.

extern crate gaol;
extern crate libc;
#[macro_use]
extern crate util as _util;

mod export {
    extern crate canvas;
    extern crate canvas_traits;
    extern crate compositing;
    extern crate devtools;
    extern crate devtools_traits;
    extern crate euclid;
    extern crate gfx;
    extern crate gleam;
    extern crate ipc_channel;
    extern crate layers;
    extern crate layout;
    extern crate msg;
    extern crate net;
    extern crate net_traits;
    extern crate profile;
    extern crate profile_traits;
    extern crate script;
    extern crate script_traits;
    extern crate style;
    extern crate url;
}

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use compositing::CompositorEventListener;
use compositing::compositor_task::InitialCompositorState;
use compositing::constellation::InitialConstellationState;
use compositing::pipeline::UnprivilegedPipelineContent;
use compositing::sandboxing;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use compositing::{CompositorProxy, CompositorTask, Constellation};
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_task::FontCacheTask;
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::CompositorMsg as ConstellationMsg;
use net::image_cache_task::new_image_cache_task;
use net::resource_task::new_resource_task;
use net::storage_task::StorageTaskFactory;
use net_traits::storage_task::StorageTask;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use std::borrow::Borrow;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use util::opts;

pub use _util as util;
pub use export::canvas;
pub use export::canvas_traits;
pub use export::compositing;
pub use export::devtools;
pub use export::devtools_traits;
pub use export::euclid;
pub use export::gfx;
pub use export::gleam::gl;
pub use export::ipc_channel;
pub use export::layers;
pub use export::layout;
pub use export::msg;
pub use export::net;
pub use export::net_traits;
pub use export::profile;
pub use export::profile_traits;
pub use export::script;
pub use export::script_traits;
pub use export::style;
pub use export::url;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.

pub struct Browser {
    compositor: Box<CompositorEventListener + 'static>,
}

impl Browser {
    pub fn new<Window>(window: Option<Rc<Window>>) -> Browser
                       where Window: WindowMethods + 'static {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        script::init();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            WindowMethods::create_compositor_channel(&window);
        let supports_clipboard = match window {
            Some(ref win_rc) => {
                let win: &Window = win_rc.borrow();
                win.supports_clipboard()
            }
            None => false
        };
        let time_profiler_chan = profile_time::Profiler::create(opts.time_profiler_period);
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let constellation_chan = create_constellation(opts.clone(),
                                                      compositor_proxy.clone_compositor_proxy(),
                                                      time_profiler_chan.clone(),
                                                      mem_profiler_chan.clone(),
                                                      devtools_chan,
                                                      supports_clipboard);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = CompositorTask::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
        });

        Browser {
            compositor: compositor,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }
}

fn create_constellation(opts: opts::Opts,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool) -> Sender<ConstellationMsg> {
    let resource_task = new_resource_task(opts.user_agent.clone(), devtools_chan.clone());

    let image_cache_task = new_image_cache_task(resource_task.clone());
    let font_cache_task = FontCacheTask::new(resource_task.clone());
    let storage_task: StorageTask = StorageTaskFactory::new();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        devtools_chan: devtools_chan,
        image_cache_task: image_cache_task,
        font_cache_task: font_cache_task,
        resource_task: resource_task,
        storage_task: storage_task,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
    };
    let constellation_chan =
        Constellation::<layout::layout_task::LayoutTask,
                        script::script_task::ScriptTask>::start(initial_state);

    // Send the URL command to the constellation.
    match opts.url {
        Some(url) => {
            constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
        },
        None => ()
    };

    constellation_chan
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
        ChildSandbox::new(sandboxing::content_process_sandbox_profile()).activate().unwrap();
    }

    script::init();

    unprivileged_content.start_all::<layout::layout_task::LayoutTask,
                                     script::script_task::ScriptTask>(true);
}

<commit_msg>Removed space between doc comment and the actual code of pub struct Browser<commit_after>/* 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/. */

/// Servo, the mighty web browser engine from the future.
///
/// This is a very simple library that wires all of Servo's components
/// together as type `Browser`, along with a generic client
/// implementing the `WindowMethods` trait, to create a working web
/// browser.
///
/// The `Browser` type is responsible for configuring a
/// `Constellation`, which does the heavy lifting of coordinating all
/// of Servo's internal subsystems, including the `ScriptTask` and the
/// `LayoutTask`, as well maintains the navigation context.
///
/// The `Browser` is fed events from a generic type that implements the
/// `WindowMethods` trait.

extern crate gaol;
extern crate libc;
#[macro_use]
extern crate util as _util;

mod export {
    extern crate canvas;
    extern crate canvas_traits;
    extern crate compositing;
    extern crate devtools;
    extern crate devtools_traits;
    extern crate euclid;
    extern crate gfx;
    extern crate gleam;
    extern crate ipc_channel;
    extern crate layers;
    extern crate layout;
    extern crate msg;
    extern crate net;
    extern crate net_traits;
    extern crate profile;
    extern crate profile_traits;
    extern crate script;
    extern crate script_traits;
    extern crate style;
    extern crate url;
}

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use compositing::CompositorEventListener;
use compositing::compositor_task::InitialCompositorState;
use compositing::constellation::InitialConstellationState;
use compositing::pipeline::UnprivilegedPipelineContent;
use compositing::sandboxing;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use compositing::{CompositorProxy, CompositorTask, Constellation};
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_task::FontCacheTask;
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::CompositorMsg as ConstellationMsg;
use net::image_cache_task::new_image_cache_task;
use net::resource_task::new_resource_task;
use net::storage_task::StorageTaskFactory;
use net_traits::storage_task::StorageTask;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use std::borrow::Borrow;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use util::opts;

pub use _util as util;
pub use export::canvas;
pub use export::canvas_traits;
pub use export::compositing;
pub use export::devtools;
pub use export::devtools_traits;
pub use export::euclid;
pub use export::gfx;
pub use export::gleam::gl;
pub use export::ipc_channel;
pub use export::layers;
pub use export::layout;
pub use export::msg;
pub use export::net;
pub use export::net_traits;
pub use export::profile;
pub use export::profile_traits;
pub use export::script;
pub use export::script_traits;
pub use export::style;
pub use export::url;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Browser {
    compositor: Box<CompositorEventListener + 'static>,
}

impl Browser {
    pub fn new<Window>(window: Option<Rc<Window>>) -> Browser
                       where Window: WindowMethods + 'static {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        script::init();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            WindowMethods::create_compositor_channel(&window);
        let supports_clipboard = match window {
            Some(ref win_rc) => {
                let win: &Window = win_rc.borrow();
                win.supports_clipboard()
            }
            None => false
        };
        let time_profiler_chan = profile_time::Profiler::create(opts.time_profiler_period);
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let constellation_chan = create_constellation(opts.clone(),
                                                      compositor_proxy.clone_compositor_proxy(),
                                                      time_profiler_chan.clone(),
                                                      mem_profiler_chan.clone(),
                                                      devtools_chan,
                                                      supports_clipboard);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = CompositorTask::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
        });

        Browser {
            compositor: compositor,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }
}

fn create_constellation(opts: opts::Opts,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool) -> Sender<ConstellationMsg> {
    let resource_task = new_resource_task(opts.user_agent.clone(), devtools_chan.clone());

    let image_cache_task = new_image_cache_task(resource_task.clone());
    let font_cache_task = FontCacheTask::new(resource_task.clone());
    let storage_task: StorageTask = StorageTaskFactory::new();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        devtools_chan: devtools_chan,
        image_cache_task: image_cache_task,
        font_cache_task: font_cache_task,
        resource_task: resource_task,
        storage_task: storage_task,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
    };
    let constellation_chan =
        Constellation::<layout::layout_task::LayoutTask,
                        script::script_task::ScriptTask>::start(initial_state);

    // Send the URL command to the constellation.
    match opts.url {
        Some(url) => {
            constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
        },
        None => ()
    };

    constellation_chan
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
        ChildSandbox::new(sandboxing::content_process_sandbox_profile()).activate().unwrap();
    }

    script::init();

    unprivileged_content.start_all::<layout::layout_task::LayoutTask,
                                     script::script_task::ScriptTask>(true);
}

<|endoftext|>


<commit_before>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::setting;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

pub enum KeyKind {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl KeyKind {
    pub fn from<S: AsRef<str>>(key: S) -> Result<KeyKind, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(KeyKind::BurnAfter),
            "sweep.period" => Ok(KeyKind::SweepPeriod),
            "sweep.time"   => Ok(KeyKind::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            KeyKind::BurnAfter   => "burn.after",
            KeyKind::SweepPeriod => "sweep.period",
            KeyKind::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn insert_deeply(table: &mut BTreeMap<String, BTreeMap<String, String>>, key: &KeyKind, value: String) {
        let (first, second) = key.to_pair();

        let second = second.to_string();

        if table.contains_key(first) {
            table.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            table.insert(first.to_string(), entry);
        }
    }

    fn new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut config = BTreeMap::new();
        Self::insert_deeply(&mut config, &KeyKind::BurnAfter  , "2 weeks".to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepPeriod, "daily"  .to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepTime  , "00:00"  .to_string());

        Self::new(toml::encode(&config))
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(setting::config_file()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().find(|_| true));
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::new(toml))
    }

    pub fn set(mut self, key: &KeyKind, value: String) -> Result<Self, ConfigError> {
        let mut config = match toml::decode::<BTreeMap<String, BTreeMap<String, String>>>(self.toml) {
            Some(decoded) => decoded,
            None          => return Err(ConfigError::new(ConfigErrorKind::Something)),
        };

        Self::insert_deeply(&mut config, key, value);

        self.toml = toml::encode(&config);

        Ok(self)
    }

    pub fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    fn extract(key: &KeyKind) -> Result<String, CliError> {
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                KeyKind::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                KeyKind::SweepPeriod => unimplemented!(),
                KeyKind::SweepTime   => unimplemented!(),
            }));
        let value = try!(result).to_string(); // This to_string() is not documented.

        Ok(value)
    }

    pub fn validate<S: AsRef<str>>(key: &KeyKind, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key {
            KeyKind::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            KeyKind::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            KeyKind::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = KeyKind::BurnAfter;

        let after       = try!(Self::extract(&key));
        let after       = try!(Self::validate(&key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }
}
<commit_msg>[Refactoring] Rename Config::extract() => get()<commit_after>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::setting;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

pub enum KeyKind {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl KeyKind {
    pub fn from<S: AsRef<str>>(key: S) -> Result<KeyKind, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(KeyKind::BurnAfter),
            "sweep.period" => Ok(KeyKind::SweepPeriod),
            "sweep.time"   => Ok(KeyKind::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            KeyKind::BurnAfter   => "burn.after",
            KeyKind::SweepPeriod => "sweep.period",
            KeyKind::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn insert_deeply(table: &mut BTreeMap<String, BTreeMap<String, String>>, key: &KeyKind, value: String) {
        let (first, second) = key.to_pair();

        let second = second.to_string();

        if table.contains_key(first) {
            table.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            table.insert(first.to_string(), entry);
        }
    }

    fn new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut config = BTreeMap::new();
        Self::insert_deeply(&mut config, &KeyKind::BurnAfter  , "2 weeks".to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepPeriod, "daily"  .to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepTime  , "00:00"  .to_string());

        Self::new(toml::encode(&config))
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(setting::config_file()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().find(|_| true));
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::new(toml))
    }

    pub fn set(mut self, key: &KeyKind, value: String) -> Result<Self, ConfigError> {
        let mut config = match toml::decode::<BTreeMap<String, BTreeMap<String, String>>>(self.toml) {
            Some(decoded) => decoded,
            None          => return Err(ConfigError::new(ConfigErrorKind::Something)),
        };

        Self::insert_deeply(&mut config, key, value);

        self.toml = toml::encode(&config);

        Ok(self)
    }

    pub fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    fn get(key: &KeyKind) -> Result<String, CliError> {
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                KeyKind::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                KeyKind::SweepPeriod => unimplemented!(),
                KeyKind::SweepTime   => unimplemented!(),
            }));
        let value = try!(result).to_string(); // This to_string() is not documented.

        Ok(value)
    }

    pub fn validate<S: AsRef<str>>(key: &KeyKind, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key {
            KeyKind::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            KeyKind::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            KeyKind::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = KeyKind::BurnAfter;

        let after       = try!(Self::get(&key));
        let after       = try!(Self::validate(&key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }
}
<|endoftext|>


<commit_before>use super::{RegionObserver, RequestContext, ResponseContext};

struct ObserverEntry {
    priority: u32,
    observer: Box<RegionObserver>,
}

/// Registry contains all registered coprocessors.
#[derive(Default)]
pub struct Registry {
    observers: Vec<ObserverEntry>, // TODO: add endpoint
}

impl Registry {
    /// register an Observer to dispatcher.
    pub fn register_observer(&mut self, priority: u32, ro: Box<RegionObserver>) {
        let o = self.observers.binary_search_by(|t| t.priority.cmp(&priority));
        let r = ObserverEntry {
            priority: priority,
            observer: ro,
        };
        match o {
            Err(i) => self.observers.insert(i, r),
            Ok(n) => {
                for i in n + 1..self.observers.len() {
                    if self.observers[i].priority != priority {
                        self.observers.insert(i, r);
                        return;
                    }
                }
                self.observers.push(r);
            }
        }
    }
}

/// Admin and invoke all coprocessors.
#[derive(Default)]
pub struct CoprocessorHost {
    pub registry: Registry,
}

impl CoprocessorHost {
    // TODO load from configuration.

    pub fn new() -> CoprocessorHost {
        CoprocessorHost::default()
    }

    /// Call all prepose hook until bypass is set to true.
    pub fn pre_propose(&mut self, ctx: &mut RequestContext) {
        for entry in &mut self.registry.observers {
            entry.observer.pre_propose(ctx);
            if ctx.bypass {
                break;
            }
        }
    }

    /// call all apply hook until bypass is set to true.
    pub fn post_apply(&mut self, ctx: &mut ResponseContext) {
        for entry in &mut self.registry.observers {
            entry.observer.post_apply(ctx);
            if ctx.bypass {
                break;
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use raftserver::coprocessor::*;
    use tempdir::TempDir;
    use raftserver::store::engine::*;
    use raftserver::store::PeerStorage;
    use std::sync::*;

    use proto::metapb::Region;
    use proto::raft_cmdpb::{RaftCommandRequest, RaftCommandResponse};

    struct TestCoprocessor {
        bypass_pre: Arc<RwLock<bool>>,
        bypass_post: Arc<RwLock<bool>>,
        called_pre: Arc<RwLock<bool>>,
        called_post: Arc<RwLock<bool>>,
    }

    impl TestCoprocessor {
        fn new(bypass_pre: Arc<RwLock<bool>>,
               bypass_post: Arc<RwLock<bool>>,
               called_pre: Arc<RwLock<bool>>,
               called_post: Arc<RwLock<bool>>)
               -> TestCoprocessor {
            TestCoprocessor {
                bypass_post: bypass_post,
                bypass_pre: bypass_pre,
                called_post: called_post,
                called_pre: called_pre,
            }
        }
    }

    impl Coprocessor for TestCoprocessor {
        fn start(&mut self) {}
        fn stop(&mut self) {}
    }

    impl RegionObserver for TestCoprocessor {
        fn pre_propose(&mut self, ctx: &mut RequestContext) {
            *self.called_pre.write().unwrap() = true;
            ctx.bypass = *self.bypass_pre.read().unwrap();
        }

        fn post_apply(&mut self, ctx: &mut ResponseContext) {
            *self.called_post.write().unwrap() = true;
            ctx.bypass = *self.bypass_post.read().unwrap();
        }
    }

    fn new_peer_storage() -> PeerStorage {
        let path = TempDir::new("test-raftserver").unwrap();
        let engine = new_engine(path.path().to_str().unwrap()).unwrap();
        PeerStorage::new(Arc::new(engine), &Region::new()).unwrap()
    }

    fn new_request_context(ps: &PeerStorage) -> RequestContext {
        RequestContext {
            snap: RegionSnapshot::new(ps),
            req: RaftCommandRequest::new(),
            bypass: false,
        }
    }

    fn new_response_context(ps: &PeerStorage) -> ResponseContext {
        ResponseContext {
            snap: RegionSnapshot::new(ps),
            req: RaftCommandRequest::new(),
            res: RaftCommandResponse::new(),
            bypass: false,
        }
    }

    fn share_bool() -> Arc<RwLock<bool>> {
        Arc::new(RwLock::new(false))
    }

    fn assert_all(conditions: &[&Arc<RwLock<bool>>], expect: &[bool]) {
        for (c, e) in conditions.iter().zip(expect) {
            assert_eq!(*c.write().unwrap(), *e);
        }
    }

    fn set_all(conditions: &[&Arc<RwLock<bool>>], b: bool) {
        for c in conditions {
            *c.write().unwrap() = b;
        }
    }

    #[test]
    fn test_coprocessor_host() {
        // bypass_pre, bypass_post, called_pre, called_post
        let (bpr1, bpt1, cpr1, cpt1) = (share_bool(), share_bool(), share_bool(), share_bool());
        let observer1 = TestCoprocessor::new(bpr1.clone(),
                                             bpt1.clone(),
                                             cpr1.clone(),
                                             cpt1.clone());
        let mut host = CoprocessorHost::default();
        host.registry.register_observer(3, Box::new(observer1));
        let ps = new_peer_storage();
        let mut req = new_request_context(&ps);
        let mut res = new_response_context(&ps);

        assert!(!*cpr1.read().unwrap());
        host.pre_propose(&mut req);
        assert!(*cpr1.read().unwrap());
        assert!(!req.bypass);

        assert!(!*cpt1.read().unwrap());
        host.post_apply(&mut res);
        assert!(*cpt1.read().unwrap());
        assert!(!res.bypass);

        // reset
        set_all(&[&cpt1, &cpr1], false);

        let (bpr2, bpt2, cpr2, cpt2) = (share_bool(), share_bool(), share_bool(), share_bool());
        let observer2 = TestCoprocessor::new(bpr2.clone(),
                                             bpt2.clone(),
                                             cpr2.clone(),
                                             cpt2.clone());
        host.registry.register_observer(2, Box::new(observer2));

        set_all(&[&bpr2, &bpt2], true);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, false, false]);

        host.pre_propose(&mut req);
        host.post_apply(&mut res);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, true, true]);

        set_all(&[&bpr2, &bpt2], false);
        set_all(&[&cpr2, &cpt2], false);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, false, false]);

        host.pre_propose(&mut req);
        host.post_apply(&mut res);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[true, true, true, true]);
    }
}
<commit_msg>add startup and cleanup implementation<commit_after>use super::{RegionObserver, RequestContext, ResponseContext};

struct ObserverEntry {
    priority: u32,
    observer: Box<RegionObserver>,
}

/// Registry contains all registered coprocessors.
#[derive(Default)]
pub struct Registry {
    observers: Vec<ObserverEntry>, // TODO: add endpoint
}

impl Registry {
    /// register an Observer to dispatcher.
    pub fn register_observer(&mut self, priority: u32, mut ro: Box<RegionObserver>) {
        ro.start();
        let o = self.observers.binary_search_by(|t| t.priority.cmp(&priority));
        let r = ObserverEntry {
            priority: priority,
            observer: ro,
        };
        match o {
            Err(i) => self.observers.insert(i, r),
            Ok(n) => {
                for i in n + 1..self.observers.len() {
                    if self.observers[i].priority != priority {
                        self.observers.insert(i, r);
                        return;
                    }
                }
                self.observers.push(r);
            }
        }
    }
}

/// Admin and invoke all coprocessors.
#[derive(Default)]
pub struct CoprocessorHost {
    pub registry: Registry,
}

impl CoprocessorHost {
    // TODO load from configuration.

    pub fn new() -> CoprocessorHost {
        CoprocessorHost::default()
    }

    /// Call all prepose hook until bypass is set to true.
    pub fn pre_propose(&mut self, ctx: &mut RequestContext) {
        for entry in &mut self.registry.observers {
            entry.observer.pre_propose(ctx);
            if ctx.bypass {
                break;
            }
        }
    }

    /// call all apply hook until bypass is set to true.
    pub fn post_apply(&mut self, ctx: &mut ResponseContext) {
        for entry in &mut self.registry.observers {
            entry.observer.post_apply(ctx);
            if ctx.bypass {
                break;
            }
        }
    }

    pub fn shutdown(&mut self) {
        for entry in &mut self.registry.observers {
            entry.observer.stop();
        }
    }
}

impl Drop for CoprocessorHost {
    fn drop(&mut self) {
        self.shutdown();
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use raftserver::coprocessor::*;
    use tempdir::TempDir;
    use raftserver::store::engine::*;
    use raftserver::store::PeerStorage;
    use std::sync::*;

    use proto::metapb::Region;
    use proto::raft_cmdpb::{RaftCommandRequest, RaftCommandResponse};

    struct TestCoprocessor {
        bypass_pre: Arc<RwLock<bool>>,
        bypass_post: Arc<RwLock<bool>>,
        called_pre: Arc<RwLock<bool>>,
        called_post: Arc<RwLock<bool>>,
    }

    impl TestCoprocessor {
        fn new(bypass_pre: Arc<RwLock<bool>>,
               bypass_post: Arc<RwLock<bool>>,
               called_pre: Arc<RwLock<bool>>,
               called_post: Arc<RwLock<bool>>)
               -> TestCoprocessor {
            TestCoprocessor {
                bypass_post: bypass_post,
                bypass_pre: bypass_pre,
                called_post: called_post,
                called_pre: called_pre,
            }
        }
    }

    impl Coprocessor for TestCoprocessor {
        fn start(&mut self) {}
        fn stop(&mut self) {}
    }

    impl RegionObserver for TestCoprocessor {
        fn pre_propose(&mut self, ctx: &mut RequestContext) {
            *self.called_pre.write().unwrap() = true;
            ctx.bypass = *self.bypass_pre.read().unwrap();
        }

        fn post_apply(&mut self, ctx: &mut ResponseContext) {
            *self.called_post.write().unwrap() = true;
            ctx.bypass = *self.bypass_post.read().unwrap();
        }
    }

    fn new_peer_storage() -> PeerStorage {
        let path = TempDir::new("test-raftserver").unwrap();
        let engine = new_engine(path.path().to_str().unwrap()).unwrap();
        PeerStorage::new(Arc::new(engine), &Region::new()).unwrap()
    }

    fn new_request_context(ps: &PeerStorage) -> RequestContext {
        RequestContext {
            snap: RegionSnapshot::new(ps),
            req: RaftCommandRequest::new(),
            bypass: false,
        }
    }

    fn new_response_context(ps: &PeerStorage) -> ResponseContext {
        ResponseContext {
            snap: RegionSnapshot::new(ps),
            req: RaftCommandRequest::new(),
            res: RaftCommandResponse::new(),
            bypass: false,
        }
    }

    fn share_bool() -> Arc<RwLock<bool>> {
        Arc::new(RwLock::new(false))
    }

    fn assert_all(conditions: &[&Arc<RwLock<bool>>], expect: &[bool]) {
        for (c, e) in conditions.iter().zip(expect) {
            assert_eq!(*c.write().unwrap(), *e);
        }
    }

    fn set_all(conditions: &[&Arc<RwLock<bool>>], b: bool) {
        for c in conditions {
            *c.write().unwrap() = b;
        }
    }

    #[test]
    fn test_coprocessor_host() {
        // bypass_pre, bypass_post, called_pre, called_post
        let (bpr1, bpt1, cpr1, cpt1) = (share_bool(), share_bool(), share_bool(), share_bool());
        let observer1 = TestCoprocessor::new(bpr1.clone(),
                                             bpt1.clone(),
                                             cpr1.clone(),
                                             cpt1.clone());
        let mut host = CoprocessorHost::default();
        host.registry.register_observer(3, Box::new(observer1));
        let ps = new_peer_storage();
        let mut req = new_request_context(&ps);
        let mut res = new_response_context(&ps);

        assert!(!*cpr1.read().unwrap());
        host.pre_propose(&mut req);
        assert!(*cpr1.read().unwrap());
        assert!(!req.bypass);

        assert!(!*cpt1.read().unwrap());
        host.post_apply(&mut res);
        assert!(*cpt1.read().unwrap());
        assert!(!res.bypass);

        // reset
        set_all(&[&cpt1, &cpr1], false);

        let (bpr2, bpt2, cpr2, cpt2) = (share_bool(), share_bool(), share_bool(), share_bool());
        let observer2 = TestCoprocessor::new(bpr2.clone(),
                                             bpt2.clone(),
                                             cpr2.clone(),
                                             cpt2.clone());
        host.registry.register_observer(2, Box::new(observer2));

        set_all(&[&bpr2, &bpt2], true);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, false, false]);

        host.pre_propose(&mut req);
        host.post_apply(&mut res);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, true, true]);

        set_all(&[&bpr2, &bpt2], false);
        set_all(&[&cpr2, &cpt2], false);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[false, false, false, false]);

        host.pre_propose(&mut req);
        host.post_apply(&mut res);

        assert_all(&[&cpr1, &cpt1, &cpr2, &cpt2], &[true, true, true, true]);
    }
}
<|endoftext|>


<commit_before>fn main() {

    let v = vec![1,2,3];

    let v2 = v;
    //v2 now has ownership, we get an error if we access v;
    //println!("Error ! {:?}", v);
    //error: use of moved value: `v`
    //This is to prevent data race connditions

    //If v2 and v could still acess the underlying data
    //If we then modified v, e.g truncated it to length 2 this would make v2 Invalid
    //v2 would still think that the vector is of length 3

    //This also is applied to functions,
    //Arguments to function take ownernship

    //----------------------------
    //Copy types

    let vv = 1;
    let va = vv;

    println!("vv is {}", vv);

    //Because i32 (As do all primitive types) implement the copy function, the data is moved,
    //not just the point, like the above examples with Vectors
    //therefore there are no issues with race conditions and we are free to use the original var

    //---------------------------
    //Borrowing
    //Pass reference of the variable to your function
    //The functionn then "borrows" ownership
    //The reference is then immutable in the funcntion

    bar(&v2);
    println!("v after bar {:?}", v2);

    //&mut reference
    //&mut T allows mutation of the reference

    let mut x = 5;
    {
        let y = &mut x;
        *y += 1;
    }

    println!("{}",x);

    //If we remove the scope brackets, it fails
    //Here are the following rules for borrinw in Rust
    //1. Any borrow must last for a scope no great than that of it's owner
    //2. Either
    //   a. One of more refrence (&T) to a resource
    //   b. Exactly one mutable reference (&mut T)

    //This is a similar definition to a data race, two pointers accessing memory at the same time
    //One of the pointers is writing and not sychronized

    //Thinking in scopes

    let mut base = 5;
    let y = &mut base;

    *y += 1;
    //println!(" base: {}", base);
    //Accessing base here returns an error, as y can mutate base
    //We need to end the scope of y, before we can access base

    let mut base2 = 5;
    {
        let yy = &mut base2;
        *yy += 1;

    }

    println!("base2 : {}", base2);

}

fn foo(v: &Vec<i32>){
    //v.push(5); We can't do this, v is immutable
}

fn bar(v: &Vec<i32>){
    println!("v in bar {:?}", v)
}

fn take(v: Vec<i32>){

}
<commit_msg>Added lifetime param argument<commit_after>fn main() {

    let v = vec![1,2,3];

    let v2 = v;
    //v2 now has ownership, we get an error if we access v;
    //println!("Error ! {:?}", v);
    //error: use of moved value: `v`
    //This is to prevent data race connditions

    //If v2 and v could still acess the underlying data
    //If we then modified v, e.g truncated it to length 2 this would make v2 Invalid
    //v2 would still think that the vector is of length 3

    //This also is applied to functions,
    //Arguments to function take ownernship

    //----------------------------
    //Copy types

    let vv = 1;
    let va = vv;

    println!("vv is {}", vv);

    //Because i32 (As do all primitive types) implement the copy function, the data is moved,
    //not just the point, like the above examples with Vectors
    //therefore there are no issues with race conditions and we are free to use the original var

    //---------------------------
    //Borrowing
    //Pass reference of the variable to your function
    //The functionn then "borrows" ownership
    //The reference is then immutable in the funcntion

    bar(&v2);
    println!("v after bar {:?}", v2);

    //&mut reference
    //&mut T allows mutation of the reference

    let mut x = 5;
    {
        let y = &mut x;
        *y += 1;
    }

    println!("{}",x);

    //If we remove the scope brackets, it fails
    //Here are the following rules for borrinw in Rust
    //1. Any borrow must last for a scope no great than that of it's owner
    //2. Either
    //   a. One of more refrence (&T) to a resource
    //   b. Exactly one mutable reference (&mut T)

    //This is a similar definition to a data race, two pointers accessing memory at the same time
    //One of the pointers is writing and not sychronized

    //Thinking in scopes

    let mut base = 5;
    let y = &mut base;

    *y += 1;
    //println!(" base: {}", base);
    //Accessing base here returns an error, as y can mutate base
    //We need to end the scope of y, before we can access base

    let mut base2 = 5;
    {
        let yy = &mut base2;
        *yy += 1;

    }

    println!("base2 : {}", base2);

    //References
    //Use after free
    //If we try and access a variable declared in a score, we get an error
    //println!("{}", yy);

    let yy : &i32;
    let x = 5;
    //yy = &x;
    //println!("{}",yy);

    //If we decalare the reference, before the variable it refers to we get the same
    //error as above
    //This is because resources in the same scope are freed in the opposite order that they
    //were declared

    //---------------------------
    //Lifetimes
    //Dangling pointer issue when borrowed resouce is freed

    /*

    let r;
    {
        let i = 1;
        r = &i;
    }

    println!("{}",r);

    */

    //E.g r is still pointing to i, out of i's scope. Rust reports this error

    //Sometimes the compiler can't deduce if a reference will be valid outside a block

    let line = "lang:en=Hello World!";
    let lang = "en";

    let res;
    {
        let p = format!("lang:{}=",lang);
        res = skip_prefex(line, p.as_str());
    }
    println!("{}",res);

    //If skip_prefex block above would be invalid if skip_prefex didnn't have the Lifetime
    //params. Else the compiler would not know if the reference returned is still living when
    //calling println at the end of the block

}

fn skip_prefex<'a,'b>(line: &'a str, prefix: &'b str) -> &'a str {
    line
}

fn foo(v: &Vec<i32>){
    //v.push(5); We can't do this, v is immutable
}

fn bar(v: &Vec<i32>){
    println!("v in bar {:?}", v)
}

fn take(v: Vec<i32>){

}
<|endoftext|>