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

use dom::bindings::codegen::Bindings::DOMExceptionBinding;
use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionConstants;
use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods;
use dom::bindings::error;
use dom::bindings::error::Error;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use servo_util::str::DOMString;

#[repr(uint)]
#[deriving(Show)]
#[jstraceable]
pub enum DOMErrorName {
    IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR as uint,
    HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR as uint,
    WrongDocumentError = DOMExceptionConstants::WRONG_DOCUMENT_ERR as uint,
    InvalidCharacterError = DOMExceptionConstants::INVALID_CHARACTER_ERR as uint,
    NoModificationAllowedError = DOMExceptionConstants::NO_MODIFICATION_ALLOWED_ERR as uint,
    NotFoundError = DOMExceptionConstants::NOT_FOUND_ERR as uint,
    NotSupportedError = DOMExceptionConstants::NOT_SUPPORTED_ERR as uint,
    InvalidStateError = DOMExceptionConstants::INVALID_STATE_ERR as uint,
    SyntaxError = DOMExceptionConstants::SYNTAX_ERR as uint,
    InvalidModificationError = DOMExceptionConstants::INVALID_MODIFICATION_ERR as uint,
    NamespaceError = DOMExceptionConstants::NAMESPACE_ERR as uint,
    InvalidAccessError = DOMExceptionConstants::INVALID_ACCESS_ERR as uint,
    SecurityError = DOMExceptionConstants::SECURITY_ERR as uint,
    NetworkError = DOMExceptionConstants::NETWORK_ERR as uint,
    AbortError = DOMExceptionConstants::ABORT_ERR as uint,
    URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR as uint,
    QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR as uint,
    TimeoutError = DOMExceptionConstants::TIMEOUT_ERR as uint,
    InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR as uint,
    DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR as uint,
    EncodingError
}

impl DOMErrorName {
    fn from_error(error: Error) -> DOMErrorName {
        match error {
            error::IndexSize => IndexSizeError,
            error::NotFound => NotFoundError,
            error::HierarchyRequest => HierarchyRequestError,
            error::InvalidCharacter => InvalidCharacterError,
            error::NotSupported => NotSupportedError,
            error::InvalidState => InvalidStateError,
            error::Syntax => SyntaxError,
            error::NamespaceError => NamespaceError,
            error::InvalidAccess => InvalidAccessError,
            error::Security => SecurityError,
            error::Network => NetworkError,
            error::Abort => AbortError,
            error::Timeout => TimeoutError,
            error::DataClone => DataCloneError,
            error::FailureUnknown => panic!(),
        }
    }
}

#[dom_struct]
pub struct DOMException {
    code: DOMErrorName,
    reflector_: Reflector
}

impl DOMException {
    fn new_inherited(code: DOMErrorName) -> DOMException {
        DOMException {
            code: code,
            reflector_: Reflector::new()
        }
    }

    pub fn new(global: &GlobalRef, code: DOMErrorName) -> Temporary<DOMException> {
        reflect_dom_object(box DOMException::new_inherited(code), global, DOMExceptionBinding::Wrap)
    }

    pub fn new_from_error(global: &GlobalRef, code: Error) -> Temporary<DOMException> {
        DOMException::new(global, DOMErrorName::from_error(code))
    }
}

impl Reflectable for DOMException {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl<'a> DOMExceptionMethods for JSRef<'a, DOMException> {
    // http://dom.spec.whatwg.org/#dom-domexception-code
    fn Code(self) -> u16 {
        match self.code {
            // http://dom.spec.whatwg.org/#concept-throw
            EncodingError => 0,
            code => code as u16
        }
    }

    // http://dom.spec.whatwg.org/#error-names-0
    fn Name(self) -> DOMString {
        self.code.to_string()
    }

    // http://dom.spec.whatwg.org/#error-names-0
    fn Message(self) -> DOMString {
        match self.code {
            IndexSizeError => "The index is not in the allowed range.".to_string(),
            HierarchyRequestError => "The operation would yield an incorrect node tree.".to_string(),
            WrongDocumentError => "The object is in the wrong document.".to_string(),
            InvalidCharacterError => "The string contains invalid characters.".to_string(),
            NoModificationAllowedError => "The object can not be modified.".to_string(),
            NotFoundError => "The object can not be found here.".to_string(),
            NotSupportedError => "The operation is not supported.".to_string(),
            InvalidStateError => "The object is in an invalid state.".to_string(),
            SyntaxError => "The string did not match the expected pattern.".to_string(),
            InvalidModificationError => "The object can not be modified in this way.".to_string(),
            NamespaceError => "The operation is not allowed by Namespaces in XML.".to_string(),
            InvalidAccessError => "The object does not support the operation or argument.".to_string(),
            SecurityError => "The operation is insecure.".to_string(),
            NetworkError => "A network error occurred.".to_string(),
            AbortError => "The operation was aborted.".to_string(),
            URLMismatchError => "The given URL does not match another URL.".to_string(),
            QuotaExceededError => "The quota has been exceeded.".to_string(),
            TimeoutError => "The operation timed out.".to_string(),
            InvalidNodeTypeError => "The supplied node is incorrect or has an incorrect ancestor for this operation.".to_string(),
            DataCloneError => "The object can not be cloned.".to_string(),
            EncodingError => "The encoding operation (either encoded or decoding) failed.".to_string()
        }
    }
}
<commit_msg>auto merge of #4132 : jtuchsen/servo/issue-4131, r=jdm<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/. */

use dom::bindings::codegen::Bindings::DOMExceptionBinding;
use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionConstants;
use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods;
use dom::bindings::error;
use dom::bindings::error::Error;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use servo_util::str::DOMString;

#[repr(uint)]
#[deriving(Show)]
#[jstraceable]
pub enum DOMErrorName {
    IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR as uint,
    HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR as uint,
    WrongDocumentError = DOMExceptionConstants::WRONG_DOCUMENT_ERR as uint,
    InvalidCharacterError = DOMExceptionConstants::INVALID_CHARACTER_ERR as uint,
    NoModificationAllowedError = DOMExceptionConstants::NO_MODIFICATION_ALLOWED_ERR as uint,
    NotFoundError = DOMExceptionConstants::NOT_FOUND_ERR as uint,
    NotSupportedError = DOMExceptionConstants::NOT_SUPPORTED_ERR as uint,
    InvalidStateError = DOMExceptionConstants::INVALID_STATE_ERR as uint,
    SyntaxError = DOMExceptionConstants::SYNTAX_ERR as uint,
    InvalidModificationError = DOMExceptionConstants::INVALID_MODIFICATION_ERR as uint,
    NamespaceError = DOMExceptionConstants::NAMESPACE_ERR as uint,
    InvalidAccessError = DOMExceptionConstants::INVALID_ACCESS_ERR as uint,
    SecurityError = DOMExceptionConstants::SECURITY_ERR as uint,
    NetworkError = DOMExceptionConstants::NETWORK_ERR as uint,
    AbortError = DOMExceptionConstants::ABORT_ERR as uint,
    URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR as uint,
    QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR as uint,
    TimeoutError = DOMExceptionConstants::TIMEOUT_ERR as uint,
    InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR as uint,
    DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR as uint,
    EncodingError
}

impl DOMErrorName {
    fn from_error(error: Error) -> DOMErrorName {
        match error {
            error::IndexSize => IndexSizeError,
            error::NotFound => NotFoundError,
            error::HierarchyRequest => HierarchyRequestError,
            error::InvalidCharacter => InvalidCharacterError,
            error::NotSupported => NotSupportedError,
            error::InvalidState => InvalidStateError,
            error::Syntax => SyntaxError,
            error::NamespaceError => NamespaceError,
            error::InvalidAccess => InvalidAccessError,
            error::Security => SecurityError,
            error::Network => NetworkError,
            error::Abort => AbortError,
            error::Timeout => TimeoutError,
            error::DataClone => DataCloneError,
            error::FailureUnknown => panic!(),
        }
    }
}

#[dom_struct]
pub struct DOMException {
    code: DOMErrorName,
    reflector_: Reflector
}

impl DOMException {
    fn new_inherited(code: DOMErrorName) -> DOMException {
        DOMException {
            code: code,
            reflector_: Reflector::new()
        }
    }

    pub fn new(global: &GlobalRef, code: DOMErrorName) -> Temporary<DOMException> {
        reflect_dom_object(box DOMException::new_inherited(code), global, DOMExceptionBinding::Wrap)
    }

    pub fn new_from_error(global: &GlobalRef, code: Error) -> Temporary<DOMException> {
        DOMException::new(global, DOMErrorName::from_error(code))
    }
}

impl Reflectable for DOMException {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl<'a> DOMExceptionMethods for JSRef<'a, DOMException> {
    // http://dom.spec.whatwg.org/#dom-domexception-code
    fn Code(self) -> u16 {
        match self.code {
            // http://dom.spec.whatwg.org/#concept-throw
            EncodingError => 0,
            code => code as u16
        }
    }

    // http://dom.spec.whatwg.org/#error-names-0
    fn Name(self) -> DOMString {
        self.code.to_string()
    }

    // http://dom.spec.whatwg.org/#error-names-0
    fn Message(self) -> DOMString {
        let message = match self.code {
            IndexSizeError => "The index is not in the allowed range.",
            HierarchyRequestError => "The operation would yield an incorrect node tree.",
            WrongDocumentError => "The object is in the wrong document.",
            InvalidCharacterError => "The string contains invalid characters.",
            NoModificationAllowedError => "The object can not be modified.",
            NotFoundError => "The object can not be found here.",
            NotSupportedError => "The operation is not supported.",
            InvalidStateError => "The object is in an invalid state.",
            SyntaxError => "The string did not match the expected pattern.",
            InvalidModificationError => "The object can not be modified in this way.",
            NamespaceError => "The operation is not allowed by Namespaces in XML.",
            InvalidAccessError => "The object does not support the operation or argument.",
            SecurityError => "The operation is insecure.",
            NetworkError => "A network error occurred.",
            AbortError => "The operation was aborted.",
            URLMismatchError => "The given URL does not match another URL.",
            QuotaExceededError => "The quota has been exceeded.",
            TimeoutError => "The operation timed out.",
            InvalidNodeTypeError => "The supplied node is incorrect or has an incorrect ancestor for this operation.",
            DataCloneError => "The object can not be cloned.",
            EncodingError => "The encoding operation (either encoded or decoding) failed."
        };

        message.to_string()
    }
}
<|endoftext|>


<commit_before>use address::Address;
use messages::outgoing;
use mio;
use node;
use routing_table::{InsertOutcome, InsertionResult, RoutingTable};
use std::collections::HashMap;
use std::io::Cursor;
use std::net::{UdpSocket, ToSocketAddrs, SocketAddr};
use std::sync::mpsc;
use std::thread;
use stun;
use transaction::{TransactionId, TransactionIdGenerator};

#[derive(Debug)]
pub enum Event {
    ReceivedPacket(Address, Vec<u8>),
    Shutdown
}

pub enum OneshotTask {
    Incoming(Vec<u8>),
    StartBootstrap,
    SendPacket(Address, Vec<u8>),
    Shutdown
}

pub enum ScheduledTask {
    ContinueBootstrap,
    ContinueHealthCheck,
    ContinueRefresh
}

enum TableAction {
    Bootstrap(mio::Timeout),
    HealthCheck(mio::Timeout),
    RefreshBucket(mio::Timeout)
}

pub type TaskSender = mio::Sender<OneshotTask>;

enum Status {
    Bootstrapping,
    Idle
}

pub struct Network {
    host: SocketAddr,
    routing_table: RoutingTable,
    self_node: Box<node::Node + 'static>,
    transaction_ids: TransactionIdGenerator,
    status: Status,
    pending_actions: HashMap<TransactionId, TableAction>,
    event_listeners: Vec<mpsc::Sender<Event>>,
    shutdown_signals: Vec<mpsc::Sender<mpsc::Sender<()>>>
}

impl Network {
    pub fn new<T: ToSocketAddrs>(self_address: Address, host: T, routers: Vec<Box<node::Node>>) -> Network {
        let host = host.to_socket_addrs().unwrap().next().unwrap();
        let mapped_host = stun::get_mapped_address(host).unwrap();
        let self_node = node::UdpNode::new(self_address, mapped_host);
        let routing_table = RoutingTable::new(8, self_address, routers);

        Network {
            host: host,
            routing_table: routing_table,
            self_node: Box::new(self_node),
            transaction_ids: TransactionIdGenerator::new(),
            status: Status::Idle,
            pending_actions: HashMap::new(),
            event_listeners: vec![],
            shutdown_signals: vec![]
        }
    }

    pub fn run(mut self) -> TaskSender {
        let mut event_loop_config = mio::EventLoopConfig::new();
        event_loop_config.notify_capacity(16384);
        let mut event_loop = mio::EventLoop::configured(event_loop_config).unwrap();

        let shutdown_signal = create_incoming_udp_channel(self.host, event_loop.channel());
        self.shutdown_signals.push(shutdown_signal);

        event_loop.channel().send(OneshotTask::StartBootstrap).unwrap();
        info!("Running server at {:?}", self.self_node);
        let mut handler = Handler::new(self);
        let task_sender = event_loop.channel();
        thread::spawn(move|| event_loop.run(&mut handler).unwrap());
        task_sender
    }

    pub fn register_event_listener(&mut self, event_listener: mpsc::Sender<Event>) {
        self.event_listeners.push(event_listener);
    }

    fn handle_incoming(&mut self, data: Vec<u8>, event_loop: &mut mio::EventLoop<Handler>) {
        use messages::incoming::{Message, Query, Response, self};
        let mut data = Cursor::new(data);
        let message = incoming::parse_from_reader(&mut data).unwrap();

        match message {
            Message::Query(transaction_id, origin, query) => {
                let origin_address = origin.address();

                match query {
                    Query::FindNode(target) => {
                        let response: Vec<u8> = outgoing::create_find_node_response(
                                transaction_id,
                                &self.self_node,
                                self.routing_table.nearest_live_nodes_to(&target, false));
                        origin.send(response);
                    },
                    Query::Packet(payload) => {
                        for listener in &self.event_listeners {
                            listener.send(Event::ReceivedPacket(origin_address, payload.clone())).unwrap();
                        }
                        let response = outgoing::create_packet_response(
                            transaction_id, &self.self_node);
                        origin.send(response);
                    },
                    Query::Ping => {
                        let response = outgoing::create_ping_response(
                            transaction_id,
                            &self.self_node);
                        origin.send(response);
                    }
                }

                // Always insert the origin node
                self.insert_node(origin).unwrap();
                if let Some(origin) = self.routing_table.find_node(&origin_address) {
                    origin.received_query(transaction_id);
                }
            }

            Message::Response(transaction_id, origin, response) => {
                let origin_address = origin.address();
                // Always insert the origin node
                let mut encounted_new_node = self.insert_node(origin) == Ok(InsertOutcome::Inserted);

                match response {
                    Response::FindNode(mut nodes) => {
                        for node in nodes.drain(..) {
                            match self.insert_node(node) {
                                Ok(InsertOutcome::Inserted) => {
                                    encounted_new_node = true;
                                }
                                Err(error) => { panic!(error) }
                                _ => { }
                            }
                        }

                        {
                            if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                                origin.received_response(transaction_id);
                            }
                        }

                        match self.pending_actions.remove(&transaction_id) {
                            Some(TableAction::Bootstrap(timeout)) => {
                                event_loop.clear_timeout(timeout);
                                if encounted_new_node {
                                    self.continue_bootstrap(event_loop);
                                } else {
                                    self.continue_health_check(event_loop);
                                    self.continue_refresh(event_loop)
                                }
                            }
                            Some(TableAction::HealthCheck(_)) => {
                                // Does not clear timeout. Lets Healthcheck proceed at regular
                                // interval.
                            },
                            Some(TableAction::RefreshBucket(_)) => {
                            }
                            None => { }
                        }

                        match self.status {
                            Status::Bootstrapping => {
                                self.status = Status::Idle;
                            }
                            _ => { }
                        }
                    }
                    Response::Packet => {
                        if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_response(transaction_id);
                        }
                    }
                    Response::Ping => {
                        if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_response(transaction_id);
                        }
                    }
                }
            }
        }
    }

    fn start_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        self.status = Status::Bootstrapping;
        self.continue_bootstrap(event_loop);
    }

    fn continue_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let address = &self.self_node.address();
        let transaction_id = self.find_node(address);
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueBootstrap, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::Bootstrap(timeout));
    }

    fn continue_health_check(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let transaction_id = self.health_check();
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueHealthCheck, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::HealthCheck(timeout));
    }

    fn continue_refresh(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueRefresh, 1000).unwrap();

        match self.refresh_bucket() {
            Some(transaction_id) => {
                self.pending_actions.insert(transaction_id, TableAction::RefreshBucket(timeout));
            }
            None => { }
        }
    }

    fn find_node(&mut self, address: &Address) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        let query = outgoing::create_find_node_query(
            transaction_id,
            &self.self_node,
            address);
        for node in self.routing_table.nearest() {
            node.sent_query(transaction_id);
            node.send(query.clone());
        }
        transaction_id
    }

    fn health_check(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();

        // TODO: this should be a separate keep-alive task, but it will be
        // dependant on the type of connection we're keeping alive.
        //
        // Ping nearest node every time.
        if let Some(nearest_node) = self.routing_table.nearest().first_mut() {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            nearest_node.sent_query(transaction_id);
            nearest_node.send(query);
        }

        for node in self.routing_table.questionable_nodes().iter_mut() {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            node.sent_query(transaction_id);
            node.send(query);
        }
        transaction_id
    }

    fn refresh_bucket(&mut self) -> Option<TransactionId> {
        if let Some(address) = self.address_to_find_for_refresh() {
            Some(self.find_node(&address))
        } else {
            None
        }
    }

    fn address_to_find_for_refresh(&self) -> Option<Address> {
        match self.routing_table.bucket_needing_refresh() {
            Some(bucket) => {
                Some(bucket.random_address_in_space())
            }
            None => None
        }
    }

    fn insert_node(&mut self, node: Box<node::Node>) -> InsertionResult {
        self.routing_table.insert(node, &self.self_node, &mut self.transaction_ids)
    }

    fn send_packet(&mut self, recipient: Address, payload: Vec<u8>, _event_loop: &mut mio::EventLoop<Handler>) {
        for node in self.routing_table.nearest_live_nodes_to(&recipient, false) {
            let transaction_id = self.transaction_ids.generate();
            let query = outgoing::create_packet_query(
                transaction_id, &self.self_node, payload.clone());
            node.sent_query(transaction_id);
            node.send(query.clone());
        }
    }

    fn shutdown(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let sentinels: Vec<mpsc::Receiver<()>> = self.shutdown_signals.iter().map(|signal| {
            let (sentinal_sender, sentinal_receiver) = mpsc::channel();
            signal.send(sentinal_sender).expect("couldn't send shutdown signal");
            sentinal_receiver
        }).collect();
        for sentinel in sentinels {
            sentinel.recv().unwrap();
        }
        event_loop.shutdown();
        for listener in &self.event_listeners {
            listener.send(Event::Shutdown).unwrap();
        }
    }
}

struct Handler {
    network: Network
}

impl Handler {
    fn new(network: Network) -> Handler {
        Handler {
            network: network
        }
    }
}

impl mio::Handler for Handler {
    type Timeout = ScheduledTask;
    type Message = OneshotTask;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Handler>, task: OneshotTask) {
        match task {
            OneshotTask::Incoming(data) => self.network.handle_incoming(data, event_loop),
            OneshotTask::StartBootstrap => self.network.start_bootstrap(event_loop),
            OneshotTask::SendPacket(recipient, payload) =>
                self.network.send_packet(recipient, payload, event_loop),
            OneshotTask::Shutdown => self.network.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Handler>, timeout: ScheduledTask) {
        match timeout {
            ScheduledTask::ContinueBootstrap => self.network.continue_bootstrap(event_loop),
            ScheduledTask::ContinueHealthCheck => self.network.continue_health_check(event_loop),
            ScheduledTask::ContinueRefresh => self.network.continue_refresh(event_loop)
        }
    }
}

// TODO: I'd rather have some kind of select on two channels instead of this nested non-blocking
// recv. It only works because of the 10ms timeout on the socket.
fn create_incoming_udp_channel(host: SocketAddr, sender: TaskSender) -> mpsc::Sender<mpsc::Sender<()>> {
    use std::time::Duration;

    let (shutdown_sender, shutdown_receiver) = mpsc::channel::<mpsc::Sender<()>>();

    thread::spawn(move || {
        let host = ("0.0.0.0", host.port());
        let socket = UdpSocket::bind(host).unwrap();
        socket.set_read_timeout(Some(Duration::from_millis(10))).unwrap();

        loop {
            match shutdown_receiver.try_recv() {
                Err(mpsc::TryRecvError::Empty) => {
                    let mut buf = [0; 4096];
                    match socket.recv(&mut buf) {
                        Ok(size) => {
                            sender.send(OneshotTask::Incoming(buf[..size].iter().cloned().collect()))
                                .unwrap_or_else(|err| info!("Couldn't handling incoming: {:?}", err));
                        }
                        Err(_) => {
                            //warn!("Error receiving from server: {}", e)
                        }
                    }
                }

                Err(mpsc::TryRecvError::Disconnected) => {
                    // The other end of the shutdown_receiver was dropped
                    break
                }

                Ok(sentinel) => {
                    sentinel.send(()).unwrap();
                    break;
                }
            }
        }

    });
    shutdown_sender
}
<commit_msg>Note intent to change op/transaction coupling<commit_after>use address::Address;
use messages::outgoing;
use mio;
use node;
use routing_table::{InsertOutcome, InsertionResult, RoutingTable};
use std::collections::HashMap;
use std::io::Cursor;
use std::net::{UdpSocket, ToSocketAddrs, SocketAddr};
use std::sync::mpsc;
use std::thread;
use stun;
use transaction::{TransactionId, TransactionIdGenerator};

#[derive(Debug)]
pub enum Event {
    ReceivedPacket(Address, Vec<u8>),
    Shutdown
}

pub enum OneshotTask {
    Incoming(Vec<u8>),
    StartBootstrap,
    SendPacket(Address, Vec<u8>),
    Shutdown
}

pub enum ScheduledTask {
    ContinueBootstrap,
    ContinueHealthCheck,
    ContinueRefresh
}

enum TableAction {
    Bootstrap(mio::Timeout),
    HealthCheck(mio::Timeout),
    RefreshBucket(mio::Timeout)
}

pub type TaskSender = mio::Sender<OneshotTask>;

enum Status {
    Bootstrapping,
    Idle
}

pub struct Network {
    host: SocketAddr,
    routing_table: RoutingTable,
    self_node: Box<node::Node + 'static>,
    transaction_ids: TransactionIdGenerator,
    status: Status,
    pending_actions: HashMap<TransactionId, TableAction>,
    event_listeners: Vec<mpsc::Sender<Event>>,
    shutdown_signals: Vec<mpsc::Sender<mpsc::Sender<()>>>
}

impl Network {
    pub fn new<T: ToSocketAddrs>(self_address: Address, host: T, routers: Vec<Box<node::Node>>) -> Network {
        let host = host.to_socket_addrs().unwrap().next().unwrap();
        let mapped_host = stun::get_mapped_address(host).unwrap();
        let self_node = node::UdpNode::new(self_address, mapped_host);
        let routing_table = RoutingTable::new(8, self_address, routers);

        Network {
            host: host,
            routing_table: routing_table,
            self_node: Box::new(self_node),
            transaction_ids: TransactionIdGenerator::new(),
            status: Status::Idle,
            pending_actions: HashMap::new(),
            event_listeners: vec![],
            shutdown_signals: vec![]
        }
    }

    pub fn run(mut self) -> TaskSender {
        let mut event_loop_config = mio::EventLoopConfig::new();
        event_loop_config.notify_capacity(16384);
        let mut event_loop = mio::EventLoop::configured(event_loop_config).unwrap();

        let shutdown_signal = create_incoming_udp_channel(self.host, event_loop.channel());
        self.shutdown_signals.push(shutdown_signal);

        event_loop.channel().send(OneshotTask::StartBootstrap).unwrap();
        info!("Running server at {:?}", self.self_node);
        let mut handler = Handler::new(self);
        let task_sender = event_loop.channel();
        thread::spawn(move|| event_loop.run(&mut handler).unwrap());
        task_sender
    }

    pub fn register_event_listener(&mut self, event_listener: mpsc::Sender<Event>) {
        self.event_listeners.push(event_listener);
    }

    fn handle_incoming(&mut self, data: Vec<u8>, event_loop: &mut mio::EventLoop<Handler>) {
        use messages::incoming::{Message, Query, Response, self};
        let mut data = Cursor::new(data);
        let message = incoming::parse_from_reader(&mut data).unwrap();

        match message {
            Message::Query(transaction_id, origin, query) => {
                let origin_address = origin.address();

                match query {
                    Query::FindNode(target) => {
                        let response: Vec<u8> = outgoing::create_find_node_response(
                                transaction_id,
                                &self.self_node,
                                self.routing_table.nearest_live_nodes_to(&target, false));
                        origin.send(response);
                    },
                    Query::Packet(payload) => {
                        for listener in &self.event_listeners {
                            listener.send(Event::ReceivedPacket(origin_address, payload.clone())).unwrap();
                        }
                        let response = outgoing::create_packet_response(
                            transaction_id, &self.self_node);
                        origin.send(response);
                    },
                    Query::Ping => {
                        let response = outgoing::create_ping_response(
                            transaction_id,
                            &self.self_node);
                        origin.send(response);
                    }
                }

                // Always insert the origin node
                self.insert_node(origin).unwrap();
                if let Some(origin) = self.routing_table.find_node(&origin_address) {
                    origin.received_query(transaction_id);
                }
            }

            Message::Response(transaction_id, origin, response) => {
                let origin_address = origin.address();
                // Always insert the origin node
                let mut encounted_new_node = self.insert_node(origin) == Ok(InsertOutcome::Inserted);

                match response {
                    Response::FindNode(mut nodes) => {
                        for node in nodes.drain(..) {
                            match self.insert_node(node) {
                                Ok(InsertOutcome::Inserted) => {
                                    encounted_new_node = true;
                                }
                                Err(error) => { panic!(error) }
                                _ => { }
                            }
                        }

                        {
                            if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                                origin.received_response(transaction_id);
                            }
                        }

                        match self.pending_actions.remove(&transaction_id) {
                            Some(TableAction::Bootstrap(timeout)) => {
                                event_loop.clear_timeout(timeout);
                                if encounted_new_node {
                                    self.continue_bootstrap(event_loop);
                                } else {
                                    self.continue_health_check(event_loop);
                                    self.continue_refresh(event_loop)
                                }
                            }
                            Some(TableAction::HealthCheck(_)) => {
                                // Does not clear timeout. Lets Healthcheck proceed at regular
                                // interval.
                            },
                            Some(TableAction::RefreshBucket(_)) => {
                            }
                            None => { }
                        }

                        match self.status {
                            Status::Bootstrapping => {
                                self.status = Status::Idle;
                            }
                            _ => { }
                        }
                    }
                    Response::Packet => {
                        if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_response(transaction_id);
                        }
                    }
                    Response::Ping => {
                        if let Some(mut origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_response(transaction_id);
                        }
                    }
                }
            }
        }
    }

    fn start_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        self.status = Status::Bootstrapping;
        self.continue_bootstrap(event_loop);
    }

    fn continue_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let address = &self.self_node.address();
        let transaction_id = self.find_node(address);
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueBootstrap, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::Bootstrap(timeout));
    }

    fn continue_health_check(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let transaction_id = self.health_check();
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueHealthCheck, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::HealthCheck(timeout));
    }

    fn continue_refresh(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueRefresh, 1000).unwrap();

        match self.refresh_bucket() {
            Some(transaction_id) => {
                self.pending_actions.insert(transaction_id, TableAction::RefreshBucket(timeout));
            }
            None => { }
        }
    }

    fn find_node(&mut self, address: &Address) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        let query = outgoing::create_find_node_query(
            transaction_id,
            &self.self_node,
            address);
        for node in self.routing_table.nearest() {
            node.sent_query(transaction_id);
            node.send(query.clone());
        }
        transaction_id
    }

    // TODO: I should de-couple operations and transactions. Some operations, e.g. health_check
    // can be comprised of multiple transactions that must be completed before the operation is
    // complete. In this case, I should receive a ping from each questionable node before
    // health_check is done.
    fn health_check(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();

        // TODO: this should be a separate keep-alive task, but it will be
        // dependant on the type of connection we're keeping alive.
        //
        // Ping nearest node every time.
        if let Some(nearest_node) = self.routing_table.nearest().first_mut() {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            nearest_node.sent_query(transaction_id);
            nearest_node.send(query);
        }

        for node in self.routing_table.questionable_nodes().iter_mut() {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            node.sent_query(transaction_id);
            node.send(query);
        }
        transaction_id
    }

    fn refresh_bucket(&mut self) -> Option<TransactionId> {
        if let Some(address) = self.address_to_find_for_refresh() {
            Some(self.find_node(&address))
        } else {
            None
        }
    }

    fn address_to_find_for_refresh(&self) -> Option<Address> {
        match self.routing_table.bucket_needing_refresh() {
            Some(bucket) => {
                Some(bucket.random_address_in_space())
            }
            None => None
        }
    }

    fn insert_node(&mut self, node: Box<node::Node>) -> InsertionResult {
        self.routing_table.insert(node, &self.self_node, &mut self.transaction_ids)
    }

    fn send_packet(&mut self, recipient: Address, payload: Vec<u8>, _event_loop: &mut mio::EventLoop<Handler>) {
        for node in self.routing_table.nearest_live_nodes_to(&recipient, false) {
            let transaction_id = self.transaction_ids.generate();
            let query = outgoing::create_packet_query(
                transaction_id, &self.self_node, payload.clone());
            node.sent_query(transaction_id);
            node.send(query.clone());
        }
    }

    fn shutdown(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let sentinels: Vec<mpsc::Receiver<()>> = self.shutdown_signals.iter().map(|signal| {
            let (sentinal_sender, sentinal_receiver) = mpsc::channel();
            signal.send(sentinal_sender).expect("couldn't send shutdown signal");
            sentinal_receiver
        }).collect();
        for sentinel in sentinels {
            sentinel.recv().unwrap();
        }
        event_loop.shutdown();
        for listener in &self.event_listeners {
            listener.send(Event::Shutdown).unwrap();
        }
    }
}

struct Handler {
    network: Network
}

impl Handler {
    fn new(network: Network) -> Handler {
        Handler {
            network: network
        }
    }
}

impl mio::Handler for Handler {
    type Timeout = ScheduledTask;
    type Message = OneshotTask;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Handler>, task: OneshotTask) {
        match task {
            OneshotTask::Incoming(data) => self.network.handle_incoming(data, event_loop),
            OneshotTask::StartBootstrap => self.network.start_bootstrap(event_loop),
            OneshotTask::SendPacket(recipient, payload) =>
                self.network.send_packet(recipient, payload, event_loop),
            OneshotTask::Shutdown => self.network.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Handler>, timeout: ScheduledTask) {
        match timeout {
            ScheduledTask::ContinueBootstrap => self.network.continue_bootstrap(event_loop),
            ScheduledTask::ContinueHealthCheck => self.network.continue_health_check(event_loop),
            ScheduledTask::ContinueRefresh => self.network.continue_refresh(event_loop)
        }
    }
}

// TODO: I'd rather have some kind of select on two channels instead of this nested non-blocking
// recv. It only works because of the 10ms timeout on the socket.
fn create_incoming_udp_channel(host: SocketAddr, sender: TaskSender) -> mpsc::Sender<mpsc::Sender<()>> {
    use std::time::Duration;

    let (shutdown_sender, shutdown_receiver) = mpsc::channel::<mpsc::Sender<()>>();

    thread::spawn(move || {
        let host = ("0.0.0.0", host.port());
        let socket = UdpSocket::bind(host).unwrap();
        socket.set_read_timeout(Some(Duration::from_millis(10))).unwrap();

        loop {
            match shutdown_receiver.try_recv() {
                Err(mpsc::TryRecvError::Empty) => {
                    let mut buf = [0; 4096];
                    match socket.recv(&mut buf) {
                        Ok(size) => {
                            sender.send(OneshotTask::Incoming(buf[..size].iter().cloned().collect()))
                                .unwrap_or_else(|err| info!("Couldn't handling incoming: {:?}", err));
                        }
                        Err(_) => {
                            //warn!("Error receiving from server: {}", e)
                        }
                    }
                }

                Err(mpsc::TryRecvError::Disconnected) => {
                    // The other end of the shutdown_receiver was dropped
                    break
                }

                Ok(sentinel) => {
                    sentinel.send(()).unwrap();
                    break;
                }
            }
        }

    });
    shutdown_sender
}
<|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/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::{EventMethods};
use dom::bindings::codegen::Bindings::StorageEventBinding;
use dom::bindings::codegen::Bindings::StorageEventBinding::{StorageEventMethods};

use dom::bindings::codegen::InheritTypes::{EventCast};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference};
use dom::bindings::utils::{reflect_dom_object};
use dom::event::{Event, EventTypeId, EventBubbles, EventCancelable};
use dom::storage::Storage;
use util::str::DOMString;

#[dom_struct]
pub struct StorageEvent {
    event: Event,
    key: DOMRefCell<Option<DOMString>>,
    oldValue: DOMRefCell<Option<DOMString>>,
    newValue: DOMRefCell<Option<DOMString>>,
    url: DOMRefCell<DOMString>,
    storageArea: MutNullableHeap<JS<Storage>>
}

impl StorageEvent {
    pub fn new_inherited(type_id: EventTypeId,
                         key: Option<DOMString>,
                         oldValue: Option<DOMString>,
                         newValue: Option<DOMString>,
                         url: DOMString,
                         storageArea: Option<&Storage>) -> StorageEvent {
        StorageEvent {
            event: Event::new_inherited(type_id),
            key: DOMRefCell::new(key),
            oldValue: DOMRefCell::new(oldValue),
            newValue: DOMRefCell::new(newValue),
            url: DOMRefCell::new(url),
            storageArea: MutNullableHeap::new(storageArea.map(JS::from_ref))
        }
    }

    pub fn new(global: GlobalRef,
               type_: DOMString,
               bubbles: EventBubbles,
               cancelable: EventCancelable,
               key: Option<DOMString>,
               oldValue: Option<DOMString>,
               newValue: Option<DOMString>,
               url: DOMString,
               storageArea: Option<&Storage>) -> Root<StorageEvent> {
        let ev = reflect_dom_object(box StorageEvent::new_inherited(EventTypeId::StorageEvent,
                                                                    key, oldValue, newValue,
                                                                    url, storageArea),
                                    global,
                                    StorageEventBinding::Wrap);
        {
            let event = EventCast::from_ref(ev.r());
            event.InitEvent(type_, bubbles == EventBubbles::Bubbles, cancelable == EventCancelable::Cancelable);
        }
        ev
    }

    pub fn Constructor(global: GlobalRef,
                       type_: DOMString,
                       init: &StorageEventBinding::StorageEventInit) -> Fallible<Root<StorageEvent>> {
        let key = init.key.clone();
        let oldValue = init.oldValue.clone();
        let newValue = init.newValue.clone();
        let url = init.url.clone();
        let storageArea = init.storageArea.r();
        let bubbles = if init.parent.bubbles { EventBubbles::Bubbles } else { EventBubbles::DoesNotBubble };
        let cancelable = if init.parent.cancelable {
            EventCancelable::Cancelable
        } else {
            EventCancelable::NotCancelable
        };
        let event = StorageEvent::new(global, type_,
                                      bubbles, cancelable,
                                      key, oldValue, newValue,
                                      url, storageArea);
        Ok(event)
    }
}

impl StorageEventMethods for StorageEvent {
    // https://html.spec.whatwg.org/multipage/#dom-storageevent-key
    fn GetKey(&self) -> Option<DOMString> {
        self.key.borrow().clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-oldvalue
    fn GetOldValue(&self) -> Option<DOMString> {
        self.oldValue.borrow().clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-newvalue
    fn GetNewValue(&self) -> Option<DOMString> {
        self.newValue.borrow().clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-url
    fn Url(&self) -> DOMString {
        self.url.borrow().clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-storagearea
    fn GetStorageArea(&self) -> Option<Root<Storage>> {
        self.storageArea.get().map(Root::from_rooted)
    }
}
<commit_msg>Remove unnecessary uses of DOMRefCell.<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/. */

use dom::bindings::codegen::Bindings::EventBinding::{EventMethods};
use dom::bindings::codegen::Bindings::StorageEventBinding;
use dom::bindings::codegen::Bindings::StorageEventBinding::{StorageEventMethods};

use dom::bindings::codegen::InheritTypes::{EventCast};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference};
use dom::bindings::utils::{reflect_dom_object};
use dom::event::{Event, EventTypeId, EventBubbles, EventCancelable};
use dom::storage::Storage;
use util::str::DOMString;

#[dom_struct]
pub struct StorageEvent {
    event: Event,
    key: Option<DOMString>,
    oldValue: Option<DOMString>,
    newValue: Option<DOMString>,
    url: DOMString,
    storageArea: MutNullableHeap<JS<Storage>>
}

impl StorageEvent {
    pub fn new_inherited(type_id: EventTypeId,
                         key: Option<DOMString>,
                         oldValue: Option<DOMString>,
                         newValue: Option<DOMString>,
                         url: DOMString,
                         storageArea: Option<&Storage>) -> StorageEvent {
        StorageEvent {
            event: Event::new_inherited(type_id),
            key: key,
            oldValue: oldValue,
            newValue: newValue,
            url: url,
            storageArea: MutNullableHeap::new(storageArea.map(JS::from_ref))
        }
    }

    pub fn new(global: GlobalRef,
               type_: DOMString,
               bubbles: EventBubbles,
               cancelable: EventCancelable,
               key: Option<DOMString>,
               oldValue: Option<DOMString>,
               newValue: Option<DOMString>,
               url: DOMString,
               storageArea: Option<&Storage>) -> Root<StorageEvent> {
        let ev = reflect_dom_object(box StorageEvent::new_inherited(EventTypeId::StorageEvent,
                                                                    key, oldValue, newValue,
                                                                    url, storageArea),
                                    global,
                                    StorageEventBinding::Wrap);
        {
            let event = EventCast::from_ref(ev.r());
            event.InitEvent(type_, bubbles == EventBubbles::Bubbles, cancelable == EventCancelable::Cancelable);
        }
        ev
    }

    pub fn Constructor(global: GlobalRef,
                       type_: DOMString,
                       init: &StorageEventBinding::StorageEventInit) -> Fallible<Root<StorageEvent>> {
        let key = init.key.clone();
        let oldValue = init.oldValue.clone();
        let newValue = init.newValue.clone();
        let url = init.url.clone();
        let storageArea = init.storageArea.r();
        let bubbles = if init.parent.bubbles { EventBubbles::Bubbles } else { EventBubbles::DoesNotBubble };
        let cancelable = if init.parent.cancelable {
            EventCancelable::Cancelable
        } else {
            EventCancelable::NotCancelable
        };
        let event = StorageEvent::new(global, type_,
                                      bubbles, cancelable,
                                      key, oldValue, newValue,
                                      url, storageArea);
        Ok(event)
    }
}

impl StorageEventMethods for StorageEvent {
    // https://html.spec.whatwg.org/multipage/#dom-storageevent-key
    fn GetKey(&self) -> Option<DOMString> {
        self.key.clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-oldvalue
    fn GetOldValue(&self) -> Option<DOMString> {
        self.oldValue.clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-newvalue
    fn GetNewValue(&self) -> Option<DOMString> {
        self.newValue.clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-url
    fn Url(&self) -> DOMString {
        self.url.clone()
    }

    // https://html.spec.whatwg.org/multipage/#dom-storageevent-storagearea
    fn GetStorageArea(&self) -> Option<Root<Storage>> {
        self.storageArea.get().map(Root::from_rooted)
    }
}
<|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/. */

use cssparser::{Parser, Token, SourcePosition};
use properties::DeclaredValue;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use string_cache::Atom;

// Does not include the `--` prefix
pub type Name = Atom;

// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
pub fn parse_name(s: &str) -> Result<Name, ()> {
    if s.starts_with("--") {
        Ok(Atom::from_slice(&s[2..]))
    } else {
        Err(())
    }
}

#[derive(Clone, PartialEq)]
pub struct Value {
    /// In CSS syntax
    value: String,

    /// Custom property names in var() functions.
    references: HashSet<Name>,
}

pub struct BorrowedValue<'a> {
    value: &'a str,
    references: Option<&'a HashSet<Name>>,
}

pub fn parse(input: &mut Parser) -> Result<Value, ()> {
    let start = input.position();
    let mut references = Some(HashSet::new());
    // FIXME: don’t consume a top-level `!` as that would prevent parsing `!important`.
    // Maybe using Parser::parse_until_before?
    try!(parse_declaration_value(input, &mut references));
    Ok(Value {
        value: input.slice_from(start).to_owned(),
        references: references.unwrap(),
    })
}

/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Name>>)
                               -> Result<(), ()> {
    if input.is_exhausted() {
        // Need at least one token
        return Err(())
    }
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket |

            Token::Semicolon |
            Token::Delim('!') => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

/// Like parse_declaration_value,
/// but accept `!` and `;` since they are only invalid at the top level
fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<HashSet<Name>>)
                                 -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

// If the var function is valid, return Ok((custom_property_name, fallback))
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Option<HashSet<Name>>)
                              -> Result<(), ()> {
    let name = try!(input.expect_ident());
    let name = try!(parse_name(&name));
    if input.expect_comma().is_ok() {
        try!(parse_declaration_value(input, references));
    }
    if let Some(ref mut refs) = *references {
        refs.insert(name);
    }
    Ok(())
}

/// Add one custom property declaration to a map,
/// unless another with the same name was already there.
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedValue<'a>>>,
                   inherited: &'a Option<Arc<HashMap<Name, String>>>,
                   seen: &mut HashSet<&'a Name>,
                   name: &'a Name,
                   value: &'a DeclaredValue<Value>) {
    let was_not_already_present = seen.insert(name);
    if was_not_already_present {
        let map = match *custom_properties {
            Some(ref mut map) => map,
            None => {
                *custom_properties = Some(match *inherited {
                    Some(ref inherited) => inherited.iter().map(|(key, value)| {
                        (key, BorrowedValue { value: &value, references: None })
                    }).collect(),
                    None => HashMap::new(),
                });
                custom_properties.as_mut().unwrap()
            }
        };
        match *value {
            DeclaredValue::Value(ref value) => {
                map.insert(name, BorrowedValue {
                    value: &value.value,
                    references: Some(&value.references),
                });
            },
            DeclaredValue::WithVariables { .. } => unreachable!(),
            DeclaredValue::Initial => {
                map.remove(&name);
            }
            DeclaredValue::Inherit => {}  // The inherited value is what we already have.
        }
    }
}

pub fn finish_cascade(custom_properties: Option<HashMap<&Name, BorrowedValue>>,
                      inherited: &Option<Arc<HashMap<Name, String>>>)
                      -> Option<Arc<HashMap<Name, String>>> {
    if let Some(mut map) = custom_properties {
        remove_cycles(&mut map);
        Some(Arc::new(substitute_all(map, inherited)))
    } else {
        inherited.clone()
    }
}

/// https://drafts.csswg.org/css-variables/#cycles
/// The initial value of a custom property is represented by this property not being in the map.
fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
    let mut to_remove = HashSet::new();
    {
        let mut visited = HashSet::new();
        let mut stack = Vec::new();
        for name in map.keys() {
            walk(map, name, &mut stack, &mut visited, &mut to_remove);

            fn walk<'a>(map: &HashMap<&'a Name, BorrowedValue<'a>>,
                        name: &'a Name,
                        stack: &mut Vec<&'a Name>,
                        visited: &mut HashSet<&'a Name>,
                        to_remove: &mut HashSet<Name>) {
                let already_visited_before = !visited.insert(name);
                if already_visited_before {
                    return
                }
                if let Some(value) = map.get(name) {
                    if let Some(references) = value.references {
                        stack.push(name);
                        for next in references {
                            if let Some(position) = stack.iter().position(|&x| x == next) {
                                // Found a cycle
                                for &in_cycle in &stack[position..] {
                                    to_remove.insert(in_cycle.clone());
                                }
                            } else {
                                walk(map, next, stack, visited, to_remove);
                            }
                        }
                        stack.pop();
                    }
                }
            }
        }
    }
    for name in &to_remove {
        map.remove(name);
    }
}

/// Replace `var()` functions for all custom properties.
fn substitute_all(custom_properties: HashMap<&Name, BorrowedValue>,
                  inherited: &Option<Arc<HashMap<Name, String>>>)
                  -> HashMap<Name, String> {
    let mut substituted_map = HashMap::new();
    let mut invalid = HashSet::new();
    for (&name, value) in &custom_properties {
        // If this value is invalid at computed-time it won’t be inserted in substituted_map.
        // Nothing else to do.
        let _ = substitute_one(
            name, value, &custom_properties, inherited, None, &mut substituted_map, &mut invalid);
    }
    substituted_map
}

/// Replace `var()` functions for one custom property.
/// Also recursively record results for other custom properties referenced by `var()` functions.
/// Return `Err(())` for invalid at computed time.
fn substitute_one(name: &Name,
                  value: &BorrowedValue,
                  custom_properties: &HashMap<&Name, BorrowedValue>,
                  inherited: &Option<Arc<HashMap<Name, String>>>,
                  substituted: Option<&mut String>,
                  substituted_map: &mut HashMap<Name, String>,
                  invalid: &mut HashSet<Name>)
                  -> Result<(), ()> {
    if let Some(value) = substituted_map.get(name) {
        if let Some(substituted) = substituted {
            substituted.push_str(value)
        }
        return Ok(())
    }

    if invalid.contains(name) {
        return Err(());
    }
    let value = if value.references.map(|set| set.is_empty()) == Some(false) {
        let mut substituted = String::new();
        let mut input = Parser::new(&value.value);
        let mut start = input.position();
        if substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
            if let Some(value) = custom_properties.get(name) {
                substitute_one(name, value, custom_properties, inherited,
                               Some(substituted), substituted_map, invalid)
            } else {
                Err(())
            }
        }).is_ok() {
            substituted.push_str(input.slice_from(start));
            substituted
        } else {
            // Invalid at computed-value time. Use the inherited value.
            // FIXME: Should it be the inital value instead?
            // See https://lists.w3.org/Archives/Public/www-style/2015Jul/0354.html
            if let Some(value) = inherited.as_ref().and_then(|i| i.get(name)) {
                value.clone()
            } else {
                invalid.insert(name.clone());
                return Err(())
            }
        }
    } else {
        value.value.to_owned()
    };
    if let Some(substituted) = substituted {
        substituted.push_str(&value)
    }
    substituted_map.insert(name.clone(), value);
    Ok(())
}

/// Replace `var()` functions in an arbitrary bit of input.
///
/// The `substitute_one` callback is called for each `var()` function in `input`.
/// If the variable has its initial value,
/// the callback should return `Err(())` and leave `substituted` unchanged.
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
/// to `substituted` and return `Ok(())`.
///
/// Return `Err(())` if `input` is invalid at computed-value time.
fn substitute_block<F>(input: &mut Parser,
                       start: &mut SourcePosition,
                       substituted: &mut String,
                       substitute_one: &mut F)
                       -> Result<(), ()>
                       where F: FnMut(&Name, &mut String) -> Result<(), ()> {
    loop {
        let input_slice = input.slice_from(*start);
        let token = if let Ok(token) = input.next() { token } else { break };
        match token {
            Token::Function(ref name) if name == "var" => {
                substituted.push_str(input_slice);
                try!(input.parse_nested_block(|input| {
                    // parse_var_function() ensures neither .unwrap() will fail.
                    let name = input.expect_ident().unwrap();
                    let name = parse_name(&name).unwrap();

                    if substitute_one(&name, substituted).is_ok() {
                        // Skip over the fallback, as `parse_nested_block` would return `Err`
                        // if we don’t consume all of `input`.
                        // FIXME: Add a specialized method to cssparser to do this with less work.
                        while let Ok(_) = input.next() {}
                    } else {
                        try!(input.expect_comma());
                        let mut start = input.position();
                        try!(substitute_block(input, &mut start, substituted, substitute_one));
                        substituted.push_str(input.slice_from(start));
                    }
                    Ok(())
                }));
                *start = input.position();
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    substitute_block(input, start, substituted, substitute_one)
                }));
            }

            _ => {}
        }
    }
    // FIXME: deal with things being implicitly closed at the end of the input. E.g.
    // ```html
    // <div style="--color: rgb(0,0,0">
    // <p style="background: var(--color) var(--image) top left; --image: url('a.png"></p>
    // </div>
    // ```
    Ok(())
}

/// Replace `var()` functions for a non-custom property.
/// Return `Err(())` for invalid at computed time.
pub fn substitute(input: &str, custom_properties: &Option<Arc<HashMap<Name, String>>>)
                  -> Result<String, ()> {
    let empty_map;
    let custom_properties = if let &Some(ref arc) = custom_properties {
        &**arc
    } else {
        empty_map = HashMap::new();
        &empty_map
    };
    let mut substituted = String::new();
    let mut input = Parser::new(input);
    let mut start = input.position();
    try!(substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
        if let Some(value) = custom_properties.get(name) {
            substituted.push_str(value);
            Ok(())
        } else {
            Err(())
        }
    }));
    substituted.push_str(input.slice_from(start));
    Ok(substituted)
}
<commit_msg>Remove obsolete FIXME comment.<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/. */

use cssparser::{Parser, Token, SourcePosition};
use properties::DeclaredValue;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use string_cache::Atom;

// Does not include the `--` prefix
pub type Name = Atom;

// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
pub fn parse_name(s: &str) -> Result<Name, ()> {
    if s.starts_with("--") {
        Ok(Atom::from_slice(&s[2..]))
    } else {
        Err(())
    }
}

#[derive(Clone, PartialEq)]
pub struct Value {
    /// In CSS syntax
    value: String,

    /// Custom property names in var() functions.
    references: HashSet<Name>,
}

pub struct BorrowedValue<'a> {
    value: &'a str,
    references: Option<&'a HashSet<Name>>,
}

pub fn parse(input: &mut Parser) -> Result<Value, ()> {
    let start = input.position();
    let mut references = Some(HashSet::new());
    // FIXME: don’t consume a top-level `!` as that would prevent parsing `!important`.
    // Maybe using Parser::parse_until_before?
    try!(parse_declaration_value(input, &mut references));
    Ok(Value {
        value: input.slice_from(start).to_owned(),
        references: references.unwrap(),
    })
}

/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Name>>)
                               -> Result<(), ()> {
    if input.is_exhausted() {
        // Need at least one token
        return Err(())
    }
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket |

            Token::Semicolon |
            Token::Delim('!') => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

/// Like parse_declaration_value,
/// but accept `!` and `;` since they are only invalid at the top level
fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<HashSet<Name>>)
                                 -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

// If the var function is valid, return Ok((custom_property_name, fallback))
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Option<HashSet<Name>>)
                              -> Result<(), ()> {
    let name = try!(input.expect_ident());
    let name = try!(parse_name(&name));
    if input.expect_comma().is_ok() {
        try!(parse_declaration_value(input, references));
    }
    if let Some(ref mut refs) = *references {
        refs.insert(name);
    }
    Ok(())
}

/// Add one custom property declaration to a map,
/// unless another with the same name was already there.
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedValue<'a>>>,
                   inherited: &'a Option<Arc<HashMap<Name, String>>>,
                   seen: &mut HashSet<&'a Name>,
                   name: &'a Name,
                   value: &'a DeclaredValue<Value>) {
    let was_not_already_present = seen.insert(name);
    if was_not_already_present {
        let map = match *custom_properties {
            Some(ref mut map) => map,
            None => {
                *custom_properties = Some(match *inherited {
                    Some(ref inherited) => inherited.iter().map(|(key, value)| {
                        (key, BorrowedValue { value: &value, references: None })
                    }).collect(),
                    None => HashMap::new(),
                });
                custom_properties.as_mut().unwrap()
            }
        };
        match *value {
            DeclaredValue::Value(ref value) => {
                map.insert(name, BorrowedValue {
                    value: &value.value,
                    references: Some(&value.references),
                });
            },
            DeclaredValue::WithVariables { .. } => unreachable!(),
            DeclaredValue::Initial => {
                map.remove(&name);
            }
            DeclaredValue::Inherit => {}  // The inherited value is what we already have.
        }
    }
}

pub fn finish_cascade(custom_properties: Option<HashMap<&Name, BorrowedValue>>,
                      inherited: &Option<Arc<HashMap<Name, String>>>)
                      -> Option<Arc<HashMap<Name, String>>> {
    if let Some(mut map) = custom_properties {
        remove_cycles(&mut map);
        Some(Arc::new(substitute_all(map, inherited)))
    } else {
        inherited.clone()
    }
}

/// https://drafts.csswg.org/css-variables/#cycles
/// The initial value of a custom property is represented by this property not being in the map.
fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
    let mut to_remove = HashSet::new();
    {
        let mut visited = HashSet::new();
        let mut stack = Vec::new();
        for name in map.keys() {
            walk(map, name, &mut stack, &mut visited, &mut to_remove);

            fn walk<'a>(map: &HashMap<&'a Name, BorrowedValue<'a>>,
                        name: &'a Name,
                        stack: &mut Vec<&'a Name>,
                        visited: &mut HashSet<&'a Name>,
                        to_remove: &mut HashSet<Name>) {
                let already_visited_before = !visited.insert(name);
                if already_visited_before {
                    return
                }
                if let Some(value) = map.get(name) {
                    if let Some(references) = value.references {
                        stack.push(name);
                        for next in references {
                            if let Some(position) = stack.iter().position(|&x| x == next) {
                                // Found a cycle
                                for &in_cycle in &stack[position..] {
                                    to_remove.insert(in_cycle.clone());
                                }
                            } else {
                                walk(map, next, stack, visited, to_remove);
                            }
                        }
                        stack.pop();
                    }
                }
            }
        }
    }
    for name in &to_remove {
        map.remove(name);
    }
}

/// Replace `var()` functions for all custom properties.
fn substitute_all(custom_properties: HashMap<&Name, BorrowedValue>,
                  inherited: &Option<Arc<HashMap<Name, String>>>)
                  -> HashMap<Name, String> {
    let mut substituted_map = HashMap::new();
    let mut invalid = HashSet::new();
    for (&name, value) in &custom_properties {
        // If this value is invalid at computed-time it won’t be inserted in substituted_map.
        // Nothing else to do.
        let _ = substitute_one(
            name, value, &custom_properties, inherited, None, &mut substituted_map, &mut invalid);
    }
    substituted_map
}

/// Replace `var()` functions for one custom property.
/// Also recursively record results for other custom properties referenced by `var()` functions.
/// Return `Err(())` for invalid at computed time.
fn substitute_one(name: &Name,
                  value: &BorrowedValue,
                  custom_properties: &HashMap<&Name, BorrowedValue>,
                  inherited: &Option<Arc<HashMap<Name, String>>>,
                  substituted: Option<&mut String>,
                  substituted_map: &mut HashMap<Name, String>,
                  invalid: &mut HashSet<Name>)
                  -> Result<(), ()> {
    if let Some(value) = substituted_map.get(name) {
        if let Some(substituted) = substituted {
            substituted.push_str(value)
        }
        return Ok(())
    }

    if invalid.contains(name) {
        return Err(());
    }
    let value = if value.references.map(|set| set.is_empty()) == Some(false) {
        let mut substituted = String::new();
        let mut input = Parser::new(&value.value);
        let mut start = input.position();
        if substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
            if let Some(value) = custom_properties.get(name) {
                substitute_one(name, value, custom_properties, inherited,
                               Some(substituted), substituted_map, invalid)
            } else {
                Err(())
            }
        }).is_ok() {
            substituted.push_str(input.slice_from(start));
            substituted
        } else {
            // Invalid at computed-value time. Use the inherited value.
            if let Some(value) = inherited.as_ref().and_then(|i| i.get(name)) {
                value.clone()
            } else {
                invalid.insert(name.clone());
                return Err(())
            }
        }
    } else {
        value.value.to_owned()
    };
    if let Some(substituted) = substituted {
        substituted.push_str(&value)
    }
    substituted_map.insert(name.clone(), value);
    Ok(())
}

/// Replace `var()` functions in an arbitrary bit of input.
///
/// The `substitute_one` callback is called for each `var()` function in `input`.
/// If the variable has its initial value,
/// the callback should return `Err(())` and leave `substituted` unchanged.
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
/// to `substituted` and return `Ok(())`.
///
/// Return `Err(())` if `input` is invalid at computed-value time.
fn substitute_block<F>(input: &mut Parser,
                       start: &mut SourcePosition,
                       substituted: &mut String,
                       substitute_one: &mut F)
                       -> Result<(), ()>
                       where F: FnMut(&Name, &mut String) -> Result<(), ()> {
    loop {
        let input_slice = input.slice_from(*start);
        let token = if let Ok(token) = input.next() { token } else { break };
        match token {
            Token::Function(ref name) if name == "var" => {
                substituted.push_str(input_slice);
                try!(input.parse_nested_block(|input| {
                    // parse_var_function() ensures neither .unwrap() will fail.
                    let name = input.expect_ident().unwrap();
                    let name = parse_name(&name).unwrap();

                    if substitute_one(&name, substituted).is_ok() {
                        // Skip over the fallback, as `parse_nested_block` would return `Err`
                        // if we don’t consume all of `input`.
                        // FIXME: Add a specialized method to cssparser to do this with less work.
                        while let Ok(_) = input.next() {}
                    } else {
                        try!(input.expect_comma());
                        let mut start = input.position();
                        try!(substitute_block(input, &mut start, substituted, substitute_one));
                        substituted.push_str(input.slice_from(start));
                    }
                    Ok(())
                }));
                *start = input.position();
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    substitute_block(input, start, substituted, substitute_one)
                }));
            }

            _ => {}
        }
    }
    // FIXME: deal with things being implicitly closed at the end of the input. E.g.
    // ```html
    // <div style="--color: rgb(0,0,0">
    // <p style="background: var(--color) var(--image) top left; --image: url('a.png"></p>
    // </div>
    // ```
    Ok(())
}

/// Replace `var()` functions for a non-custom property.
/// Return `Err(())` for invalid at computed time.
pub fn substitute(input: &str, custom_properties: &Option<Arc<HashMap<Name, String>>>)
                  -> Result<String, ()> {
    let empty_map;
    let custom_properties = if let &Some(ref arc) = custom_properties {
        &**arc
    } else {
        empty_map = HashMap::new();
        &empty_map
    };
    let mut substituted = String::new();
    let mut input = Parser::new(input);
    let mut start = input.position();
    try!(substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
        if let Some(value) = custom_properties.get(name) {
            substituted.push_str(value);
            Ok(())
        } else {
            Err(())
        }
    }));
    substituted.push_str(input.slice_from(start));
    Ok(substituted)
}
<|endoftext|>