<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use std::error;
use std::result;
use std::fmt;
use std::borrow::Cow;

#[derive(Debug, PartialEq, Clone)]
/// Source of an error file
pub struct Source {
    /// File name of the source
    pub file: Option<String>,
    /// Line number of the source
    pub line: Option<u32>,
}

impl Source {
    /// Create an empty source, with both fields set to None
    pub fn empty() -> Source {
        Source { file: None, line: None }
    }

    /// Create a new source pointing to file
    pub fn new<S: Into<String>>(s: S) -> Source {
        Source { file: Some(s.into()), line: None }
    }

    /// Sets line number of a source (with &mut self)
    pub fn set_line(&mut self, line: u32) {
        self.line = Some(line);
    }

    /// Unsets a line number of a source
    pub fn unset_line(&mut self) {
        self.line = None;
    }
}

impl<'a> From<&'a Source> for Source {
    fn from(s: &'a Source) -> Source {
        s.clone()
    }
}
#[derive(Debug, PartialEq)]
/// Crowbook Error type
pub struct Error {
    /// Origin (file, line) of the error, if there is one
    source: Source,
    inner: Inner,
}

impl Error {
    /// Creates a new default error
    pub fn default<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Default(msg.into())
        }
    }

    /// Creates a new parser error
    ///
    /// Error when parsing markdown file
    pub fn parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Parser(msg.into())
        }
    }

    /// Creates a new config parser error
    ///
    /// Error when parsing book file
    pub fn config_parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::ConfigParser(msg.into())
        }
    }

    /// Creates a new file not found error
    ///
    /// * msg: description of why the file was needed
    /// * file: file name
    pub fn file_not_found<S1: Into<Cow<'static, str>>, S2: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S1, file: S2) -> Error {
        Error {
            source: source.into(),
            inner: Inner::FileNotFound(msg.into(), file.into())
        }
    }

    /// Creates a new render error
    ///
    /// Error when rendering
    pub fn render<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Render(msg.into())
        }
    }

    /// Creates a new invalid option error
    ///
    /// Error when trying to set an option
    pub fn invalid_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::InvalidOption(msg.into())
        }
    }

    /// Creates a new zipper error
    ///
    /// Error when moving/copying files to temporary dir, e.g. using `zip` commmand
    pub fn zipper<S: Into<Cow<'static, str>>>(msg: S) -> Error {
        Error {
            source: Source::empty(),
            inner: Inner::Zipper(msg.into())
        }
    }

    /// Creates a new book option error
    ///
    /// Used when converting an error to invalid type
    pub fn book_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::BookOption(msg.into())
        }
    }

    /// Change the source of an error
    pub fn with_source<O: Into<Source>>(mut self, source: O) -> Error {
        self.source = source.into();
        self
    }

    /// Returns true if self is a default option error, false else
    pub fn is_default(&self) -> bool {
        match self.inner {
            Inner::Default(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a parser error, false else
    pub fn is_parser(&self) -> bool {
        match self.inner {
            Inner::Parser(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a config parser error, false else
    pub fn is_config_parser(&self) -> bool {
        match self.inner {
            Inner::ConfigParser(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a file not found error, false else
    pub fn is_file_not_found(&self) -> bool {
        match self.inner {
            Inner::FileNotFound(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a render error, false else
    pub fn is_render(&self) -> bool {
        match self.inner {
            Inner::Render(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a zipper error, false else
    pub fn is_zipper(&self) -> bool {
        match self.inner {
            Inner::Zipper(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a book option error, false else
    pub fn is_book_option(&self) -> bool {
        match self.inner {
            Inner::BookOption(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is an invalid option error, false else
    pub fn is_invalid_option(&self) -> bool {
        match self.inner {
            Inner::InvalidOption(..) => true,
            _ => false,
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self.inner {
            Inner::Default(ref s)
                | Inner::Parser(ref s)
                | Inner::Zipper(ref s)
                | Inner::BookOption(ref s)
                | Inner::ConfigParser(ref s)
                | Inner::InvalidOption(ref s)
                | Inner::Render(ref s)
                => s.as_ref(),

            Inner::FileNotFound(..) => "File not found",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let source = &self.source;
        if let Some(ref file) = source.file {
            try!(write!(f, "{}", file));
            if let Some(line) = source.line {
                try!(write!(f, ":{}", line));
            }
            try!(write!(f, ": "));
        }

        try!(match self.inner {
            Inner::Default(ref s) => {
                write!(f, "Error: {}", s)
            },
            Inner::Parser(ref s) => {
                write!(f, "Error parsing markdown: {}", s)
            },
            Inner::ConfigParser(ref s) => {
                try!(f.write_str("Error parsing configuration file: "));
                f.write_str(s)
            },
            Inner::FileNotFound(ref description, ref file) => {
                write!(f, "Could not find file '{}' for {}", file, description)
            },
            Inner::Render(ref s) => {
                try!(f.write_str("Error during rendering: "));
                f.write_str(s)
            },
            Inner::Zipper(ref s) => {
                try!(f.write_str("Error during temporary files editing: "));
                f.write_str(s)
            },
            Inner::BookOption(ref s) => {
                try!(f.write_str("Error converting BookOption: "));
                f.write_str(s)
            },
            Inner::InvalidOption(ref s) => {
                try!(f.write_str("Error accessing book option: "));
                f.write_str(s)
            },
        });
        Ok(())
    }
}

/// Crowbook's Result type, used by many methods that can fail
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, PartialEq)]
enum Inner {
    /// Default variant
    Default(Cow<'static, str>),
    /// An error in Parsing markdown file
    Parser(Cow<'static, str>),
    /// An error in parsing a book configuration file
    ConfigParser(Cow<'static, str>),
    /// An error when a file is not found
    FileNotFound(Cow<'static, str>, Cow<'static, str>), //description, file
    /// An error in a renderer
    Render(Cow<'static, str>),
    /// An error during "zipping" processus
    Zipper(Cow<'static, str>),
    /// An error relative to BookOption convertion (usually a type error)
    BookOption(Cow<'static, str>),
    /// An invalid option
    InvalidOption(Cow<'static, str>),
}
<commit_msg>Change formatting of Error::default<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use std::error;
use std::result;
use std::fmt;
use std::borrow::Cow;

#[derive(Debug, PartialEq, Clone)]
/// Source of an error file
pub struct Source {
    /// File name of the source
    pub file: Option<String>,
    /// Line number of the source
    pub line: Option<u32>,
}

impl Source {
    /// Create an empty source, with both fields set to None
    pub fn empty() -> Source {
        Source { file: None, line: None }
    }

    /// Create a new source pointing to file
    pub fn new<S: Into<String>>(s: S) -> Source {
        Source { file: Some(s.into()), line: None }
    }

    /// Sets line number of a source (with &mut self)
    pub fn set_line(&mut self, line: u32) {
        self.line = Some(line);
    }

    /// Unsets a line number of a source
    pub fn unset_line(&mut self) {
        self.line = None;
    }
}

impl<'a> From<&'a Source> for Source {
    fn from(s: &'a Source) -> Source {
        s.clone()
    }
}
#[derive(Debug, PartialEq)]
/// Crowbook Error type
pub struct Error {
    /// Origin (file, line) of the error, if there is one
    source: Source,
    inner: Inner,
}

impl Error {
    /// Creates a new default error
    pub fn default<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Default(msg.into())
        }
    }

    /// Creates a new parser error
    ///
    /// Error when parsing markdown file
    pub fn parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Parser(msg.into())
        }
    }

    /// Creates a new config parser error
    ///
    /// Error when parsing book file
    pub fn config_parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::ConfigParser(msg.into())
        }
    }

    /// Creates a new file not found error
    ///
    /// * msg: description of why the file was needed
    /// * file: file name
    pub fn file_not_found<S1: Into<Cow<'static, str>>, S2: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S1, file: S2) -> Error {
        Error {
            source: source.into(),
            inner: Inner::FileNotFound(msg.into(), file.into())
        }
    }

    /// Creates a new render error
    ///
    /// Error when rendering
    pub fn render<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::Render(msg.into())
        }
    }

    /// Creates a new invalid option error
    ///
    /// Error when trying to set an option
    pub fn invalid_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::InvalidOption(msg.into())
        }
    }

    /// Creates a new zipper error
    ///
    /// Error when moving/copying files to temporary dir, e.g. using `zip` commmand
    pub fn zipper<S: Into<Cow<'static, str>>>(msg: S) -> Error {
        Error {
            source: Source::empty(),
            inner: Inner::Zipper(msg.into())
        }
    }

    /// Creates a new book option error
    ///
    /// Used when converting an error to invalid type
    pub fn book_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
        Error {
            source: source.into(),
            inner: Inner::BookOption(msg.into())
        }
    }

    /// Change the source of an error
    pub fn with_source<O: Into<Source>>(mut self, source: O) -> Error {
        self.source = source.into();
        self
    }

    /// Returns true if self is a default option error, false else
    pub fn is_default(&self) -> bool {
        match self.inner {
            Inner::Default(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a parser error, false else
    pub fn is_parser(&self) -> bool {
        match self.inner {
            Inner::Parser(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a config parser error, false else
    pub fn is_config_parser(&self) -> bool {
        match self.inner {
            Inner::ConfigParser(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a file not found error, false else
    pub fn is_file_not_found(&self) -> bool {
        match self.inner {
            Inner::FileNotFound(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a render error, false else
    pub fn is_render(&self) -> bool {
        match self.inner {
            Inner::Render(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a zipper error, false else
    pub fn is_zipper(&self) -> bool {
        match self.inner {
            Inner::Zipper(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is a book option error, false else
    pub fn is_book_option(&self) -> bool {
        match self.inner {
            Inner::BookOption(..) => true,
            _ => false,
        }
    }

    /// Returns true if self is an invalid option error, false else
    pub fn is_invalid_option(&self) -> bool {
        match self.inner {
            Inner::InvalidOption(..) => true,
            _ => false,
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self.inner {
            Inner::Default(ref s)
                | Inner::Parser(ref s)
                | Inner::Zipper(ref s)
                | Inner::BookOption(ref s)
                | Inner::ConfigParser(ref s)
                | Inner::InvalidOption(ref s)
                | Inner::Render(ref s)
                => s.as_ref(),

            Inner::FileNotFound(..) => "File not found",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let source = &self.source;
        if let Some(ref file) = source.file {
            try!(write!(f, "{}", file));
            if let Some(line) = source.line {
                try!(write!(f, ":{}", line));
            }
            try!(write!(f, ": "));
        }

        try!(match self.inner {
            Inner::Default(ref s) => {
                write!(f, "{}", s)
            },
            Inner::Parser(ref s) => {
                write!(f, "Error parsing markdown: {}", s)
            },
            Inner::ConfigParser(ref s) => {
                try!(f.write_str("Error parsing configuration file: "));
                f.write_str(s)
            },
            Inner::FileNotFound(ref description, ref file) => {
                write!(f, "Could not find file '{}' for {}", file, description)
            },
            Inner::Render(ref s) => {
                try!(f.write_str("Error during rendering: "));
                f.write_str(s)
            },
            Inner::Zipper(ref s) => {
                try!(f.write_str("Error during temporary files editing: "));
                f.write_str(s)
            },
            Inner::BookOption(ref s) => {
                try!(f.write_str("Error converting BookOption: "));
                f.write_str(s)
            },
            Inner::InvalidOption(ref s) => {
                try!(f.write_str("Error accessing book option: "));
                f.write_str(s)
            },
        });
        Ok(())
    }
}

/// Crowbook's Result type, used by many methods that can fail
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, PartialEq)]
enum Inner {
    /// Default variant
    Default(Cow<'static, str>),
    /// An error in Parsing markdown file
    Parser(Cow<'static, str>),
    /// An error in parsing a book configuration file
    ConfigParser(Cow<'static, str>),
    /// An error when a file is not found
    FileNotFound(Cow<'static, str>, Cow<'static, str>), //description, file
    /// An error in a renderer
    Render(Cow<'static, str>),
    /// An error during "zipping" processus
    Zipper(Cow<'static, str>),
    /// An error relative to BookOption convertion (usually a type error)
    BookOption(Cow<'static, str>),
    /// An invalid option
    InvalidOption(Cow<'static, str>),
}
<|endoftext|>


<commit_before>
use std::hashmap::{HashMap};

use std::unstable::atomics::{atomic_add, AcqRel};

use std::ptr;

use vm::{ThreadContext};
use object::{JavaObject, JavaObjectId};
use class::{JavaClassRef};
use objectbroker::{ObjectBrokerMessage, OB_RQ_DISOWN, OB_RQ_OWN, OB_RQ_ADD_REF, OB_RQ_RELEASE};

// LocalHeap is a thread-local utility for threads to create,
// destroy and access Java objects. Even though it is technically
// not a heap (actual heap management is forwarded to Rust's
// runtime heap manager), it is referred to as such because of it
// behaviour which is to provide the Java heap. LocalHeap is tightly
// coupled to a ThreadContext. For cross-thread access to objects,
// LocalHeap holds a connection to the VM's ObjectBroker.

pub struct LocalHeap {
	// backref to owning thread. Unfortunately a borrowed ref
	// cannot solve this so we need an unsafe pointer.
	// http://stackoverflow.com/questions/20698384
	priv thread : *mut ThreadContext,

	// shortcut to thread-id
	priv tid : uint,

	// heap objects currently owned by this thread context
	priv owned_objects : HashMap<JavaObjectId, ~JavaObject>,

	//
}

static LH_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 1024;
static mut ObjectIdCounter : JavaObjectId = 0;

impl LocalHeap  {

	// ----------------------------------------------
	pub fn dummy() -> LocalHeap {
		LocalHeap {
			thread : ptr::mut_null(),
			tid : 0,
			owned_objects : HashMap::new(),
		}
	}

	// ----------------------------------------------
	pub unsafe fn new_with_owner(t : &mut ThreadContext) -> LocalHeap {
		LocalHeap {
			thread : ptr::to_mut_unsafe_ptr(t),
			tid : t.get_tid(),

			owned_objects : HashMap::with_capacity(LH_INITIAL_OBJ_HASHMAP_CAPACITY),
		}
	}

	// ----------------------------------------------
	fn get_thread<'t>(&'t self) -> &'t ThreadContext {
		unsafe { &*self.thread }
	}

	fn get_thread_mut<'t>(&'t self) -> &'t mut ThreadContext {
		unsafe { &mut *self.thread }
	}

	// ----------------------------------------------
	pub fn new_object(&mut self, jclass : JavaClassRef) -> JavaObjectId {
		// generate an unique object id
		let id = unsafe {
			atomic_add(&mut ObjectIdCounter, 1, AcqRel)
		};

		// this id must be unique - if not, we ran out of
		// 64bit indices ("impossible - our shields cannot be
		// broken") or there is a logic flaw somewhere.
		assert!(!self.owned_objects.contains_key(&id));

		// tell the object broker to ensure other threads
		// can request the object by its oid
		self.get_thread_mut().send_message(OB_RQ_ADD_REF(self.tid, id));
		self.owned_objects.insert(id, ~JavaObject::new(jclass, id));
		id
	}

	// ----------------------------------------------
	pub fn new_array_object() {
		// TODO
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn add_ref(&mut self, oid : JavaObjectId) {
		// if this is a local object, addref it
		match self.owned_objects.find_mut(&oid) {
			Some(obj) => {
				obj.intern_add_ref();
				return
			},
			// fallthru
			None => ()
		}
		// forward request to ObjectBroker for remote objects
		self.get_thread().send_message(OB_RQ_ADD_REF(self.tid, oid));
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn release(&mut self, oid : JavaObjectId) {
		// forward request to ObjectBroker for remote objects
		if !self.owned_objects.contains_key(&oid) {
			self.get_thread().send_message(OB_RQ_RELEASE(self.tid, oid));
			return;
		}

		{
			// if this is a local object, release it
			let m = self.owned_objects.find_mut(&oid).unwrap();
			if m.intern_release() {
				return;
			}
		}

		// the object's reference counter reached zero
		// and we can therefore safely drop it.
		self.owned_objects.pop(&oid);
	}

	// ----------------------------------------------
	// Access a specific object. If the object requested
	// is owned by the current thread, access is immediately
	// granted, otherwise the current task blocks until
	// ownership can be obtained.
	pub fn access_object(&mut self, oid : JavaObjectId, wrap : |&JavaObject| -> ()) {
		match self.owned_objects.find(&oid) {
			Some(ref mut obj) => {
				wrap(**obj);

				// check if we have any pending requests for this object,
				// if so, satisfy them immediately
			/*	match obj.pop_waiting_thread() {
					None => (),
					Some(tid) => {
						self.send_to_thread(obj, tid);
					}
				} */
				return
			},
			// fallthru
			None => ()
		}

		self.get_thread().send_message(OB_RQ_OWN(self.tid, oid));
		self.get_thread_mut().handle_messages_until(|msg : &ObjectBrokerMessage| -> bool {
			match *msg {
				OB_RQ_DISOWN(ref rtid, ref roid, ref obj, ref rec) => {
					// when waiting for objects, we always block on
					// obtaining them so it is not possible that
					// multiple requests are sent and responses
					// received in a different order.
					assert_eq!(*rec, self.tid);
					true
				},
				_ => false
			}
		});

		self.access_object(oid, wrap)
	}

	// ----------------------------------------------
	// Transfer ownership of an object to a particular thread
	pub fn send_to_thread(&mut self, oid : JavaObjectId, tid : uint) {
		let obj = self.owned_objects.pop(&oid).unwrap();
		let m = OB_RQ_DISOWN(self.tid, oid, obj, tid);
		self.get_thread().send_message(m);
	}

	// ----------------------------------------------
	// Handle any of the OB_RQ messages
	pub fn handle_message(&mut self, o : ObjectBrokerMessage) {
		match o {
			OB_RQ_ADD_REF(a,b) => self.add_ref(b),
			OB_RQ_RELEASE(a,b) => self.release(b),
			OB_RQ_OWN(a,b) => self.send_to_thread(b, a),

			OB_RQ_DISOWN(a,b,obj,rec) => {
				// currently we should not be receiving objects that we
				// did not request using OB_RQ_OWN
				assert_eq!(rec, self.tid);
				self.owned_objects.insert(b, obj);
			},

			_ => fail!("logic error, message unexpected"),
		}
	}
}
<commit_msg>doc cleanup<commit_after>
use std::hashmap::{HashMap};

use std::unstable::atomics::{atomic_add, AcqRel};

use std::ptr;

use vm::{ThreadContext};
use object::{JavaObject, JavaObjectId};
use class::{JavaClassRef};
use objectbroker::*;

// LocalHeap is a thread-local utility for threads to create,
// destroy and access Java objects. Even though it is technically
// not a heap (actual heap management is forwarded to Rust's
// runtime heap manager), it is referred to as such because of it
// behaviour which is to provide the Java heap.
//
// LocalHeap is tightly 1:1 coupled to a ThreadContext.
// ThreadContext forwards all OB_RQ messages that it receives from
// ObjectBroker to LocalHeap.

pub struct LocalHeap {
	// backref to owning thread. Unfortunately a borrowed ref
	// cannot solve this so we need an unsafe pointer.
	// http://stackoverflow.com/questions/20698384
	priv thread : *mut ThreadContext,

	// shortcut to thread-id
	priv tid : uint,

	// heap objects currently owned by this thread context
	priv owned_objects : HashMap<JavaObjectId, ~JavaObject>,

	//
}

static LH_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 1024;
static mut ObjectIdCounter : JavaObjectId = 0;

impl LocalHeap  {

	// ----------------------------------------------
	pub fn dummy() -> LocalHeap {
		LocalHeap {
			thread : ptr::mut_null(),
			tid : 0,
			owned_objects : HashMap::new(),
		}
	}

	// ----------------------------------------------
	pub unsafe fn new_with_owner(t : &mut ThreadContext) -> LocalHeap {
		LocalHeap {
			thread : ptr::to_mut_unsafe_ptr(t),
			tid : t.get_tid(),

			owned_objects : HashMap::with_capacity(LH_INITIAL_OBJ_HASHMAP_CAPACITY),
		}
	}

	// ----------------------------------------------
	fn get_thread<'t>(&'t self) -> &'t ThreadContext {
		unsafe { &*self.thread }
	}

	fn get_thread_mut<'t>(&'t self) -> &'t mut ThreadContext {
		unsafe { &mut *self.thread }
	}

	// ----------------------------------------------
	pub fn new_object(&mut self, jclass : JavaClassRef) -> JavaObjectId {
		// generate an unique object id
		let id = unsafe {
			atomic_add(&mut ObjectIdCounter, 1, AcqRel)
		};

		// this id must be unique - if not, we ran out of
		// 64bit indices ("impossible - our shields cannot be
		// broken") or there is a logic flaw somewhere.
		assert!(!self.owned_objects.contains_key(&id));

		// tell the object broker to ensure other threads
		// can request the object by its oid
		self.get_thread_mut().send_message(OB_RQ_ADD_REF(self.tid, id));
		self.owned_objects.insert(id, ~JavaObject::new(jclass, id));
		id
	}

	// ----------------------------------------------
	pub fn new_array_object() {
		// TODO
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn add_ref(&mut self, oid : JavaObjectId) {
		// if this is a local object, addref it
		match self.owned_objects.find_mut(&oid) {
			Some(obj) => {
				obj.intern_add_ref();
				return
			},
			// fallthru
			None => ()
		}
		// forward request to ObjectBroker for remote objects
		self.get_thread().send_message(OB_RQ_ADD_REF(self.tid, oid));
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn release(&mut self, oid : JavaObjectId) {
		// forward request to ObjectBroker for remote objects
		if !self.owned_objects.contains_key(&oid) {
			self.get_thread().send_message(OB_RQ_RELEASE(self.tid, oid));
			return;
		}

		{
			// if this is a local object, release it
			let m = self.owned_objects.find_mut(&oid).unwrap();
			if m.intern_release() {
				return;
			}
		}

		// the object's reference counter reached zero
		// and we can therefore safely drop it.
		self.owned_objects.pop(&oid);
	}

	// ----------------------------------------------
	// Access a specific object. If the object requested
	// is owned by the current thread, access is immediately
	// granted, otherwise the current task blocks until
	// ownership can be obtained.
	pub fn access_object(&mut self, oid : JavaObjectId, wrap : |&JavaObject| -> ()) {
		match self.owned_objects.find(&oid) {
			Some(ref mut obj) => {
				wrap(**obj);

				// check if we have any pending requests for this object,
				// if so, satisfy them immediately
			/*	match obj.pop_waiting_thread() {
					None => (),
					Some(tid) => {
						self.send_to_thread(obj, tid);
					}
				} */
				return
			},
			// fallthru
			None => ()
		}

		self.get_thread().send_message(OB_RQ_OWN(self.tid, oid));
		self.get_thread_mut().handle_messages_until(|msg : &ObjectBrokerMessage| -> bool {
			match *msg {
				OB_RQ_DISOWN(ref rtid, ref roid, ref obj, ref rec) => {
					// when waiting for objects, we always block on
					// obtaining them so it is not possible that
					// multiple requests are sent and responses
					// received in a different order.
					assert_eq!(*rec, self.tid);
					true
				},
				_ => false
			}
		});

		self.access_object(oid, wrap)
	}

	// ----------------------------------------------
	// Transfer ownership of an object to a particular thread
	pub fn send_to_thread(&mut self, oid : JavaObjectId, tid : uint) {
		let obj = self.owned_objects.pop(&oid).unwrap();
		let m = OB_RQ_DISOWN(self.tid, oid, obj, tid);
		self.get_thread().send_message(m);
	}

	// ----------------------------------------------
	// Handle any of the OB_RQ messages
	pub fn handle_message(&mut self, o : ObjectBrokerMessage) {
		match o {
			OB_RQ_ADD_REF(a,b) => self.add_ref(b),
			OB_RQ_RELEASE(a,b) => self.release(b),
			OB_RQ_OWN(a,b) => self.send_to_thread(b, a),

			OB_RQ_DISOWN(a,b,obj,rec) => {
				// currently we should not be receiving objects that we
				// did not request using OB_RQ_OWN
				assert_eq!(rec, self.tid);
				self.owned_objects.insert(b, obj);
			},

			_ => fail!("logic error, message unexpected"),
		}
	}
}
<|endoftext|>


<commit_before>//! Creates a jack midi input and output ports. The application prints
//! out all values sent to it through the input port. It also sends a
//! Note On and Off event, once every cycle, on the output port.
extern crate jack;
use std::io;

fn main() {
    // open client
    let (client, _status) =
        jack::Client::new("rust_jack_show_midi", jack::ClientOptions::NO_START_SERVER).unwrap();

    // process logic
    let mut maker = client
        .register_port("rust_midi_maker", jack::MidiOut::default())
        .unwrap();
    let shower = client
        .register_port("rust_midi_shower", jack::MidiIn::default())
        .unwrap();
    let cback = move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
        let show_p = shower.iter(ps);
        for e in show_p {
            println!("{:?}", e);
        }
        let mut put_p = maker.writer(ps);
        put_p
            .write(&jack::RawMidi {
                time: 0,
                bytes: &[
                    0b10010000, /* Note On, channel 1 */
                    0b01000000, /* Key number */
                    0b01111111, /* Velocity */
                ],
            })
            .unwrap();
        put_p
            .write(&jack::RawMidi {
                time: ps.n_frames() / 2,
                bytes: &[
                    0b10000000, /* Note Off, channel 1 */
                    0b01000000, /* Key number */
                    0b01111111, /* Velocity */
                ],
            })
            .unwrap();
        jack::Control::Continue
    };

    // activate
    let active_client = client
        .activate_async((), jack::ClosureProcessHandler::new(cback))
        .unwrap();

    // wait
    println!("Press any key to quit");
    let mut user_input = String::new();
    io::stdin().read_line(&mut user_input).ok();

    // optional deactivation
    active_client.deactivate().unwrap();
}
<commit_msg>Realtime midi (#113)<commit_after>//! Creates a jack midi input and output ports. The application prints
//! out all values sent to it through the input port. It also sends a
//! Note On and Off event, once every cycle, on the output port.
extern crate jack;
use std::convert::From;
use std::io;
use std::sync::mpsc::sync_channel;

const MAX_MIDI: usize = 3;

//a fixed size container to copy data out of real-time thread
#[derive(Copy, Clone)]
struct MidiCopy {
    len: usize,
    data: [u8; MAX_MIDI],
    time: jack::Frames,
}

impl From<jack::RawMidi<'_>> for MidiCopy {
    fn from(midi: jack::RawMidi<'_>) -> Self {
        let len = std::cmp::min(MAX_MIDI, midi.bytes.len());
        let mut data = [0; MAX_MIDI];
        data[..len].copy_from_slice(&midi.bytes[..len]);
        MidiCopy {
            len,
            data,
            time: midi.time,
        }
    }
}

impl std::fmt::Debug for MidiCopy {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "Midi {{ time: {}, len: {}, data: {:?} }}",
            self.time,
            self.len,
            &self.data[..self.len]
        )
    }
}

fn main() {
    // open client
    let (client, _status) =
        jack::Client::new("rust_jack_show_midi", jack::ClientOptions::NO_START_SERVER).unwrap();

    //create a sync channel to send back copies of midi messages we get
    let (sender, receiver) = sync_channel(64);

    // process logic
    let mut maker = client
        .register_port("rust_midi_maker", jack::MidiOut::default())
        .unwrap();
    let shower = client
        .register_port("rust_midi_shower", jack::MidiIn::default())
        .unwrap();

    let cback = move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
        let show_p = shower.iter(ps);
        for e in show_p {
            let c: MidiCopy = e.into();
            let _ = sender.try_send(c);
        }
        let mut put_p = maker.writer(ps);
        put_p
            .write(&jack::RawMidi {
                time: 0,
                bytes: &[
                    0b10010000, /* Note On, channel 1 */
                    0b01000000, /* Key number */
                    0b01111111, /* Velocity */
                ],
            })
            .unwrap();
        put_p
            .write(&jack::RawMidi {
                time: ps.n_frames() / 2,
                bytes: &[
                    0b10000000, /* Note Off, channel 1 */
                    0b01000000, /* Key number */
                    0b01111111, /* Velocity */
                ],
            })
            .unwrap();
        jack::Control::Continue
    };

    // activate
    let active_client = client
        .activate_async((), jack::ClosureProcessHandler::new(cback))
        .unwrap();

    //spawn a non-real-time thread that prints out the midi messages we get
    std::thread::spawn(move || {
        while let Ok(m) = receiver.recv() {
            println!("{:?}", m);
        }
    });

    // wait
    println!("Press any key to quit");
    let mut user_input = String::new();
    io::stdin().read_line(&mut user_input).ok();

    // optional deactivation
    active_client.deactivate().unwrap();
}
<|endoftext|>


<commit_before><commit_msg>Rendering polys in a separate function<commit_after><|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/. */

#![allow(unsafe_code)]

//! A drop-in replacement for string_cache, but backed by Gecko `nsIAtom`s.

use gecko_bindings::bindings::Gecko_AddRefAtom;
use gecko_bindings::bindings::Gecko_Atomize;
use gecko_bindings::bindings::Gecko_Atomize16;
use gecko_bindings::bindings::Gecko_ReleaseAtom;
use gecko_bindings::structs::{nsIAtom, nsIAtom_AtomKind};
use nsstring::{nsAString, nsString};
use precomputed_hash::PrecomputedHash;
use std::ascii::AsciiExt;
use std::borrow::{Cow, Borrow};
use std::char::{self, DecodeUtf16};
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::iter::Cloned;
use std::mem;
use std::ops::Deref;
use std::slice;

#[macro_use]
#[allow(improper_ctypes, non_camel_case_types, missing_docs)]
pub mod atom_macro {
    include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs"));
}

#[macro_use]
pub mod namespace;

pub use self::namespace::{Namespace, WeakNamespace};

macro_rules! local_name {
    ($s: tt) => { atom!($s) }
}

/// A strong reference to a Gecko atom.
#[derive(Eq, PartialEq)]
pub struct Atom(*mut WeakAtom);

/// An atom *without* a strong reference.
///
/// Only usable as `&'a WeakAtom`,
/// where `'a` is the lifetime of something that holds a strong reference to that atom.
pub struct WeakAtom(nsIAtom);

/// A BorrowedAtom for Gecko is just a weak reference to a `nsIAtom`, that
/// hasn't been bumped.
pub type BorrowedAtom<'a> = &'a WeakAtom;

impl Deref for Atom {
    type Target = WeakAtom;

    #[inline]
    fn deref(&self) -> &WeakAtom {
        unsafe {
            &*self.0
        }
    }
}

impl PrecomputedHash for Atom {
    #[inline]
    fn precomputed_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl Borrow<WeakAtom> for Atom {
    #[inline]
    fn borrow(&self) -> &WeakAtom {
        self
    }
}

impl Eq for WeakAtom {}
impl PartialEq for WeakAtom {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        let weak: *const WeakAtom = self;
        let other: *const WeakAtom = other;
        weak == other
    }
}

unsafe impl Send for Atom {}
unsafe impl Sync for Atom {}
unsafe impl Sync for WeakAtom {}

impl WeakAtom {
    /// Construct a `WeakAtom` from a raw `nsIAtom`.
    #[inline]
    pub unsafe fn new<'a>(atom: *const nsIAtom) -> &'a mut Self {
        &mut *(atom as *mut WeakAtom)
    }

    /// Clone this atom, bumping the refcount if the atom is not static.
    #[inline]
    pub fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }

    /// Get the atom hash.
    #[inline]
    pub fn get_hash(&self) -> u32 {
        self.0.mHash
    }

    /// Get the atom as a slice of utf-16 chars.
    #[inline]
    pub fn as_slice(&self) -> &[u16] {
        unsafe {
            slice::from_raw_parts((*self.as_ptr()).mString, self.len() as usize)
        }
    }

    // NOTE: don't expose this, since it's slow, and easy to be misused.
    fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
        char::decode_utf16(self.as_slice().iter().cloned())
    }

    /// Execute `cb` with the string that this atom represents.
    ///
    /// Find alternatives to this function when possible, please, since it's
    /// pretty slow.
    pub fn with_str<F, Output>(&self, cb: F) -> Output
        where F: FnOnce(&str) -> Output
    {
        // FIXME(bholley): We should measure whether it makes more sense to
        // cache the UTF-8 version in the Gecko atom table somehow.
        let owned = self.to_string();
        cb(&owned)
    }

    /// Convert this Atom into a string, decoding the UTF-16 bytes.
    ///
    /// Find alternatives to this function when possible, please, since it's
    /// pretty slow.
    #[inline]
    pub fn to_string(&self) -> String {
        String::from_utf16(self.as_slice()).unwrap()
    }

    /// Returns whether this atom is static.
    #[inline]
    pub fn is_static(&self) -> bool {
        unsafe {
            (*self.as_ptr()).mKind() == nsIAtom_AtomKind::StaticAtom as u32
        }
    }

    /// Returns the length of the atom string.
    #[inline]
    pub fn len(&self) -> u32 {
        unsafe {
            (*self.as_ptr()).mLength()
        }
    }

    /// Returns whether this atom is the empty string.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the atom as a mutable pointer.
    #[inline]
    pub fn as_ptr(&self) -> *mut nsIAtom {
        let const_ptr: *const nsIAtom = &self.0;
        const_ptr as *mut nsIAtom
    }

    /// Convert this atom to ASCII lower-case
    pub fn to_ascii_lowercase(&self) -> Atom {
        let slice = self.as_slice();
        match slice.iter().position(|&char16| (b'A' as u16) <= char16 && char16 <= (b'Z' as u16)) {
            None => self.clone(),
            Some(i) => {
                let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
                let mut vec;
                let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
                    buffer_prefix.copy_from_slice(slice);
                    buffer_prefix
                } else {
                    vec = slice.to_vec();
                    &mut vec
                };
                for char16 in &mut mutable_slice[i..] {
                    if *char16 <= 0x7F {
                        *char16 = (*char16 as u8).to_ascii_lowercase() as u16
                    }
                }
                Atom::from(&*mutable_slice)
            }
        }
    }

    /// Return whether two atoms are ASCII-case-insensitive matches
    pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        if self == other {
            return true;
        }

        let a = self.as_slice();
        let b = other.as_slice();
        a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
            if a16 <= 0x7F && b16 <= 0x7F {
                (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
            } else {
                a16 == b16
            }
        })
    }

    /// Return whether this atom is an ASCII-case-insensitive match for the given string
    pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
        self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
        .eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
    }
}

impl fmt::Debug for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko WeakAtom({:p}, {})", self, self)
    }
}

impl fmt::Display for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        for c in self.chars() {
            w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))?
        }
        Ok(())
    }
}

impl Atom {
    /// Execute a callback with the atom represented by `ptr`.
    pub unsafe fn with<F, R>(ptr: *mut nsIAtom, callback: F) -> R where F: FnOnce(&Atom) -> R {
        let atom = Atom(WeakAtom::new(ptr));
        let ret = callback(&atom);
        mem::forget(atom);
        ret
    }

    /// Creates an atom from an static atom pointer without checking in release
    /// builds.
    ///
    /// Right now it's only used by the atom macro, and ideally it should keep
    /// that way, now we have sugar for is_static, creating atoms using
    /// Atom::from should involve almost no overhead.
    #[inline]
    unsafe fn from_static(ptr: *mut nsIAtom) -> Self {
        let atom = Atom(ptr as *mut WeakAtom);
        debug_assert!(atom.is_static(),
                      "Called from_static for a non-static atom!");
        atom
    }

    /// Creates an atom from a dynamic atom pointer that has already had AddRef
    /// called on it.
    #[inline]
    pub unsafe fn from_addrefed(ptr: *mut nsIAtom) -> Self {
        debug_assert!(!ptr.is_null());
        unsafe {
            Atom(WeakAtom::new(ptr))
        }
    }

    /// Convert this atom into an addrefed nsIAtom pointer.
    #[inline]
    pub fn into_addrefed(self) -> *mut nsIAtom {
        let ptr = self.as_ptr();
        mem::forget(self);
        ptr
    }
}

impl Hash for Atom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Hash for WeakAtom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Clone for Atom {
    #[inline(always)]
    fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }
}

impl Drop for Atom {
    #[inline]
    fn drop(&mut self) {
        if !self.is_static() {
            unsafe {
                Gecko_ReleaseAtom(self.as_ptr());
            }
        }
    }
}

impl Default for Atom {
    #[inline]
    fn default() -> Self {
        atom!("")
    }
}

impl fmt::Debug for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko Atom({:p}, {})", self.0, self)
    }
}

impl fmt::Display for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            (&*self.0).fmt(w)
        }
    }
}

impl<'a> From<&'a str> for Atom {
    #[inline]
    fn from(string: &str) -> Atom {
        debug_assert!(string.len() <= u32::max_value() as usize);
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize(string.as_ptr() as *const _, string.len() as u32)
            ))
        }
    }
}

impl<'a> From<&'a [u16]> for Atom {
    #[inline]
    fn from(slice: &[u16]) -> Atom {
        Atom::from(&*nsString::from(slice))
    }
}

impl<'a> From<&'a nsAString> for Atom {
    #[inline]
    fn from(string: &nsAString) -> Atom {
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize16(string)
            ))
        }
    }
}

impl<'a> From<Cow<'a, str>> for Atom {
    #[inline]
    fn from(string: Cow<'a, str>) -> Atom {
        Atom::from(&*string)
    }
}

impl From<String> for Atom {
    #[inline]
    fn from(string: String) -> Atom {
        Atom::from(&*string)
    }
}

impl From<*mut nsIAtom> for Atom {
    #[inline]
    fn from(ptr: *mut nsIAtom) -> Atom {
        debug_assert!(!ptr.is_null());
        unsafe {
            let ret = Atom(WeakAtom::new(ptr));
            if !ret.is_static() {
                Gecko_AddRefAtom(ptr);
            }
            ret
        }
    }
}
<commit_msg>Auto merge of #18302 - upsuper:atom-assert, r=bholley<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/. */

#![allow(unsafe_code)]

//! A drop-in replacement for string_cache, but backed by Gecko `nsIAtom`s.

use gecko_bindings::bindings::Gecko_AddRefAtom;
use gecko_bindings::bindings::Gecko_Atomize;
use gecko_bindings::bindings::Gecko_Atomize16;
use gecko_bindings::bindings::Gecko_ReleaseAtom;
use gecko_bindings::structs::{nsIAtom, nsIAtom_AtomKind};
use nsstring::{nsAString, nsString};
use precomputed_hash::PrecomputedHash;
use std::ascii::AsciiExt;
use std::borrow::{Cow, Borrow};
use std::char::{self, DecodeUtf16};
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::iter::Cloned;
use std::mem;
use std::ops::Deref;
use std::slice;

#[macro_use]
#[allow(improper_ctypes, non_camel_case_types, missing_docs)]
pub mod atom_macro {
    include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs"));
}

#[macro_use]
pub mod namespace;

pub use self::namespace::{Namespace, WeakNamespace};

macro_rules! local_name {
    ($s: tt) => { atom!($s) }
}

/// A strong reference to a Gecko atom.
#[derive(Eq, PartialEq)]
pub struct Atom(*mut WeakAtom);

/// An atom *without* a strong reference.
///
/// Only usable as `&'a WeakAtom`,
/// where `'a` is the lifetime of something that holds a strong reference to that atom.
pub struct WeakAtom(nsIAtom);

/// A BorrowedAtom for Gecko is just a weak reference to a `nsIAtom`, that
/// hasn't been bumped.
pub type BorrowedAtom<'a> = &'a WeakAtom;

impl Deref for Atom {
    type Target = WeakAtom;

    #[inline]
    fn deref(&self) -> &WeakAtom {
        unsafe {
            &*self.0
        }
    }
}

impl PrecomputedHash for Atom {
    #[inline]
    fn precomputed_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl Borrow<WeakAtom> for Atom {
    #[inline]
    fn borrow(&self) -> &WeakAtom {
        self
    }
}

impl Eq for WeakAtom {}
impl PartialEq for WeakAtom {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        let weak: *const WeakAtom = self;
        let other: *const WeakAtom = other;
        weak == other
    }
}

unsafe impl Send for Atom {}
unsafe impl Sync for Atom {}
unsafe impl Sync for WeakAtom {}

impl WeakAtom {
    /// Construct a `WeakAtom` from a raw `nsIAtom`.
    #[inline]
    pub unsafe fn new<'a>(atom: *const nsIAtom) -> &'a mut Self {
        &mut *(atom as *mut WeakAtom)
    }

    /// Clone this atom, bumping the refcount if the atom is not static.
    #[inline]
    pub fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }

    /// Get the atom hash.
    #[inline]
    pub fn get_hash(&self) -> u32 {
        self.0.mHash
    }

    /// Get the atom as a slice of utf-16 chars.
    #[inline]
    pub fn as_slice(&self) -> &[u16] {
        unsafe {
            slice::from_raw_parts((*self.as_ptr()).mString, self.len() as usize)
        }
    }

    // NOTE: don't expose this, since it's slow, and easy to be misused.
    fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
        char::decode_utf16(self.as_slice().iter().cloned())
    }

    /// Execute `cb` with the string that this atom represents.
    ///
    /// Find alternatives to this function when possible, please, since it's
    /// pretty slow.
    pub fn with_str<F, Output>(&self, cb: F) -> Output
        where F: FnOnce(&str) -> Output
    {
        // FIXME(bholley): We should measure whether it makes more sense to
        // cache the UTF-8 version in the Gecko atom table somehow.
        let owned = self.to_string();
        cb(&owned)
    }

    /// Convert this Atom into a string, decoding the UTF-16 bytes.
    ///
    /// Find alternatives to this function when possible, please, since it's
    /// pretty slow.
    #[inline]
    pub fn to_string(&self) -> String {
        String::from_utf16(self.as_slice()).unwrap()
    }

    /// Returns whether this atom is static.
    #[inline]
    pub fn is_static(&self) -> bool {
        unsafe {
            (*self.as_ptr()).mKind() == nsIAtom_AtomKind::StaticAtom as u32
        }
    }

    /// Returns the length of the atom string.
    #[inline]
    pub fn len(&self) -> u32 {
        unsafe {
            (*self.as_ptr()).mLength()
        }
    }

    /// Returns whether this atom is the empty string.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the atom as a mutable pointer.
    #[inline]
    pub fn as_ptr(&self) -> *mut nsIAtom {
        let const_ptr: *const nsIAtom = &self.0;
        const_ptr as *mut nsIAtom
    }

    /// Convert this atom to ASCII lower-case
    pub fn to_ascii_lowercase(&self) -> Atom {
        let slice = self.as_slice();
        match slice.iter().position(|&char16| (b'A' as u16) <= char16 && char16 <= (b'Z' as u16)) {
            None => self.clone(),
            Some(i) => {
                let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
                let mut vec;
                let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
                    buffer_prefix.copy_from_slice(slice);
                    buffer_prefix
                } else {
                    vec = slice.to_vec();
                    &mut vec
                };
                for char16 in &mut mutable_slice[i..] {
                    if *char16 <= 0x7F {
                        *char16 = (*char16 as u8).to_ascii_lowercase() as u16
                    }
                }
                Atom::from(&*mutable_slice)
            }
        }
    }

    /// Return whether two atoms are ASCII-case-insensitive matches
    pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        if self == other {
            return true;
        }

        let a = self.as_slice();
        let b = other.as_slice();
        a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
            if a16 <= 0x7F && b16 <= 0x7F {
                (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
            } else {
                a16 == b16
            }
        })
    }

    /// Return whether this atom is an ASCII-case-insensitive match for the given string
    pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
        self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
        .eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
    }
}

impl fmt::Debug for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko WeakAtom({:p}, {})", self, self)
    }
}

impl fmt::Display for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        for c in self.chars() {
            w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))?
        }
        Ok(())
    }
}

impl Atom {
    /// Execute a callback with the atom represented by `ptr`.
    pub unsafe fn with<F, R>(ptr: *mut nsIAtom, callback: F) -> R where F: FnOnce(&Atom) -> R {
        let atom = Atom(WeakAtom::new(ptr));
        let ret = callback(&atom);
        mem::forget(atom);
        ret
    }

    /// Creates an atom from an static atom pointer without checking in release
    /// builds.
    ///
    /// Right now it's only used by the atom macro, and ideally it should keep
    /// that way, now we have sugar for is_static, creating atoms using
    /// Atom::from should involve almost no overhead.
    #[inline]
    unsafe fn from_static(ptr: *mut nsIAtom) -> Self {
        let atom = Atom(ptr as *mut WeakAtom);
        debug_assert!(atom.is_static(),
                      "Called from_static for a non-static atom!");
        atom
    }

    /// Creates an atom from a dynamic atom pointer that has already had AddRef
    /// called on it.
    #[inline]
    pub unsafe fn from_addrefed(ptr: *mut nsIAtom) -> Self {
        assert!(!ptr.is_null());
        unsafe {
            Atom(WeakAtom::new(ptr))
        }
    }

    /// Convert this atom into an addrefed nsIAtom pointer.
    #[inline]
    pub fn into_addrefed(self) -> *mut nsIAtom {
        let ptr = self.as_ptr();
        mem::forget(self);
        ptr
    }
}

impl Hash for Atom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Hash for WeakAtom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Clone for Atom {
    #[inline(always)]
    fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }
}

impl Drop for Atom {
    #[inline]
    fn drop(&mut self) {
        if !self.is_static() {
            unsafe {
                Gecko_ReleaseAtom(self.as_ptr());
            }
        }
    }
}

impl Default for Atom {
    #[inline]
    fn default() -> Self {
        atom!("")
    }
}

impl fmt::Debug for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko Atom({:p}, {})", self.0, self)
    }
}

impl fmt::Display for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            (&*self.0).fmt(w)
        }
    }
}

impl<'a> From<&'a str> for Atom {
    #[inline]
    fn from(string: &str) -> Atom {
        debug_assert!(string.len() <= u32::max_value() as usize);
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize(string.as_ptr() as *const _, string.len() as u32)
            ))
        }
    }
}

impl<'a> From<&'a [u16]> for Atom {
    #[inline]
    fn from(slice: &[u16]) -> Atom {
        Atom::from(&*nsString::from(slice))
    }
}

impl<'a> From<&'a nsAString> for Atom {
    #[inline]
    fn from(string: &nsAString) -> Atom {
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize16(string)
            ))
        }
    }
}

impl<'a> From<Cow<'a, str>> for Atom {
    #[inline]
    fn from(string: Cow<'a, str>) -> Atom {
        Atom::from(&*string)
    }
}

impl From<String> for Atom {
    #[inline]
    fn from(string: String) -> Atom {
        Atom::from(&*string)
    }
}

impl From<*mut nsIAtom> for Atom {
    #[inline]
    fn from(ptr: *mut nsIAtom) -> Atom {
        assert!(!ptr.is_null());
        unsafe {
            let ret = Atom(WeakAtom::new(ptr));
            if !ret.is_static() {
                Gecko_AddRefAtom(ptr);
            }
            ret
        }
    }
}
<|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/. */

//! Specified color values.

use cssparser::{Color as CSSParserColor, Parser, RGBA, Token, BasicParseError};
#[cfg(feature = "gecko")]
use gecko_bindings::structs::nscolor;
use itoa;
use parser::{ParserContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::color::SystemColor;
use std::fmt;
use std::io::Write;
use style_traits::{ToCss, ParseError, StyleParseError};
use super::AllowQuirks;
use values::computed::{Color as ComputedColor, Context, ToComputedValue};

/// Specified color value
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Color {
    /// The 'currentColor' keyword
    CurrentColor,
    /// A specific RGBA color
    Numeric {
        /// Parsed RGBA color
        parsed: RGBA,
        /// Authored representation
        authored: Option<Box<str>>,
    },
    /// A complex color value from computed value
    Complex(ComputedColor),

    /// A system color
    #[cfg(feature = "gecko")]
    System(SystemColor),
    /// A special color keyword value used in Gecko
    #[cfg(feature = "gecko")]
    Special(gecko::SpecialColorKeyword),
    /// Quirksmode-only rule for inheriting color from the body
    #[cfg(feature = "gecko")]
    InheritFromBodyQuirk,
}

no_viewport_percentage!(Color);

#[cfg(feature = "gecko")]
mod gecko {
    use style_traits::ToCss;

    define_css_keyword_enum! { SpecialColorKeyword:
        "-moz-default-color" => MozDefaultColor,
        "-moz-default-background-color" => MozDefaultBackgroundColor,
        "-moz-hyperlinktext" => MozHyperlinktext,
        "-moz-activehyperlinktext" => MozActiveHyperlinktext,
        "-moz-visitedhyperlinktext" => MozVisitedHyperlinktext,
    }
}

impl From<RGBA> for Color {
    fn from(value: RGBA) -> Self {
        Color::rgba(value)
    }
}

impl Parse for Color {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        // Currently we only store authored value for color keywords,
        // because all browsers serialize those values as keywords for
        // specified value.
        let start_position = input.position();
        let authored = match input.next() {
            Ok(Token::Ident(s)) => Some(s.to_lowercase().into_boxed_str()),
            _ => None,
        };
        input.reset(start_position);
        if let Ok(value) = input.try(CSSParserColor::parse) {
            Ok(match value {
                CSSParserColor::CurrentColor => Color::CurrentColor,
                CSSParserColor::RGBA(rgba) => Color::Numeric {
                    parsed: rgba,
                    authored: authored,
                },
            })
        } else {
            #[cfg(feature = "gecko")] {
                if let Ok(system) = input.try(SystemColor::parse) {
                    Ok(Color::System(system))
                } else {
                    gecko::SpecialColorKeyword::parse(input).map(Color::Special)
                }
            }
            #[cfg(not(feature = "gecko"))] {
                Err(StyleParseError::UnspecifiedError.into())
            }
        }
    }
}

impl ToCss for Color {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
            Color::Numeric { authored: Some(ref authored), .. } => dest.write_str(authored),
            Color::Numeric { parsed: ref rgba, .. } => rgba.to_css(dest),
            Color::Complex(_) => Ok(()),
            #[cfg(feature = "gecko")]
            Color::System(system) => system.to_css(dest),
            #[cfg(feature = "gecko")]
            Color::Special(special) => special.to_css(dest),
            #[cfg(feature = "gecko")]
            Color::InheritFromBodyQuirk => Ok(()),
        }
    }
}

/// A wrapper of cssparser::Color::parse_hash.
///
/// That function should never return CurrentColor, so it makes no sense
/// to handle a cssparser::Color here. This should really be done in
/// cssparser directly rather than here.
fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
    CSSParserColor::parse_hash(value).map(|color| {
        match color {
            CSSParserColor::RGBA(rgba) => rgba,
            CSSParserColor::CurrentColor =>
                unreachable!("parse_hash should never return currentcolor"),
        }
    })
}

impl Color {
    /// Returns currentcolor value.
    #[inline]
    pub fn currentcolor() -> Color {
        Color::CurrentColor
    }

    /// Returns transparent value.
    #[inline]
    pub fn transparent() -> Color {
        // We should probably set authored to "transparent", but maybe it doesn't matter.
        Color::rgba(RGBA::transparent())
    }

    /// Returns a numeric RGBA color value.
    #[inline]
    pub fn rgba(rgba: RGBA) -> Self {
        Color::Numeric {
            parsed: rgba,
            authored: None,
        }
    }

    /// Parse a color, with quirks.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    pub fn parse_quirky<'i, 't>(context: &ParserContext,
                                input: &mut Parser<'i, 't>,
                                allow_quirks: AllowQuirks)
                                -> Result<Self, ParseError<'i>> {
        input.try(|i| Self::parse(context, i)).or_else(|_| {
            if !allow_quirks.allowed(context.quirks_mode) {
                return Err(StyleParseError::UnspecifiedError.into());
            }
            Color::parse_quirky_color(input).map(|rgba| Color::rgba(rgba))
        })
    }

    /// Parse a <quirky-color> value.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<RGBA, ParseError<'i>> {
        let (value, unit) = match input.next()? {
            Token::Number { int_value: Some(integer), .. } => {
                (integer, None)
            },
            Token::Dimension { int_value: Some(integer), unit, .. } => {
                (integer, Some(unit))
            },
            Token::Ident(ident) => {
                if ident.len() != 3 && ident.len() != 6 {
                    return Err(StyleParseError::UnspecifiedError.into());
                }
                return parse_hash_color(ident.as_bytes())
                    .map_err(|()| StyleParseError::UnspecifiedError.into());
            }
            t => {
                return Err(BasicParseError::UnexpectedToken(t).into());
            },
        };
        if value < 0 {
            return Err(StyleParseError::UnspecifiedError.into());
        }
        let length = if value <= 9 {
            1
        } else if value <= 99 {
            2
        } else if value <= 999 {
            3
        } else if value <= 9999 {
            4
        } else if value <= 99999 {
            5
        } else if value <= 999999 {
            6
        } else {
            return Err(StyleParseError::UnspecifiedError.into())
        };
        let total = length + unit.as_ref().map_or(0, |d| d.len());
        if total > 6 {
            return Err(StyleParseError::UnspecifiedError.into());
        }
        let mut serialization = [b'0'; 6];
        let space_padding = 6 - total;
        let mut written = space_padding;
        written += itoa::write(&mut serialization[written..], value).unwrap();
        if let Some(unit) = unit {
            written += (&mut serialization[written..]).write(unit.as_bytes()).unwrap();
        }
        debug_assert!(written == 6);
        parse_hash_color(&serialization).map_err(|()| StyleParseError::UnspecifiedError.into())
    }

    /// Returns false if the color is completely transparent, and
    /// true otherwise.
    pub fn is_non_transparent(&self) -> bool {
        match *self {
            Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
            _ => true,
        }
    }
}

#[cfg(feature = "gecko")]
fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
    use gecko::values::convert_nscolor_to_rgba;
    ComputedColor::rgba(convert_nscolor_to_rgba(color))
}

impl ToComputedValue for Color {
    type ComputedValue = ComputedColor;

    fn to_computed_value(&self, _context: &Context) -> ComputedColor {
        match *self {
            Color::CurrentColor => ComputedColor::currentcolor(),
            Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
            Color::Complex(ref complex) => *complex,
            #[cfg(feature = "gecko")]
            Color::System(system) =>
                convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
            #[cfg(feature = "gecko")]
            Color::Special(special) => {
                use self::gecko::SpecialColorKeyword as Keyword;
                let pres_context = _context.device().pres_context();
                convert_nscolor_to_computedcolor(match special {
                    Keyword::MozDefaultColor => pres_context.mDefaultColor,
                    Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                    Keyword::MozHyperlinktext => pres_context.mLinkColor,
                    Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
                    Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
                })
            }
            #[cfg(feature = "gecko")]
            Color::InheritFromBodyQuirk => {
                use dom::TElement;
                use gecko::wrapper::GeckoElement;
                use gecko_bindings::bindings::Gecko_GetBody;
                let pres_context = _context.device().pres_context();
                let body = unsafe {
                    Gecko_GetBody(pres_context)
                };
                if let Some(body) = body {
                    let wrap = GeckoElement(body);
                    let borrow = wrap.borrow_data();
                    ComputedColor::rgba(borrow.as_ref().unwrap()
                                              .styles.primary()
                                              .get_color()
                                              .clone_color())
                } else {
                    convert_nscolor_to_computedcolor(pres_context.mDefaultColor)
                }
            },
        }
    }

    fn from_computed_value(computed: &ComputedColor) -> Self {
        if computed.is_numeric() {
            Color::rgba(computed.color)
        } else if computed.is_currentcolor() {
            Color::currentcolor()
        } else {
            Color::Complex(*computed)
        }
    }
}

/// Specified color value, but resolved to just RGBA for computed value
/// with value from color property at the same context.
#[derive(Clone, Debug, PartialEq, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct RGBAColor(pub Color);

no_viewport_percentage!(RGBAColor);

impl Parse for RGBAColor {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        Color::parse(context, input).map(RGBAColor)
    }
}

impl ToComputedValue for RGBAColor {
    type ComputedValue = RGBA;

    fn to_computed_value(&self, context: &Context) -> RGBA {
        self.0.to_computed_value(context)
            .to_rgba(context.style().get_color().clone_color())
    }

    fn from_computed_value(computed: &RGBA) -> Self {
        RGBAColor(Color::rgba(*computed))
    }
}

impl From<Color> for RGBAColor {
    fn from(color: Color) -> RGBAColor {
        RGBAColor(color)
    }
}
<commit_msg>Auto merge of #17799 - upsuper:body-color-panic, r=heycam<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/. */

//! Specified color values.

use cssparser::{Color as CSSParserColor, Parser, RGBA, Token, BasicParseError};
#[cfg(feature = "gecko")]
use gecko_bindings::structs::nscolor;
use itoa;
use parser::{ParserContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::color::SystemColor;
use std::fmt;
use std::io::Write;
use style_traits::{ToCss, ParseError, StyleParseError};
use super::AllowQuirks;
use values::computed::{Color as ComputedColor, Context, ToComputedValue};

/// Specified color value
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Color {
    /// The 'currentColor' keyword
    CurrentColor,
    /// A specific RGBA color
    Numeric {
        /// Parsed RGBA color
        parsed: RGBA,
        /// Authored representation
        authored: Option<Box<str>>,
    },
    /// A complex color value from computed value
    Complex(ComputedColor),

    /// A system color
    #[cfg(feature = "gecko")]
    System(SystemColor),
    /// A special color keyword value used in Gecko
    #[cfg(feature = "gecko")]
    Special(gecko::SpecialColorKeyword),
    /// Quirksmode-only rule for inheriting color from the body
    #[cfg(feature = "gecko")]
    InheritFromBodyQuirk,
}

no_viewport_percentage!(Color);

#[cfg(feature = "gecko")]
mod gecko {
    use style_traits::ToCss;

    define_css_keyword_enum! { SpecialColorKeyword:
        "-moz-default-color" => MozDefaultColor,
        "-moz-default-background-color" => MozDefaultBackgroundColor,
        "-moz-hyperlinktext" => MozHyperlinktext,
        "-moz-activehyperlinktext" => MozActiveHyperlinktext,
        "-moz-visitedhyperlinktext" => MozVisitedHyperlinktext,
    }
}

impl From<RGBA> for Color {
    fn from(value: RGBA) -> Self {
        Color::rgba(value)
    }
}

impl Parse for Color {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        // Currently we only store authored value for color keywords,
        // because all browsers serialize those values as keywords for
        // specified value.
        let start_position = input.position();
        let authored = match input.next() {
            Ok(Token::Ident(s)) => Some(s.to_lowercase().into_boxed_str()),
            _ => None,
        };
        input.reset(start_position);
        if let Ok(value) = input.try(CSSParserColor::parse) {
            Ok(match value {
                CSSParserColor::CurrentColor => Color::CurrentColor,
                CSSParserColor::RGBA(rgba) => Color::Numeric {
                    parsed: rgba,
                    authored: authored,
                },
            })
        } else {
            #[cfg(feature = "gecko")] {
                if let Ok(system) = input.try(SystemColor::parse) {
                    Ok(Color::System(system))
                } else {
                    gecko::SpecialColorKeyword::parse(input).map(Color::Special)
                }
            }
            #[cfg(not(feature = "gecko"))] {
                Err(StyleParseError::UnspecifiedError.into())
            }
        }
    }
}

impl ToCss for Color {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
            Color::Numeric { authored: Some(ref authored), .. } => dest.write_str(authored),
            Color::Numeric { parsed: ref rgba, .. } => rgba.to_css(dest),
            Color::Complex(_) => Ok(()),
            #[cfg(feature = "gecko")]
            Color::System(system) => system.to_css(dest),
            #[cfg(feature = "gecko")]
            Color::Special(special) => special.to_css(dest),
            #[cfg(feature = "gecko")]
            Color::InheritFromBodyQuirk => Ok(()),
        }
    }
}

/// A wrapper of cssparser::Color::parse_hash.
///
/// That function should never return CurrentColor, so it makes no sense
/// to handle a cssparser::Color here. This should really be done in
/// cssparser directly rather than here.
fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
    CSSParserColor::parse_hash(value).map(|color| {
        match color {
            CSSParserColor::RGBA(rgba) => rgba,
            CSSParserColor::CurrentColor =>
                unreachable!("parse_hash should never return currentcolor"),
        }
    })
}

impl Color {
    /// Returns currentcolor value.
    #[inline]
    pub fn currentcolor() -> Color {
        Color::CurrentColor
    }

    /// Returns transparent value.
    #[inline]
    pub fn transparent() -> Color {
        // We should probably set authored to "transparent", but maybe it doesn't matter.
        Color::rgba(RGBA::transparent())
    }

    /// Returns a numeric RGBA color value.
    #[inline]
    pub fn rgba(rgba: RGBA) -> Self {
        Color::Numeric {
            parsed: rgba,
            authored: None,
        }
    }

    /// Parse a color, with quirks.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    pub fn parse_quirky<'i, 't>(context: &ParserContext,
                                input: &mut Parser<'i, 't>,
                                allow_quirks: AllowQuirks)
                                -> Result<Self, ParseError<'i>> {
        input.try(|i| Self::parse(context, i)).or_else(|_| {
            if !allow_quirks.allowed(context.quirks_mode) {
                return Err(StyleParseError::UnspecifiedError.into());
            }
            Color::parse_quirky_color(input).map(|rgba| Color::rgba(rgba))
        })
    }

    /// Parse a <quirky-color> value.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<RGBA, ParseError<'i>> {
        let (value, unit) = match input.next()? {
            Token::Number { int_value: Some(integer), .. } => {
                (integer, None)
            },
            Token::Dimension { int_value: Some(integer), unit, .. } => {
                (integer, Some(unit))
            },
            Token::Ident(ident) => {
                if ident.len() != 3 && ident.len() != 6 {
                    return Err(StyleParseError::UnspecifiedError.into());
                }
                return parse_hash_color(ident.as_bytes())
                    .map_err(|()| StyleParseError::UnspecifiedError.into());
            }
            t => {
                return Err(BasicParseError::UnexpectedToken(t).into());
            },
        };
        if value < 0 {
            return Err(StyleParseError::UnspecifiedError.into());
        }
        let length = if value <= 9 {
            1
        } else if value <= 99 {
            2
        } else if value <= 999 {
            3
        } else if value <= 9999 {
            4
        } else if value <= 99999 {
            5
        } else if value <= 999999 {
            6
        } else {
            return Err(StyleParseError::UnspecifiedError.into())
        };
        let total = length + unit.as_ref().map_or(0, |d| d.len());
        if total > 6 {
            return Err(StyleParseError::UnspecifiedError.into());
        }
        let mut serialization = [b'0'; 6];
        let space_padding = 6 - total;
        let mut written = space_padding;
        written += itoa::write(&mut serialization[written..], value).unwrap();
        if let Some(unit) = unit {
            written += (&mut serialization[written..]).write(unit.as_bytes()).unwrap();
        }
        debug_assert!(written == 6);
        parse_hash_color(&serialization).map_err(|()| StyleParseError::UnspecifiedError.into())
    }

    /// Returns false if the color is completely transparent, and
    /// true otherwise.
    pub fn is_non_transparent(&self) -> bool {
        match *self {
            Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
            _ => true,
        }
    }
}

#[cfg(feature = "gecko")]
fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
    use gecko::values::convert_nscolor_to_rgba;
    ComputedColor::rgba(convert_nscolor_to_rgba(color))
}

impl ToComputedValue for Color {
    type ComputedValue = ComputedColor;

    fn to_computed_value(&self, _context: &Context) -> ComputedColor {
        match *self {
            Color::CurrentColor => ComputedColor::currentcolor(),
            Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
            Color::Complex(ref complex) => *complex,
            #[cfg(feature = "gecko")]
            Color::System(system) =>
                convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
            #[cfg(feature = "gecko")]
            Color::Special(special) => {
                use self::gecko::SpecialColorKeyword as Keyword;
                let pres_context = _context.device().pres_context();
                convert_nscolor_to_computedcolor(match special {
                    Keyword::MozDefaultColor => pres_context.mDefaultColor,
                    Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                    Keyword::MozHyperlinktext => pres_context.mLinkColor,
                    Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
                    Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
                })
            }
            #[cfg(feature = "gecko")]
            Color::InheritFromBodyQuirk => {
                use dom::TElement;
                use gecko::wrapper::GeckoElement;
                use gecko_bindings::bindings::Gecko_GetBody;
                let pres_context = _context.device().pres_context();
                let body = unsafe { Gecko_GetBody(pres_context) }.map(GeckoElement);
                let data = body.as_ref().and_then(|wrap| wrap.borrow_data());
                if let Some(data) = data {
                    ComputedColor::rgba(data.styles.primary()
                                            .get_color()
                                            .clone_color())
                } else {
                    convert_nscolor_to_computedcolor(pres_context.mDefaultColor)
                }
            },
        }
    }

    fn from_computed_value(computed: &ComputedColor) -> Self {
        if computed.is_numeric() {
            Color::rgba(computed.color)
        } else if computed.is_currentcolor() {
            Color::currentcolor()
        } else {
            Color::Complex(*computed)
        }
    }
}

/// Specified color value, but resolved to just RGBA for computed value
/// with value from color property at the same context.
#[derive(Clone, Debug, PartialEq, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct RGBAColor(pub Color);

no_viewport_percentage!(RGBAColor);

impl Parse for RGBAColor {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        Color::parse(context, input).map(RGBAColor)
    }
}

impl ToComputedValue for RGBAColor {
    type ComputedValue = RGBA;

    fn to_computed_value(&self, context: &Context) -> RGBA {
        self.0.to_computed_value(context)
            .to_rgba(context.style().get_color().clone_color())
    }

    fn from_computed_value(computed: &RGBA) -> Self {
        RGBAColor(Color::rgba(*computed))
    }
}

impl From<Color> for RGBAColor {
    fn from(color: Color) -> RGBAColor {
        RGBAColor(color)
    }
}
<|endoftext|>