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

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = if pseudo.supports_user_action_state() {
            input.try(|input| {
                let mut state = ElementState::empty();

                while !input.is_exhausted() {
                    input.expect_colon()?;
                    let ident = input.expect_ident()?;
                    let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                    if !pseudo_class.is_safe_user_action_state() {
                        return Err(())
                    }
                    state.insert(pseudo_class.state_flag());
                }

                Ok(state)
            }).ok()
        } else {
            None
        };

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<commit_msg>Auto merge of #16875 - bzbarsky:dir-no-revalidation, r=emilio<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/. */

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        // :dir() depends on state only, but doesn't use state_flag because its
        // semantics don't quite match.  Nevertheless, it doesn't need cache
        // revalidation, because we already compare states for elements and
        // candidates.
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_) | NonTSPseudoClass::Dir(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = if pseudo.supports_user_action_state() {
            input.try(|input| {
                let mut state = ElementState::empty();

                while !input.is_exhausted() {
                    input.expect_colon()?;
                    let ident = input.expect_ident()?;
                    let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                    if !pseudo_class.is_safe_user_action_state() {
                        return Err(())
                    }
                    state.insert(pseudo_class.state_flag());
                }

                Ok(state)
            }).ok()
        } else {
            None
        };

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<|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/. */

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = if pseudo.supports_user_action_state() {
            input.try(|input| {
                let mut state = ElementState::empty();

                while !input.is_exhausted() {
                    input.expect_colon()?;
                    let ident = input.expect_ident()?;
                    let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                    if !pseudo_class.is_safe_user_action_state() {
                        return Err(())
                    }
                    state.insert(pseudo_class.state_flag());
                }

                Ok(state)
            }).ok()
        } else {
            None
        };

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<commit_msg>Don't require style sharing cache invalidation for :dir<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/. */

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        // :dir() depends on state only, but doesn't use state_flag because its
        // semantics don't quite match.  Nevertheless, it doesn't need cache
        // revalidation, because we already compare states for elements and
        // candidates.
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_) | NonTSPseudoClass::Dir(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = if pseudo.supports_user_action_state() {
            input.try(|input| {
                let mut state = ElementState::empty();

                while !input.is_exhausted() {
                    input.expect_colon()?;
                    let ident = input.expect_ident()?;
                    let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                    if !pseudo_class.is_safe_user_action_state() {
                        return Err(())
                    }
                    state.insert(pseudo_class.state_flag());
                }

                Ok(state)
            }).ok()
        } else {
            None
        };

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<|endoftext|>


<commit_before>extern crate multirust_dist;
#[macro_use]
extern crate multirust_utils;

#[macro_use]
extern crate clap;
extern crate regex;
extern crate hyper;
#[macro_use]
extern crate multirust;
extern crate term;
extern crate openssl;
extern crate itertools;
extern crate time;
extern crate rand;
#[macro_use]
extern crate scopeguard;
extern crate tempdir;

#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate winreg;
#[cfg(windows)]
extern crate user32;
#[cfg(windows)]
extern crate kernel32;
extern crate libc;

#[macro_use]
mod log;
mod cli;
mod common;
mod download_tracker;
mod multirust_mode;
mod proxy_mode;
mod setup_mode;
mod self_update;
mod tty;

use std::env;
use std::path::PathBuf;
use multirust::{Error, Result};

fn main() {
    if let Err(e) = run_multirust() {
        err!("{}", e);
        std::process::exit(1);
    }
}

fn run_multirust() -> Result<()> {
    // Guard against infinite recursion
    let recursion_count = env::var("RUST_RECURSION_COUNT").ok()
        .and_then(|s| s.parse().ok()).unwrap_or(0);
    if recursion_count > 5 {
        return Err(Error::InfiniteRecursion);
    }

    // The name of arg0 determines how the program is going to behave
    let arg0 = env::args().next().map(|a| PathBuf::from(a));
    let name = arg0.as_ref()
        .and_then(|a| a.file_stem())
        .and_then(|a| a.to_str());
    match name {
        Some("multirust") => {
            multirust_mode::main()
        }
        Some("multirust-setup") => {
            setup_mode::main()
        }
        Some(n) if n.starts_with("multirust-gc-") => {
            // This is the final uninstallation stage on windows where
            // multirust deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(n) if n.starts_with("multirust-") => {
            // This is for compatibility with previous revisions of
            // multirust-rs self-update, which expect multirust-rs to
            // be available on the server, downloads it to
            // ~/.multirust/tmp/multirust-$random, and execute it with
            // `self install` as the arguments.  FIXME: Verify this
            // works.
            self_update::install(false, false)
        }
        Some(_) => {
            proxy_mode::main()
        }
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(Error::NoExeName)
        }
    }
}

<commit_msg>Don't prompt on upgrade from multirust-rs<commit_after>extern crate multirust_dist;
#[macro_use]
extern crate multirust_utils;

#[macro_use]
extern crate clap;
extern crate regex;
extern crate hyper;
#[macro_use]
extern crate multirust;
extern crate term;
extern crate openssl;
extern crate itertools;
extern crate time;
extern crate rand;
#[macro_use]
extern crate scopeguard;
extern crate tempdir;

#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate winreg;
#[cfg(windows)]
extern crate user32;
#[cfg(windows)]
extern crate kernel32;
extern crate libc;

#[macro_use]
mod log;
mod cli;
mod common;
mod download_tracker;
mod multirust_mode;
mod proxy_mode;
mod setup_mode;
mod self_update;
mod tty;

use std::env;
use std::path::PathBuf;
use multirust::{Error, Result};

fn main() {
    if let Err(e) = run_multirust() {
        err!("{}", e);
        std::process::exit(1);
    }
}

fn run_multirust() -> Result<()> {
    // Guard against infinite recursion
    let recursion_count = env::var("RUST_RECURSION_COUNT").ok()
        .and_then(|s| s.parse().ok()).unwrap_or(0);
    if recursion_count > 5 {
        return Err(Error::InfiniteRecursion);
    }

    // The name of arg0 determines how the program is going to behave
    let arg0 = env::args().next().map(|a| PathBuf::from(a));
    let name = arg0.as_ref()
        .and_then(|a| a.file_stem())
        .and_then(|a| a.to_str());
    match name {
        Some("multirust") => {
            multirust_mode::main()
        }
        Some("multirust-setup") => {
            setup_mode::main()
        }
        Some(n) if n.starts_with("multirust-gc-") => {
            // This is the final uninstallation stage on windows where
            // multirust deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(n) if n.starts_with("multirust-") => {
            // This is for compatibility with previous revisions of
            // multirust-rs self-update, which expect multirust-rs to
            // be available on the server, downloads it to
            // ~/.multirust/tmp/multirust-$random, and execute it with
            // `self install` as the arguments.  FIXME: Verify this
            // works.
            self_update::install(true, false)
        }
        Some(_) => {
            proxy_mode::main()
        }
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(Error::NoExeName)
        }
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright 2015 Ben Ashford
 * Copyright 2015 Astro
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of ElasticSearch Analyze operation

use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};

use ::Client;
use ::error::EsError;

pub struct AnalyzeOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client:   &'a mut Client,

    body:     &'b str,
    index:    Option<&'b str>,
    analyzer: Option<&'b str>
}

impl<'a, 'b> AnalyzeOperation<'a, 'b> {
    pub fn new(client: &'a mut Client, body: &'b str) -> AnalyzeOperation<'a, 'b> {
        AnalyzeOperation {
            client:   client,
            body:     body,
            index:    None,
            analyzer: None
        }
    }

    pub fn with_index(&'b mut self, index: &'b str) -> &'b mut Self {
        self.index = Some(index);
        self
    }

    pub fn with_analyzer(&'b mut self, analyzer: &'b str) -> &'b mut Self {
        self.analyzer = Some(analyzer);
        self
    }

    pub fn send(&'b mut self) -> Result<AnalyzeResult, EsError> {
        let mut url = match self.index {
            None => "/_analyze".to_owned(),
            Some(index) => format!("{}/_analyze", index)
        };
        match self.analyzer {
            None => (),
            Some(analyzer) => {
                url.push_str(&format!("?analyzer={}", analyzer))
            }
        }
        let (_, result) = try!(self.client.post_body_op(&url, &self.body));
        Ok(AnalyzeResult::from(&result.expect("No Json payload")))
    }
}

/// The result of an analyze operation
#[derive(Debug)]
pub struct AnalyzeResult {
    pub tokens: Vec<Token>
}

#[derive(Debug)]
pub struct Token {
    pub token: String,
    pub token_type: String,
    pub position: u64,
    pub start_offset: u64,
    pub end_offset: u64
}

impl<'a> From<&'a Json> for AnalyzeResult {
    fn from(r: &'a Json) -> AnalyzeResult {
        let mut tokens = Vec::new();
        for t in get_json_array!(r, "tokens") {
            tokens.push(Token {
                token: get_json_string!(t, "token"),
                token_type: get_json_string!(t, "type"),
                position: get_json_u64!(t, "position"),
                start_offset: get_json_u64!(t, "start_offset"),
                end_offset: get_json_u64!(t, "end_offset")
            })
        }
        AnalyzeResult {
            tokens: tokens
        }
    }
}
<commit_msg>analyze: avoid json req body by using client.http_client<commit_after>/*
 * Copyright 2015 Ben Ashford
 * Copyright 2015 Astro
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of ElasticSearch Analyze operation

use std::collections::BTreeMap;
use rustc_serialize::json::{Json};

use ::do_req;
use ::Client;
use ::error::EsError;

pub struct AnalyzeOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client:   &'a mut Client,

    body:     &'b str,
    index:    Option<&'b str>,
    analyzer: Option<&'b str>
}

impl<'a, 'b> AnalyzeOperation<'a, 'b> {
    pub fn new(client: &'a mut Client, body: &'b str) -> AnalyzeOperation<'a, 'b> {
        AnalyzeOperation {
            client:   client,
            body:     body,
            index:    None,
            analyzer: None
        }
    }

    pub fn with_index(&'b mut self, index: &'b str) -> &'b mut Self {
        self.index = Some(index);
        self
    }

    pub fn with_analyzer(&'b mut self, analyzer: &'b str) -> &'b mut Self {
        self.analyzer = Some(analyzer);
        self
    }

    pub fn send(&'b mut self) -> Result<AnalyzeResult, EsError> {
        let mut url = match self.index {
            None => "/_analyze".to_owned(),
            Some(index) => format!("{}/_analyze", index)
        };
        match self.analyzer {
            None => (),
            Some(analyzer) => {
                url.push_str(&format!("?analyzer={}", analyzer))
            }
        }
        let client = &self.client;
        let full_url = client.full_url(&url);
        let mut req = try!(client.http_client
                           .post(&full_url)
                           .body(self.body)
                           .send());
        let (_, result) = try!(do_req(&mut req));
        Ok(AnalyzeResult::from(&result.expect("No Json payload")))
    }
}

/// The result of an analyze operation
#[derive(Debug)]
pub struct AnalyzeResult {
    pub tokens: Vec<Token>
}

#[derive(Debug)]
pub struct Token {
    pub token: String,
    pub token_type: String,
    pub position: u64,
    pub start_offset: u64,
    pub end_offset: u64
}

impl<'a> From<&'a Json> for AnalyzeResult {
    fn from(r: &'a Json) -> AnalyzeResult {
        let mut tokens = Vec::new();
        for t in get_json_array!(r, "tokens") {
            tokens.push(Token {
                token: get_json_string!(t, "token"),
                token_type: get_json_string!(t, "type"),
                position: get_json_u64!(t, "position"),
                start_offset: get_json_u64!(t, "start_offset"),
                end_offset: get_json_u64!(t, "end_offset")
            })
        }
        AnalyzeResult {
            tokens: tokens
        }
    }
}
<|endoftext|>


<commit_before>use zeroize::Zeroize;

/// Newtype (pattern) to protect secret credentials stored as Strings.
#[derive(Clone)]
pub struct Secret(String);

impl From<String> for Secret {
    fn from(s: String) -> Self {
        Secret(s)
    }
}

/// Allow dereferencing Secrets as &str.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(Secret::from("hello".to_string()).as_ref(), "hello");
/// ```
impl AsRef<str> for Secret {
    fn as_ref(&self) -> &str {
        return self.0.as_str();
    }
}

impl Drop for Secret {
    fn drop(&mut self) {
        let s = &mut self.0;
        s.zeroize();
    }
}

/// Secrets must not leak, so make sure they are not printed.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(format!("{:?}",Secret::from("hello".to_string())), "\"*******\"");
/// ```
impl std::fmt::Debug for Secret {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "\"*******\"")
    }
}

/// Secrets must not leak, so make sure they are not displayed.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(format!("{}",Secret::from("hello".to_string())), "*******");
/// ```
impl std::fmt::Display for Secret {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "*******")
    }
}
<commit_msg>Fix clippy linting errors in core<commit_after>use zeroize::Zeroize;

/// Newtype (pattern) to protect secret credentials stored as Strings.
#[derive(Clone)]
pub struct Secret(String);

impl From<String> for Secret {
    fn from(s: String) -> Self {
        Secret(s)
    }
}

/// Allow dereferencing Secrets as &str.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(Secret::from("hello".to_string()).as_ref(), "hello");
/// ```
impl AsRef<str> for Secret {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

impl Drop for Secret {
    fn drop(&mut self) {
        let s = &mut self.0;
        s.zeroize();
    }
}

/// Secrets must not leak, so make sure they are not printed.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(format!("{:?}",Secret::from("hello".to_string())), "\"*******\"");
/// ```
impl std::fmt::Debug for Secret {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "\"*******\"")
    }
}

/// Secrets must not leak, so make sure they are not displayed.
///
/// ```rust
/// # use rusoto_credential::Secret;
/// assert_eq!(format!("{}",Secret::from("hello".to_string())), "*******");
/// ```
impl std::fmt::Display for Secret {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "*******")
    }
}
<|endoftext|>


<commit_before>use std::ptr::null;
use libc::{c_char, c_int, c_double};
use std::ffi::CString;
use std::cell::RefCell;
use utils::_string;
use vector::ogr;
use geo;

/// OGR Geometry
pub struct Geometry {
    c_geometry_ref: RefCell<Option<*const ()>>,
    owned: bool,
}

impl Geometry {
    pub unsafe fn lazy_feature_geometry() -> Geometry {
        // Geometry objects created with this method map to a Feature's
        // geometry whose memory is managed by the GDAL feature.
        // This object has a tricky lifecycle:
        //
        // * Initially it's created with a null c_geometry
        // * The first time `Feature::geometry` is called, it gets
        //   c_geometry from GDAL and calls `set_c_geometry` with it.
        // * When the Feature is destroyed, this object is also destroyed,
        //   which is good, because that's when c_geometry (which is managed
        //   by the GDAL feature) becomes invalid. Because `self.owned` is
        //   `true`, we don't call `OGR_G_DestroyGeometry`.
        return Geometry{c_geometry_ref: RefCell::new(None), owned: false};
    }

    pub fn has_gdal_ptr(&self) -> bool {
        return self.c_geometry_ref.borrow().is_some();
    }

    pub unsafe fn set_c_geometry(&self, c_geometry: *const ()) {
        assert!(! self.has_gdal_ptr());
        assert_eq!(self.owned, false);
        *(self.c_geometry_ref.borrow_mut()) = Some(c_geometry);
    }

    unsafe fn with_c_geometry(c_geom: *const(), owned: bool) -> Geometry {
        return Geometry{
            c_geometry_ref: RefCell::new(Some(c_geom)),
            owned: owned,
        };
    }

    pub fn empty(wkb_type: c_int) -> Geometry {
        let c_geom = unsafe { ogr::OGR_G_CreateGeometry(wkb_type) };
        assert!(c_geom != null());
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    /// Create a geometry by parsing a
    /// [WKT](https://en.wikipedia.org/wiki/Well-known_text) string.
    pub fn from_wkt(wkt: &str) -> Geometry {
        let c_wkt = CString::new(wkt.as_bytes()).unwrap();
        let mut c_wkt_ptr: *const c_char = c_wkt.as_ptr();
        let mut c_geom: *const () = null();
        let rv = unsafe { ogr::OGR_G_CreateFromWkt(&mut c_wkt_ptr, null(), &mut c_geom) };
        assert_eq!(rv, ogr::OGRERR_NONE);
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    /// Create a rectangular geometry from West, South, East and North values.
    pub fn bbox(w: f64, s: f64, e: f64, n: f64) -> Geometry {
        Geometry::from_wkt(&format!(
            "POLYGON (({} {}, {} {}, {} {}, {} {}, {} {}))",
            w, n,
            e, n,
            e, s,
            w, s,
            w, n,
        ))
    }

    /// Serialize the geometry as JSON.
    pub fn json(&self) -> String {
        let c_json = unsafe { ogr::OGR_G_ExportToJson(self.c_geometry()) };
        let rv = _string(c_json);
        unsafe { ogr::VSIFree(c_json as *mut ()) };
        return rv;
    }

    /// Serialize the geometry as WKT.
    pub fn wkt(&self) -> String {
        let mut c_wkt: *const c_char = null();
        let _err = unsafe { ogr::OGR_G_ExportToWkt(self.c_geometry(), &mut c_wkt) };
        assert_eq!(_err, ogr::OGRERR_NONE);
        let wkt = _string(c_wkt);
        unsafe { ogr::OGRFree(c_wkt as *mut ()) };
        return wkt;
    }

    pub unsafe fn c_geometry(&self) -> *const () {
        return self.c_geometry_ref.borrow().unwrap();
    }

    pub fn set_point_2d(&mut self, i: usize, p: (f64, f64)) {
        let (x, y) = p;
        unsafe { ogr::OGR_G_SetPoint_2D(
            self.c_geometry(),
            i as c_int,
            x as c_double,
            y as c_double,
        ) };
    }

    pub fn get_point(&self, i: i32) -> (f64, f64, f64) {
        let mut x: c_double = 0.;
        let mut y: c_double = 0.;
        let mut z: c_double = 0.;
        unsafe { ogr::OGR_G_GetPoint(self.c_geometry(), i, &mut x, &mut y, &mut z) };
        return (x as f64, y as f64, z as f64);
    }

    pub fn get_point_vec(&self) -> Vec<(f64, f64, f64)> {
        let length = unsafe{ ogr::OGR_G_GetPointCount(self.c_geometry()) };
        return (0..length).map(|i| self.get_point(i)).collect();
    }

    /// Compute the convex hull of this geometry.
    pub fn convex_hull(&self) -> Geometry {
        let c_geom = unsafe { ogr::OGR_G_ConvexHull(self.c_geometry()) };
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    unsafe fn _get_geometry(&self, n: usize) -> Geometry {
        // get the n-th sub-geometry as a non-owned Geometry; don't keep this
        // object for long.
        let c_geom = ogr::OGR_G_GetGeometryRef(self.c_geometry(), n as c_int);
        return Geometry::with_c_geometry(c_geom, false);
    }
}

impl geo::ToGeo for Geometry {
    fn to_geo(&self) -> geo::Geometry {
        let geometry_type = unsafe { ogr::OGR_G_GetGeometryType(self.c_geometry()) };

        let ring = |n: usize| {
            let ring = unsafe { self._get_geometry(n) };
            return match ring.to_geo() {
                geo::Geometry::LineString(r) => r,
                _ => panic!("Expected to get a LineString")
            };
        };

        match geometry_type {
            ogr::WKB_POINT => {
                let (x, y, _) = self.get_point(0);
                geo::Geometry::Point(geo::Point(geo::Coordinate{x: x, y: y}))
            },
            ogr::WKB_LINESTRING => {
                let coords = self.get_point_vec().iter()
                    .map(|&(x, y, _)| geo::Point(geo::Coordinate{x: x, y: y}))
                    .collect();
                geo::Geometry::LineString(geo::LineString(coords))
            },
            ogr::WKB_POLYGON => {
                let ring_count = unsafe { ogr::OGR_G_GetGeometryCount(self.c_geometry()) } as usize;
                let outer = ring(0);
                let holes = (1..ring_count).map(|n| ring(n)).collect();
                geo::Geometry::Polygon(geo::Polygon(outer, holes))
            },
            _ => panic!("Unknown geometry type")
        }
    }
}

impl Drop for Geometry {
    fn drop(&mut self) {
        if self.owned {
            let c_geometry = self.c_geometry_ref.borrow();
            unsafe { ogr::OGR_G_DestroyGeometry(c_geometry.unwrap() as *mut ()) };
        }
    }
}

pub trait ToGdal {
    fn to_gdal(&self) -> Geometry;
}

impl ToGdal for geo::Point {
    fn to_gdal(&self) -> Geometry {
        let mut geom = Geometry::empty(ogr::WKB_POINT);
        let &geo::Point(coordinate) = self;
        geom.set_point_2d(0, (coordinate.x, coordinate.y));
        return geom;
    }
}

impl ToGdal for geo::LineString {
    fn to_gdal(&self) -> Geometry {
        let mut geom = Geometry::empty(ogr::WKB_LINESTRING);
        let &geo::LineString(ref linestring) = self;
        for (i, &geo::Point(coordinate)) in linestring.iter().enumerate() {
            geom.set_point_2d(i, (coordinate.x, coordinate.y));
        }
        return geom;
    }
}

impl ToGdal for geo::Geometry {
    fn to_gdal(&self) -> Geometry {
        return match *self {
            geo::Geometry::Point(ref c) => c.to_gdal(),
            geo::Geometry::LineString(ref c) => c.to_gdal(),
            _ => panic!("Unknown geometry type")
        }
    }
}

#[cfg(test)]
mod tests {
    use vector::{Geometry, ToGdal};
    use geo;
    use geo::ToGeo;

    #[test]
    fn test_import_export_point() {
        let wkt = "POINT (1 2)";
        let coord = geo::Coordinate{x: 1., y: 2.};
        let geo = geo::Geometry::Point(geo::Point(coord));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
        assert_eq!(geo.to_gdal().wkt(), wkt);
    }

    #[test]
    fn test_import_export_linestring() {
        let wkt = "LINESTRING (0 0,0 1,1 2)";
        let coord = vec!(
            geo::Point(geo::Coordinate{x: 0., y: 0.}),
            geo::Point(geo::Coordinate{x: 0., y: 1.}),
            geo::Point(geo::Coordinate{x: 1., y: 2.}),
        );
        let geo = geo::Geometry::LineString(geo::LineString(coord));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
        assert_eq!(geo.to_gdal().wkt(), wkt);
    }

    fn square(x0: isize, y0: isize, x1: isize, y1: isize) -> geo::LineString {
        geo::LineString(vec!(
            geo::Point(geo::Coordinate{x: x0 as f64, y: y0 as f64}),
            geo::Point(geo::Coordinate{x: x0 as f64, y: y1 as f64}),
            geo::Point(geo::Coordinate{x: x1 as f64, y: y1 as f64}),
            geo::Point(geo::Coordinate{x: x1 as f64, y: y0 as f64}),
            geo::Point(geo::Coordinate{x: x0 as f64, y: y0 as f64}),
        ))
    }

    #[test]
    fn test_import_export_polygon() {
        let wkt = "POLYGON ((0 0,0 5,5 5,5 0,0 0), \
                            (1 1,1 2,2 2,2 1,1 1), \
                            (3 3,3 4,4 4,4 3,3 3))";
        let outer = square(0, 0, 5, 5);
        let holes = vec!(square(1, 1, 2, 2), square(3, 3, 4, 4));
        let geo = geo::Geometry::Polygon(geo::Polygon(outer, holes));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
    }
}
<commit_msg>extract geometry_with_points factory<commit_after>use std::ptr::null;
use libc::{c_char, c_int, c_double};
use std::ffi::CString;
use std::cell::RefCell;
use utils::_string;
use vector::ogr;
use geo;

/// OGR Geometry
pub struct Geometry {
    c_geometry_ref: RefCell<Option<*const ()>>,
    owned: bool,
}

impl Geometry {
    pub unsafe fn lazy_feature_geometry() -> Geometry {
        // Geometry objects created with this method map to a Feature's
        // geometry whose memory is managed by the GDAL feature.
        // This object has a tricky lifecycle:
        //
        // * Initially it's created with a null c_geometry
        // * The first time `Feature::geometry` is called, it gets
        //   c_geometry from GDAL and calls `set_c_geometry` with it.
        // * When the Feature is destroyed, this object is also destroyed,
        //   which is good, because that's when c_geometry (which is managed
        //   by the GDAL feature) becomes invalid. Because `self.owned` is
        //   `true`, we don't call `OGR_G_DestroyGeometry`.
        return Geometry{c_geometry_ref: RefCell::new(None), owned: false};
    }

    pub fn has_gdal_ptr(&self) -> bool {
        return self.c_geometry_ref.borrow().is_some();
    }

    pub unsafe fn set_c_geometry(&self, c_geometry: *const ()) {
        assert!(! self.has_gdal_ptr());
        assert_eq!(self.owned, false);
        *(self.c_geometry_ref.borrow_mut()) = Some(c_geometry);
    }

    unsafe fn with_c_geometry(c_geom: *const(), owned: bool) -> Geometry {
        return Geometry{
            c_geometry_ref: RefCell::new(Some(c_geom)),
            owned: owned,
        };
    }

    pub fn empty(wkb_type: c_int) -> Geometry {
        let c_geom = unsafe { ogr::OGR_G_CreateGeometry(wkb_type) };
        assert!(c_geom != null());
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    /// Create a geometry by parsing a
    /// [WKT](https://en.wikipedia.org/wiki/Well-known_text) string.
    pub fn from_wkt(wkt: &str) -> Geometry {
        let c_wkt = CString::new(wkt.as_bytes()).unwrap();
        let mut c_wkt_ptr: *const c_char = c_wkt.as_ptr();
        let mut c_geom: *const () = null();
        let rv = unsafe { ogr::OGR_G_CreateFromWkt(&mut c_wkt_ptr, null(), &mut c_geom) };
        assert_eq!(rv, ogr::OGRERR_NONE);
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    /// Create a rectangular geometry from West, South, East and North values.
    pub fn bbox(w: f64, s: f64, e: f64, n: f64) -> Geometry {
        Geometry::from_wkt(&format!(
            "POLYGON (({} {}, {} {}, {} {}, {} {}, {} {}))",
            w, n,
            e, n,
            e, s,
            w, s,
            w, n,
        ))
    }

    /// Serialize the geometry as JSON.
    pub fn json(&self) -> String {
        let c_json = unsafe { ogr::OGR_G_ExportToJson(self.c_geometry()) };
        let rv = _string(c_json);
        unsafe { ogr::VSIFree(c_json as *mut ()) };
        return rv;
    }

    /// Serialize the geometry as WKT.
    pub fn wkt(&self) -> String {
        let mut c_wkt: *const c_char = null();
        let _err = unsafe { ogr::OGR_G_ExportToWkt(self.c_geometry(), &mut c_wkt) };
        assert_eq!(_err, ogr::OGRERR_NONE);
        let wkt = _string(c_wkt);
        unsafe { ogr::OGRFree(c_wkt as *mut ()) };
        return wkt;
    }

    pub unsafe fn c_geometry(&self) -> *const () {
        return self.c_geometry_ref.borrow().unwrap();
    }

    pub fn set_point_2d(&mut self, i: usize, p: (f64, f64)) {
        let (x, y) = p;
        unsafe { ogr::OGR_G_SetPoint_2D(
            self.c_geometry(),
            i as c_int,
            x as c_double,
            y as c_double,
        ) };
    }

    pub fn get_point(&self, i: i32) -> (f64, f64, f64) {
        let mut x: c_double = 0.;
        let mut y: c_double = 0.;
        let mut z: c_double = 0.;
        unsafe { ogr::OGR_G_GetPoint(self.c_geometry(), i, &mut x, &mut y, &mut z) };
        return (x as f64, y as f64, z as f64);
    }

    pub fn get_point_vec(&self) -> Vec<(f64, f64, f64)> {
        let length = unsafe{ ogr::OGR_G_GetPointCount(self.c_geometry()) };
        return (0..length).map(|i| self.get_point(i)).collect();
    }

    /// Compute the convex hull of this geometry.
    pub fn convex_hull(&self) -> Geometry {
        let c_geom = unsafe { ogr::OGR_G_ConvexHull(self.c_geometry()) };
        return unsafe { Geometry::with_c_geometry(c_geom, true) };
    }

    unsafe fn _get_geometry(&self, n: usize) -> Geometry {
        // get the n-th sub-geometry as a non-owned Geometry; don't keep this
        // object for long.
        let c_geom = ogr::OGR_G_GetGeometryRef(self.c_geometry(), n as c_int);
        return Geometry::with_c_geometry(c_geom, false);
    }
}

impl geo::ToGeo for Geometry {
    fn to_geo(&self) -> geo::Geometry {
        let geometry_type = unsafe { ogr::OGR_G_GetGeometryType(self.c_geometry()) };

        let ring = |n: usize| {
            let ring = unsafe { self._get_geometry(n) };
            return match ring.to_geo() {
                geo::Geometry::LineString(r) => r,
                _ => panic!("Expected to get a LineString")
            };
        };

        match geometry_type {
            ogr::WKB_POINT => {
                let (x, y, _) = self.get_point(0);
                geo::Geometry::Point(geo::Point(geo::Coordinate{x: x, y: y}))
            },
            ogr::WKB_LINESTRING => {
                let coords = self.get_point_vec().iter()
                    .map(|&(x, y, _)| geo::Point(geo::Coordinate{x: x, y: y}))
                    .collect();
                geo::Geometry::LineString(geo::LineString(coords))
            },
            ogr::WKB_POLYGON => {
                let ring_count = unsafe { ogr::OGR_G_GetGeometryCount(self.c_geometry()) } as usize;
                let outer = ring(0);
                let holes = (1..ring_count).map(|n| ring(n)).collect();
                geo::Geometry::Polygon(geo::Polygon(outer, holes))
            },
            _ => panic!("Unknown geometry type")
        }
    }
}

impl Drop for Geometry {
    fn drop(&mut self) {
        if self.owned {
            let c_geometry = self.c_geometry_ref.borrow();
            unsafe { ogr::OGR_G_DestroyGeometry(c_geometry.unwrap() as *mut ()) };
        }
    }
}

pub trait ToGdal {
    fn to_gdal(&self) -> Geometry;
}

impl ToGdal for geo::Point {
    fn to_gdal(&self) -> Geometry {
        let mut geom = Geometry::empty(ogr::WKB_POINT);
        let &geo::Point(coordinate) = self;
        geom.set_point_2d(0, (coordinate.x, coordinate.y));
        return geom;
    }
}

fn geometry_with_points(wkb_type: c_int, points: &geo::LineString) -> Geometry {
    let mut geom = Geometry::empty(wkb_type);
    let &geo::LineString(ref linestring) = points;
    for (i, &geo::Point(coordinate)) in linestring.iter().enumerate() {
        geom.set_point_2d(i, (coordinate.x, coordinate.y));
    }
    return geom;
}

impl ToGdal for geo::LineString {
    fn to_gdal(&self) -> Geometry {
        geometry_with_points(ogr::WKB_LINESTRING, self)
    }
}

impl ToGdal for geo::Geometry {
    fn to_gdal(&self) -> Geometry {
        return match *self {
            geo::Geometry::Point(ref c) => c.to_gdal(),
            geo::Geometry::LineString(ref c) => c.to_gdal(),
            _ => panic!("Unknown geometry type")
        }
    }
}

#[cfg(test)]
mod tests {
    use vector::{Geometry, ToGdal};
    use geo;
    use geo::ToGeo;

    #[test]
    fn test_import_export_point() {
        let wkt = "POINT (1 2)";
        let coord = geo::Coordinate{x: 1., y: 2.};
        let geo = geo::Geometry::Point(geo::Point(coord));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
        assert_eq!(geo.to_gdal().wkt(), wkt);
    }

    #[test]
    fn test_import_export_linestring() {
        let wkt = "LINESTRING (0 0,0 1,1 2)";
        let coord = vec!(
            geo::Point(geo::Coordinate{x: 0., y: 0.}),
            geo::Point(geo::Coordinate{x: 0., y: 1.}),
            geo::Point(geo::Coordinate{x: 1., y: 2.}),
        );
        let geo = geo::Geometry::LineString(geo::LineString(coord));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
        assert_eq!(geo.to_gdal().wkt(), wkt);
    }

    fn square(x0: isize, y0: isize, x1: isize, y1: isize) -> geo::LineString {
        geo::LineString(vec!(
            geo::Point(geo::Coordinate{x: x0 as f64, y: y0 as f64}),
            geo::Point(geo::Coordinate{x: x0 as f64, y: y1 as f64}),
            geo::Point(geo::Coordinate{x: x1 as f64, y: y1 as f64}),
            geo::Point(geo::Coordinate{x: x1 as f64, y: y0 as f64}),
            geo::Point(geo::Coordinate{x: x0 as f64, y: y0 as f64}),
        ))
    }

    #[test]
    fn test_import_export_polygon() {
        let wkt = "POLYGON ((0 0,0 5,5 5,5 0,0 0), \
                            (1 1,1 2,2 2,2 1,1 1), \
                            (3 3,3 4,4 4,4 3,3 3))";
        let outer = square(0, 0, 5, 5);
        let holes = vec!(square(1, 1, 2, 2), square(3, 3, 4, 4));
        let geo = geo::Geometry::Polygon(geo::Polygon(outer, holes));

        assert_eq!(Geometry::from_wkt(wkt).to_geo(), geo);
    }
}
<|endoftext|>


<commit_before>use phi::{Phi, View, ViewAction};
use phi::data::Rectangle;
use phi::gfx::{Sprite, RenderSprite};
use sdl2::pixels::Color;

struct Action {
    /// The function which should be executed if the action is chosen.
    func: Box<Fn(&mut Phi) -> ViewAction>,

    /// The sprite which is rendered when the player does not focus on this
    /// action's label.
    idle_sprite: Sprite,

    /// The sprite which is rendered when the player "focuses" a label with the
    /// directional keys.
    hover_sprite: Sprite,
}

impl Action {
    fn new(phi: &mut Phi, label: &'static str, func: Box<Fn(&mut Phi) -> ViewAction>) -> Action {
        Action {
            func: func,
            idle_sprite: phi.ttf_str_sprite(label, "assets/belligerent.ttf", 32, Color::RGB(220, 220, 220)).unwrap(),
            hover_sprite: phi.ttf_str_sprite(label, "assets/belligerent.ttf", 38, Color::RGB(255, 255, 255)).unwrap(),
        }
    }
}

pub struct MainMenuView {
    actions: Vec<Action>,
}

impl MainMenuView {
    pub fn new(phi: &mut Phi) -> MainMenuView {
        MainMenuView {
            actions: vec![
                Action::new(phi, "New Game", Box::new(|phi| {
                    ViewAction::ChangeView { new_view: Box::new(::views::game::ShipView::new(phi)) }
                })),
                Action::new(phi, "Quit", Box::new(|_| {
                    ViewAction::Quit
                })),
            ],
        }
    }
}

impl View for MainMenuView {
    fn render(&mut self, phi: &mut Phi, _: f64) -> ViewAction {
        if phi.events.now.quit || phi.events.now.key_escape == Some(true) {
            return ViewAction::Quit;
        }

        // Clear the screen
        phi.renderer.set_draw_color(Color::RGB(0, 0, 0));
        phi.renderer.clear();

        for (i, action) in self.actions.iter().enumerate() {
            let (w, h) = action.idle_sprite.size();
            phi.renderer.render_sprite(&action.idle_sprite, Rectangle {
                x: 32.0,
                //? We place every element under the previous one.
                y: 32.0 + 48.0 * i as f64,
                w: w,
                h: h,
            });
        }

        ViewAction::None
    }
}

<commit_msg>The main menu is now usable!<commit_after>use phi::{Phi, View, ViewAction};
use phi::data::Rectangle;
use phi::gfx::{Sprite, RenderSprite};
use sdl2::pixels::Color;

struct Action {
    /// The function which should be executed if the action is chosen.
    func: Box<Fn(&mut Phi) -> ViewAction>,

    /// The sprite which is rendered when the player does not focus on this
    /// action's label.
    idle_sprite: Sprite,

    /// The sprite which is rendered when the player "focuses" a label with the
    /// directional keys.
    hover_sprite: Sprite,
}

impl Action {
    fn new(phi: &mut Phi, label: &'static str, func: Box<Fn(&mut Phi) -> ViewAction>) -> Action {
        Action {
            func: func,
            idle_sprite: phi.ttf_str_sprite(label, "assets/belligerent.ttf", 32, Color::RGB(220, 220, 220)).unwrap(),
            hover_sprite: phi.ttf_str_sprite(label, "assets/belligerent.ttf", 38, Color::RGB(255, 255, 255)).unwrap(),
        }
    }
}

pub struct MainMenuView {
    actions: Vec<Action>,
    selected: i8,
}

impl MainMenuView {
    pub fn new(phi: &mut Phi) -> MainMenuView {
        MainMenuView {
            actions: vec![
                Action::new(phi, "New Game", Box::new(|phi| {
                    ViewAction::ChangeView { new_view: Box::new(::views::game::ShipView::new(phi)) }
                })),
                Action::new(phi, "Quit", Box::new(|_| {
                    ViewAction::Quit
                })),
            ],

            selected: 0,
        }
    }
}

impl View for MainMenuView {
    fn render(&mut self, phi: &mut Phi, _: f64) -> ViewAction {
        if phi.events.now.quit || phi.events.now.key_escape == Some(true) {
            return ViewAction::Quit;
        }

        if phi.events.now.key_space == Some(true) {
            return (self.actions[self.selected as usize].func)(phi);
        }

        if phi.events.now.key_down == Some(true) {
            self.selected += 1;
            if self.selected >= self.actions.len() as i8 {
                self.selected = 0;
            }
        } else if phi.events.now.key_up == Some(true) {
            self.selected -= 1;
            if self.selected < 0 {
                self.selected = (self.actions.len() - 1) as i8;
            }
        }

        // Clear the screen
        phi.renderer.set_draw_color(Color::RGB(0, 0, 0));
        phi.renderer.clear();

        for (i, action) in self.actions.iter().enumerate() {
            let sprite_to_render = if self.selected == i as i8 {
                &action.hover_sprite
            } else {
                &action.idle_sprite
            };

            let (w, h) = sprite_to_render.size();

            let (win_w, win_h) = phi.output_size();
            phi.renderer.render_sprite(sprite_to_render, Rectangle {
                x: (win_w - w) / 2.0,
                //? We place every element under the previous one.
                y: (win_h - h) / 2.0 + h * 1.5 * i as f64,
                w: w,
                h: h,
            });
        }

        ViewAction::None
    }
}

<|endoftext|>