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

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{Parser, SourcePosition, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CompactCowStr;
use selectors::parser::SelectorParseError;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CompactCowStr<'a>),
    Ident(CompactCowStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> String {
        match self {
            ErrorString::Snippet(s) => s.into_owned(),
            ErrorString::Ident(i) => escape_css_ident(&i),
            ErrorString::UnexpectedToken(t) => token_to_str(t),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::new();

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i.to_string(), escape_css_ident(&unit.to_string())),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value.to_string(), escape_css_ident(&unit.to_string())),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CompactCowStr<'a>, ParseError<'a>);
    fn error_param(self) -> ErrorString<'a>;
    fn to_gecko_message(&self) -> &'static [u8];
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CompactCowStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_param(self) -> ErrorString<'a> {
        match self.error_data() {
            (_, CssParseError::Basic(BasicParseError::UnexpectedToken(t))) =>
                ErrorString::UnexpectedToken(t),

            (_, CssParseError::Basic(BasicParseError::AtRuleInvalid(i))) =>
                ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::PropertyDeclaration(
                    PropertyDeclarationParseError::InvalidValue(property))))) =>
                ErrorString::Snippet(property.into()),

            (_, CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident))) =>
                ErrorString::Ident(ident),

            (_, CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace))) =>
                ErrorString::Ident(namespace),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnknownProperty(property)))) =>
                ErrorString::Ident(property),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(token)))) =>
                ErrorString::UnexpectedToken(token),

            (s, _)  => ErrorString::Snippet(s)
        }
    }

    fn to_gecko_message(&self) -> &'static [u8] {
        match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                b"PEParseDeclarationDeclExpected\0",
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_))))) =>
                b"PEValueParsingError\0",
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                b"PEUnknownProperty\0",
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                b"PEUnknwnFontDesc\0",
            ContextualParseError::InvalidKeyframeRule(..) =>
                b"PEKeyframeBadName\0",
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                b"PEBadSelectorKeyframeRuleIgnored\0",
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                b"PEUnknownNamespacePrefix\0",
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                b"PEAtNSUnexpected\0",
            ContextualParseError::InvalidRule(..) =>
                b"PEBadSelectorRSIgnored\0",
            ContextualParseError::UnsupportedRule(..) =>
                b"PEDeclDropped\0",
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                b"PEUnknownAtRule\0",
        }
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error<'a>(&self,
                        input: &mut Parser,
                        position: SourcePosition,
                        error: ContextualParseError<'a>,
                        url: &UrlExtraData,
                        line_number_offset: u64) {
        let location = input.source_location(position);
        let line_number = location.line + line_number_offset as u32;

        let name = error.to_gecko_message();
        let param = error.error_param().into_str();
        let source = input.current_line();
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           line_number as u32,
                                           location.column as u32,
                                           url.mBaseURI.raw::<nsIURI>());
        }
    }
}
<commit_msg>Auto merge of #17738 - jdm:no-css-text, r=bholley<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{Parser, SourcePosition, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CompactCowStr;
use selectors::parser::SelectorParseError;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CompactCowStr<'a>),
    Ident(CompactCowStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> String {
        match self {
            ErrorString::Snippet(s) => s.into_owned(),
            ErrorString::Ident(i) => escape_css_ident(&i),
            ErrorString::UnexpectedToken(t) => token_to_str(t),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::new();

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i.to_string(), escape_css_ident(&unit.to_string())),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value.to_string(), escape_css_ident(&unit.to_string())),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CompactCowStr<'a>, ParseError<'a>);
    fn error_param(self) -> ErrorString<'a>;
    fn to_gecko_message(&self) -> &'static [u8];
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CompactCowStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_param(self) -> ErrorString<'a> {
        match self.error_data() {
            (_, CssParseError::Basic(BasicParseError::UnexpectedToken(t))) =>
                ErrorString::UnexpectedToken(t),

            (_, CssParseError::Basic(BasicParseError::AtRuleInvalid(i))) =>
                ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::PropertyDeclaration(
                    PropertyDeclarationParseError::InvalidValue(property))))) =>
                ErrorString::Snippet(property.into()),

            (_, CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident))) =>
                ErrorString::Ident(ident),

            (_, CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace))) =>
                ErrorString::Ident(namespace),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnknownProperty(property)))) =>
                ErrorString::Ident(property),

            (_, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(token)))) =>
                ErrorString::UnexpectedToken(token),

            (s, _)  => ErrorString::Snippet(s)
        }
    }

    fn to_gecko_message(&self) -> &'static [u8] {
        match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                b"PEParseDeclarationDeclExpected\0",
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_))))) =>
                b"PEValueParsingError\0",
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                b"PEUnknownProperty\0",
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                b"PEUnknwnFontDesc\0",
            ContextualParseError::InvalidKeyframeRule(..) =>
                b"PEKeyframeBadName\0",
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                b"PEBadSelectorKeyframeRuleIgnored\0",
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                b"PEUnknownNamespacePrefix\0",
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                b"PEAtNSUnexpected\0",
            ContextualParseError::InvalidRule(..) =>
                b"PEBadSelectorRSIgnored\0",
            ContextualParseError::UnsupportedRule(..) =>
                b"PEDeclDropped\0",
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                b"PEUnknownAtRule\0",
        }
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error<'a>(&self,
                        input: &mut Parser,
                        position: SourcePosition,
                        error: ContextualParseError<'a>,
                        url: &UrlExtraData,
                        line_number_offset: u64) {
        let location = input.source_location(position);
        let line_number = location.line + line_number_offset as u32;

        let name = error.to_gecko_message();
        let param = error.error_param().into_str();
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           line_number as u32,
                                           location.column as u32,
                                           url.mBaseURI.raw::<nsIURI>());
        }
    }
}
<|endoftext|>


<commit_before>use std::{io, path, ptr, mem, ffi, slice, time, fs};
use std::path::PathBuf;
use std::io::Read;
use std::ops::Sub;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_void, c_int, c_ulong, c_ushort, c_uint, c_long, c_schar, c_uchar, size_t, uid_t};
use data::*;
use super::common::*;

fn read_file(path: &str) -> io::Result<String> {
    let mut s = String::new();
    fs::File::open(path)
        .and_then(|mut f| f.read_to_string(&mut s))
        .map(|_| s)
}

fn value_from_file(path: &str) -> io::Result<i32> {
    try!(read_file(path))
        .trim_right_matches("\n")
        .parse()
        .and_then(|n| Ok(n))
        .or_else(|_| {
            Err(io::Error::new(io::ErrorKind::Other,
                               format!("File: \"{}\" doesn't contain an int value", &path)))
        })
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        let mut loads: [f64; 3] = [0.0, 0.0, 0.0];
        if unsafe { getloadavg(&mut loads[0], 3) } != 3 {
            return Err(io::Error::new(io::ErrorKind::Other, "getloadavg() failed"));
        }
        Ok(LoadAverage {
            one: loads[0] as f32,
            five: loads[1] as f32,
            fifteen: loads[2] as f32,
        })
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = try!(fs::read_dir(&dir));
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += try!(value_from_file(&(s.to_string() + "/charge_full")));
                    now += try!(value_from_file(&(s.to_string() + "/charge_now")));
                    current += try!(value_from_file(&(s.to_string() + "/current_now")));
                }
            }
        }
        if full != 0 {
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Missing battery information"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        value_from_file("/sys/class/power_supply/AC/online").map(|v| v == 1)
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn getloadavg(loadavg: *mut f64, nelem: c_int) -> c_int;
    fn sysinfo(info: *mut sysinfo);
}
<commit_msg>Avoid division by zero<commit_after>use std::{io, path, ptr, mem, ffi, slice, time, fs};
use std::path::PathBuf;
use std::io::Read;
use std::ops::Sub;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_void, c_int, c_ulong, c_ushort, c_uint, c_long, c_schar, c_uchar, size_t, uid_t};
use data::*;
use super::common::*;

fn read_file(path: &str) -> io::Result<String> {
    let mut s = String::new();
    fs::File::open(path)
        .and_then(|mut f| f.read_to_string(&mut s))
        .map(|_| s)
}

fn value_from_file(path: &str) -> io::Result<i32> {
    try!(read_file(path))
        .trim_right_matches("\n")
        .parse()
        .and_then(|n| Ok(n))
        .or_else(|_| {
            Err(io::Error::new(io::ErrorKind::Other,
                               format!("File: \"{}\" doesn't contain an int value", &path)))
        })
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    if current_now != 0 {
	    Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
	} else {
		Duration::new(0, 0)
	}
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        let mut loads: [f64; 3] = [0.0, 0.0, 0.0];
        if unsafe { getloadavg(&mut loads[0], 3) } != 3 {
            return Err(io::Error::new(io::ErrorKind::Other, "getloadavg() failed"));
        }
        Ok(LoadAverage {
            one: loads[0] as f32,
            five: loads[1] as f32,
            fifteen: loads[2] as f32,
        })
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = try!(fs::read_dir(&dir));
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += try!(value_from_file(&(s.to_string() + "/charge_full")));
                    now += try!(value_from_file(&(s.to_string() + "/charge_now")));
                    current += try!(value_from_file(&(s.to_string() + "/current_now")));
                }
            }
        }
        if full != 0 {
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Missing battery information"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        value_from_file("/sys/class/power_supply/AC/online").map(|v| v == 1)
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn getloadavg(loadavg: *mut f64, nelem: c_int) -> c_int;
    fn sysinfo(info: *mut sysinfo);
}
<|endoftext|>


<commit_before>use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use byteorder::{BigEndian, WriteBytesExt};

// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4   = 4,
    TCP   = 6,
    UDP   = 17,
    DCCP  = 33,
    IP6   = 41,
    SCTP  = 132,
    UTP   = 301,
    UDT   = 302,
    IPFS  = 421,
    HTTP  = 480,
    HTTPS = 443,
    ONION = 444,
}

impl From<ProtocolTypes> for u16 {
    fn from(t: ProtocolTypes) -> u16 {
        t as u16
    }
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        self.into()
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }

    // Try to convert a string to a protocol
    pub fn from_name(s: &str) -> Option<ProtocolTypes> {
        match s {
            "ip4"   => Some(ProtocolTypes::IP4),
	    "tcp"   => Some(ProtocolTypes::TCP),
	    "udp"   => Some(ProtocolTypes::UDP),
	    "dccp"  => Some(ProtocolTypes::DCCP),
	    "ip6"   => Some(ProtocolTypes::IP6),
	    "sctp"  => Some(ProtocolTypes::SCTP),
	    "utp"   => Some(ProtocolTypes::UTP),
	    "udt"   => Some(ProtocolTypes::UDT),
	    "ipfs"  => Some(ProtocolTypes::IPFS),
	    "http"  => Some(ProtocolTypes::HTTP),
	    "https" => Some(ProtocolTypes::HTTPS),
	    "onion" => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn address_string_to_bytes(&self, a: &str) -> Option<Vec<u8>> {
        match *self {
            ProtocolTypes::IP4        => {
                let octets = Ipv4Addr::from_str(a).unwrap().octets();
                let mut res = Vec::new();
                res.extend(octets.iter().cloned());
                println!("{:?}", res);
                Some(res)
            },
            ProtocolTypes::IP6        => {
                let segments = Ipv6Addr::from_str(a).unwrap().segments();
                let mut res = Vec::new();

                for segment in &segments {
                    println!("{}", *segment);
                    res.write_u16::<BigEndian>(*segment).unwrap();
                }

                Some(res)
            },
	    ProtocolTypes::TCP
                | ProtocolTypes::UDP
                | ProtocolTypes::DCCP
                | ProtocolTypes::SCTP => {
                    let parsed: u16 = a.parse().unwrap();
                    let mut res = Vec::new();
                    res.write_u16::<BigEndian>(parsed).unwrap();
                    println!("{:?}", res);
                    Some(res)
                },
	    ProtocolTypes::IPFS       => Some(Vec::new()),
	    ProtocolTypes::ONION      => Some(Vec::new()),
	    ProtocolTypes::UTP
	        | ProtocolTypes::UDT
	        | ProtocolTypes::HTTP
	        | ProtocolTypes::HTTPS => {
                    // These all have length 0 so just return an empty vector
                    // for consistency
                    Some(Vec::new())
                },
        }
    }
}
<commit_msg>Use into for code conversion<commit_after>use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use std::convert::From;
use byteorder::{BigEndian, WriteBytesExt};

// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4   = 4,
    TCP   = 6,
    UDP   = 17,
    DCCP  = 33,
    IP6   = 41,
    SCTP  = 132,
    UTP   = 301,
    UDT   = 302,
    IPFS  = 421,
    HTTP  = 480,
    HTTPS = 443,
    ONION = 444,
}

impl From<ProtocolTypes> for u16 {
    fn from(t: ProtocolTypes) -> u16 {
        t as u16
    }
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        (*self).into()
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }

    // Try to convert a string to a protocol
    pub fn from_name(s: &str) -> Option<ProtocolTypes> {
        match s {
            "ip4"   => Some(ProtocolTypes::IP4),
	    "tcp"   => Some(ProtocolTypes::TCP),
	    "udp"   => Some(ProtocolTypes::UDP),
	    "dccp"  => Some(ProtocolTypes::DCCP),
	    "ip6"   => Some(ProtocolTypes::IP6),
	    "sctp"  => Some(ProtocolTypes::SCTP),
	    "utp"   => Some(ProtocolTypes::UTP),
	    "udt"   => Some(ProtocolTypes::UDT),
	    "ipfs"  => Some(ProtocolTypes::IPFS),
	    "http"  => Some(ProtocolTypes::HTTP),
	    "https" => Some(ProtocolTypes::HTTPS),
	    "onion" => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn address_string_to_bytes(&self, a: &str) -> Option<Vec<u8>> {
        match *self {
            ProtocolTypes::IP4        => {
                let octets = Ipv4Addr::from_str(a).unwrap().octets();
                let mut res = Vec::new();
                res.extend(octets.iter().cloned());
                println!("{:?}", res);
                Some(res)
            },
            ProtocolTypes::IP6        => {
                let segments = Ipv6Addr::from_str(a).unwrap().segments();
                let mut res = Vec::new();

                for segment in &segments {
                    println!("{}", *segment);
                    res.write_u16::<BigEndian>(*segment).unwrap();
                }

                Some(res)
            },
	    ProtocolTypes::TCP
                | ProtocolTypes::UDP
                | ProtocolTypes::DCCP
                | ProtocolTypes::SCTP => {
                    let parsed: u16 = a.parse().unwrap();
                    let mut res = Vec::new();
                    res.write_u16::<BigEndian>(parsed).unwrap();
                    println!("{:?}", res);
                    Some(res)
                },
	    ProtocolTypes::IPFS       => Some(Vec::new()),
	    ProtocolTypes::ONION      => Some(Vec::new()),
	    ProtocolTypes::UTP
	        | ProtocolTypes::UDT
	        | ProtocolTypes::HTTP
	        | ProtocolTypes::HTTPS => {
                    // These all have length 0 so just return an empty vector
                    // for consistency
                    Some(Vec::new())
                },
        }
    }
}
<|endoftext|>


<commit_before>use euclid::{Rect, Size2D};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, DrawLayer, RendererFrame};
use ipc_channel::ipc::IpcReceiver;
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::mem;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, IdNamespace, RenderNotifier, ScrollLayerId};
use webrender_traits::{WebGLContextId, WebGLCommand};
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{NativeGLContext, GLContext, ColorAttachmentType, NativeGLContextMethods, NativeGLContextHandle};
use gleam::gl;

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    result_tx: Sender<ResultMsg>,

    viewport: Rect<i32>,
    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    pending_webgl_commands: Vec<(WebGLContextId, WebGLCommand)>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               result_tx: Sender<ResultMsg>,
               viewport: Rect<i32>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        let backend = RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            result_tx: result_tx,
            viewport: viewport,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            pending_webgl_commands: Vec::new(),
        };

        backend
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();

            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::AddDisplayList(id,
                                               pipeline_id,
                                               epoch,
                                               display_list_builder) => {
                            self.scene.add_display_list(id,
                                                        pipeline_id,
                                                        epoch,
                                                        display_list_builder,
                                                        &mut self.resource_cache);
                        }
                        ApiMsg::AddStackingContext(id,
                                                   pipeline_id,
                                                   epoch,
                                                   stacking_context) => {
                            self.scene.add_stacking_context(id,
                                                            pipeline_id,
                                                            epoch,
                                                            stacking_context);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     &mut self.resource_cache);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta) => {
                            let frame = profile_counters.total_time.profile(|| {
                                let viewport_size = Size2D::new(self.viewport.size.width as f32,
                                                                self.viewport.size.height as f32);
                                self.frame.scroll(&delta, &viewport_size);
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // TODO(pcwalton): Select other layers for mouse events.
                            let point = point / self.device_pixel_ratio;
                            match self.frame.layers.get_mut(&ScrollLayerId(0)) {
                                None => tx.send(point).unwrap(),
                                Some(layer) => tx.send(point - layer.scroll_offset).unwrap(),
                            }
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok(id)).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            self.pending_webgl_commands.push((context_id, command));
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn apply_webgl_command(&self, command: WebGLCommand) {
        match command {
            WebGLCommand::Clear(what)
                => gl::clear(what),
            WebGLCommand::ClearColor(r, g, b, a)
                => gl::clear_color(r, g, b, a),
        }
    }

    fn apply_webgl_commands(&mut self) {
        if self.pending_webgl_commands.is_empty() {
            return; // Early return in easy case
        }

        let mut current_context_id = None;
        let mut current_context: Option<&GLContext<NativeGLContext>> = None;

        // Sort by context id to minimize context switching
        self.pending_webgl_commands.sort_by(|a, b| a.0.cmp(&b.0));

        let mut pending = mem::replace(&mut self.pending_webgl_commands, Vec::new());

        for (context_id, command) in pending.drain(..) {
            if current_context_id != Some(context_id) {
                current_context_id = Some(context_id);
                current_context = Some(self.webgl_contexts.get(&context_id).unwrap());
                current_context.unwrap().make_current().unwrap();
            }

            self.apply_webgl_command(command);
        }

        // Without this the changes in the texture are not seen in the renderer.
        if let Some(ctx) = current_context {
            ctx.unbind().unwrap();
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        self.frame.create(&self.scene,
                          Size2D::new(self.viewport.size.width as u32,
                                      self.viewport.size.height as u32),
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        self.apply_webgl_commands();

        let mut frame = self.frame.build(&self.viewport,
                                         &mut self.resource_cache,
                                         &mut self.thread_pool,
                                         self.device_pixel_ratio);

        // Bit of a hack - if there was nothing visible, at least
        // add one layer to the frame so that the screen gets
        // cleared to the default UA background color. Perhaps
        // there is a better way to handle this...
        if frame.layers.len() == 0 {
            frame.layers.push(DrawLayer {
                texture_id: None,
                size: Size2D::new(self.viewport.size.width as u32,
                                   self.viewport.size.height as u32),
                commands: Vec::new(),
            });
        }

        let pending_update = self.resource_cache.pending_updates();
        if pending_update.updates.len() > 0 {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        let pending_update = self.frame.pending_updates();
        if pending_update.updates.len() > 0 {
            self.result_tx.send(ResultMsg::UpdateBatches(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let msg = ResultMsg::NewFrame(frame, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }
}

<commit_msg>Use WebGL logic from webrender_traits<commit_after>use euclid::{Rect, Size2D};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, DrawLayer, RendererFrame};
use ipc_channel::ipc::IpcReceiver;
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, IdNamespace, RenderNotifier, ScrollLayerId};
use webrender_traits::WebGLContextId;
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{NativeGLContext, GLContext, ColorAttachmentType, NativeGLContextMethods, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    result_tx: Sender<ResultMsg>,

    viewport: Rect<i32>,
    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               result_tx: Sender<ResultMsg>,
               viewport: Rect<i32>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        let backend = RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            result_tx: result_tx,
            viewport: viewport,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
        };

        backend
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();

            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::AddDisplayList(id,
                                               pipeline_id,
                                               epoch,
                                               display_list_builder) => {
                            self.scene.add_display_list(id,
                                                        pipeline_id,
                                                        epoch,
                                                        display_list_builder,
                                                        &mut self.resource_cache);
                        }
                        ApiMsg::AddStackingContext(id,
                                                   pipeline_id,
                                                   epoch,
                                                   stacking_context) => {
                            self.scene.add_stacking_context(id,
                                                            pipeline_id,
                                                            epoch,
                                                            stacking_context);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     &mut self.resource_cache);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta) => {
                            let frame = profile_counters.total_time.profile(|| {
                                let viewport_size = Size2D::new(self.viewport.size.width as f32,
                                                                self.viewport.size.height as f32);
                                self.frame.scroll(&delta, &viewport_size);
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // TODO(pcwalton): Select other layers for mouse events.
                            let point = point / self.device_pixel_ratio;
                            match self.frame.layers.get_mut(&ScrollLayerId(0)) {
                                None => tx.send(point).unwrap(),
                                Some(layer) => tx.send(point - layer.scroll_offset).unwrap(),
                            }
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok(id)).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            // TODO: Buffer the commands and only apply them here if they need to
                            // be synchronous.
                            let ctx = self.webgl_contexts.get(&context_id).unwrap();
                            ctx.make_current().unwrap();
                            command.apply(ctx);
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        self.frame.create(&self.scene,
                          Size2D::new(self.viewport.size.width as u32,
                                      self.viewport.size.height as u32),
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let mut frame = self.frame.build(&self.viewport,
                                         &mut self.resource_cache,
                                         &mut self.thread_pool,
                                         self.device_pixel_ratio);

        // Bit of a hack - if there was nothing visible, at least
        // add one layer to the frame so that the screen gets
        // cleared to the default UA background color. Perhaps
        // there is a better way to handle this...
        if frame.layers.len() == 0 {
            frame.layers.push(DrawLayer {
                texture_id: None,
                size: Size2D::new(self.viewport.size.width as u32,
                                   self.viewport.size.height as u32),
                commands: Vec::new(),
            });
        }

        let pending_update = self.resource_cache.pending_updates();
        if pending_update.updates.len() > 0 {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        let pending_update = self.frame.pending_updates();
        if pending_update.updates.len() > 0 {
            self.result_tx.send(ResultMsg::UpdateBatches(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let msg = ResultMsg::NewFrame(frame, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }
}

<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc_index::vec::Idx;
use std::fmt::Debug;
use std::io::{self, Write};

use super::pretty::dump_mir_def_ids;

/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<W>(
    tcx: TyCtxt<'_>,
    single: Option<DefId>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    let def_ids = dump_mir_def_ids(tcx, single);

    let use_subgraphs = def_ids.len() > 1;
    if use_subgraphs {
        writeln!(w, "digraph __crate__ {{")?;
    }

    for def_id in def_ids {
        let body = &tcx.optimized_mir(def_id);
        write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?;
    }

    if use_subgraphs {
        writeln!(w, "}}")?;
    }

    Ok(())
}

// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
// it does not have to be user friendly.
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
    format!(
        "{}_{}",
        def_id.krate.index(),
        def_id.index.index(),
    )
}

/// Write a graphviz DOT graph of the MIR.
pub fn write_mir_fn_graphviz<'tcx, W>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    subgraph: bool,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    let kind = if subgraph { "subgraph" } else { "digraph" };
    let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
    let def_name = graphviz_safe_def_name(def_id);
    writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;

    // Global graph properties
    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
    writeln!(w, r#"    node [fontname="monospace"];"#)?;
    writeln!(w, r#"    edge [fontname="monospace"];"#)?;

    // Graph label
    write_graph_label(tcx, def_id, body, w)?;

    // Nodes
    for (block, _) in body.basic_blocks().iter_enumerated() {
        write_node(def_id, block, body, w)?;
    }

    // Edges
    for (source, _) in body.basic_blocks().iter_enumerated() {
        write_edges(def_id, source, body, w)?;
    }
    writeln!(w, "}}")
}

/// Write a graphviz HTML-styled label for the given basic block, with
/// all necessary escaping already performed. (This is suitable for
/// emitting directly, as is done in this module, or for use with the
/// LabelText::HtmlStr from libgraphviz.)
///
/// `init` and `fini` are callbacks for emitting additional rows of
/// data (using HTML enclosed with `<tr>` in the emitted text).
pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
                                              body: &Body<'_>,
                                              w: &mut W,
                                              num_cols: u32,
                                              init: INIT,
                                              fini: FINI) -> io::Result<()>
    where INIT: Fn(&mut W) -> io::Result<()>,
          FINI: Fn(&mut W) -> io::Result<()>
{
    let data = &body[block];

    write!(w, r#"
"#)?;

    // Basic block number at the top.
    write!(w, r#"
{blk}
"#,
           attrs=r#"bgcolor="gray" align="center""#,
           colspan=num_cols,
           blk=block.index())?;

    init(w)?;

    // List of statements in the middle.
    if !data.statements.is_empty() {
        write!(w, r#"
"#)?;
        for statement in &data.statements {
            write!(w, "{}
", escape(statement))?;
        }
        write!(w, "
")?;
    }

    // Terminator head at the bottom, not including the list of successor blocks. Those will be
    // displayed as labels on the edges between blocks.
    let mut terminator_head = String::new();
    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
    write!(w, r#"
{}
"#, dot::escape_html(&terminator_head))?;

    fini(w)?;

    // Close the table
    write!(w, "
")
}

/// Write a graphviz DOT node for the given basic block.
fn write_node<W: Write>(
    def_id: DefId,
    block: BasicBlock,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
    write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
    // Close the node label and the node itself.
    writeln!(w, ">];")
}

/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
fn write_edges<W: Write>(
    def_id: DefId,
    source: BasicBlock,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    let terminator = body[source].terminator();
    let labels = terminator.kind.fmt_successor_labels();

    for (&target, label) in terminator.successors().zip(labels) {
        let src = node(def_id, source);
        let trg = node(def_id, target);
        writeln!(w, r#"    {} -> {} [label="{}"];"#, src, trg, label)?;
    }

    Ok(())
}

/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
/// all the variables and temporaries.
fn write_graph_label<'tcx, W: Write>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;

    // fn argument types.
    for (i, arg) in body.args_iter().enumerate() {
        if i > 0 {
            write!(w, ", ")?;
        }
        write!(w,
               "{:?}: {}",
               Place::from(arg),
               escape(&body.local_decls[arg].ty)
        )?;
    }

    write!(w, ") -> {}", escape(&body.return_ty()))?;
    write!(w, r#"
"#)?;

    for local in body.vars_and_temps_iter() {
        let decl = &body.local_decls[local];

        write!(w, "let ")?;
        if decl.mutability == Mutability::Mut {
            write!(w, "mut ")?;
        }

        write!(w, r#"{:?}: {};
"#,
               Place::from(local), escape(&decl.ty))?;
    }

    for var_debug_info in &body.var_debug_info {
        write!(w, r#"debug {} => {};
"#,
               var_debug_info.name, escape(&var_debug_info.place))?;
    }

    writeln!(w, ">;")
}

fn node(def_id: DefId, block: BasicBlock) -> String {
    format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
}

fn escape<T: Debug>(t: &T) -> String {
    dot::escape_html(&format!("{:?}", t))
}
<commit_msg>Fix angle bracket formatting when dumping MIR debug vars<commit_after>use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc_index::vec::Idx;
use std::fmt::Debug;
use std::io::{self, Write};

use super::pretty::dump_mir_def_ids;

/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<W>(
    tcx: TyCtxt<'_>,
    single: Option<DefId>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    let def_ids = dump_mir_def_ids(tcx, single);

    let use_subgraphs = def_ids.len() > 1;
    if use_subgraphs {
        writeln!(w, "digraph __crate__ {{")?;
    }

    for def_id in def_ids {
        let body = &tcx.optimized_mir(def_id);
        write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?;
    }

    if use_subgraphs {
        writeln!(w, "}}")?;
    }

    Ok(())
}

// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
// it does not have to be user friendly.
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
    format!(
        "{}_{}",
        def_id.krate.index(),
        def_id.index.index(),
    )
}

/// Write a graphviz DOT graph of the MIR.
pub fn write_mir_fn_graphviz<'tcx, W>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    subgraph: bool,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    let kind = if subgraph { "subgraph" } else { "digraph" };
    let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
    let def_name = graphviz_safe_def_name(def_id);
    writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;

    // Global graph properties
    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
    writeln!(w, r#"    node [fontname="monospace"];"#)?;
    writeln!(w, r#"    edge [fontname="monospace"];"#)?;

    // Graph label
    write_graph_label(tcx, def_id, body, w)?;

    // Nodes
    for (block, _) in body.basic_blocks().iter_enumerated() {
        write_node(def_id, block, body, w)?;
    }

    // Edges
    for (source, _) in body.basic_blocks().iter_enumerated() {
        write_edges(def_id, source, body, w)?;
    }
    writeln!(w, "}}")
}

/// Write a graphviz HTML-styled label for the given basic block, with
/// all necessary escaping already performed. (This is suitable for
/// emitting directly, as is done in this module, or for use with the
/// LabelText::HtmlStr from libgraphviz.)
///
/// `init` and `fini` are callbacks for emitting additional rows of
/// data (using HTML enclosed with `<tr>` in the emitted text).
pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
                                              body: &Body<'_>,
                                              w: &mut W,
                                              num_cols: u32,
                                              init: INIT,
                                              fini: FINI) -> io::Result<()>
    where INIT: Fn(&mut W) -> io::Result<()>,
          FINI: Fn(&mut W) -> io::Result<()>
{
    let data = &body[block];

    write!(w, r#"
"#)?;

    // Basic block number at the top.
    write!(w, r#"
{blk}
"#,
           attrs=r#"bgcolor="gray" align="center""#,
           colspan=num_cols,
           blk=block.index())?;

    init(w)?;

    // List of statements in the middle.
    if !data.statements.is_empty() {
        write!(w, r#"
"#)?;
        for statement in &data.statements {
            write!(w, "{}
", escape(statement))?;
        }
        write!(w, "
")?;
    }

    // Terminator head at the bottom, not including the list of successor blocks. Those will be
    // displayed as labels on the edges between blocks.
    let mut terminator_head = String::new();
    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
    write!(w, r#"
{}
"#, dot::escape_html(&terminator_head))?;

    fini(w)?;

    // Close the table
    write!(w, "
")
}

/// Write a graphviz DOT node for the given basic block.
fn write_node<W: Write>(
    def_id: DefId,
    block: BasicBlock,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
    write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
    // Close the node label and the node itself.
    writeln!(w, ">];")
}

/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
fn write_edges<W: Write>(
    def_id: DefId,
    source: BasicBlock,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    let terminator = body[source].terminator();
    let labels = terminator.kind.fmt_successor_labels();

    for (&target, label) in terminator.successors().zip(labels) {
        let src = node(def_id, source);
        let trg = node(def_id, target);
        writeln!(w, r#"    {} -> {} [label="{}"];"#, src, trg, label)?;
    }

    Ok(())
}

/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
/// all the variables and temporaries.
fn write_graph_label<'tcx, W: Write>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;

    // fn argument types.
    for (i, arg) in body.args_iter().enumerate() {
        if i > 0 {
            write!(w, ", ")?;
        }
        write!(w,
               "{:?}: {}",
               Place::from(arg),
               escape(&body.local_decls[arg].ty)
        )?;
    }

    write!(w, ") -> {}", escape(&body.return_ty()))?;
    write!(w, r#"
"#)?;

    for local in body.vars_and_temps_iter() {
        let decl = &body.local_decls[local];

        write!(w, "let ")?;
        if decl.mutability == Mutability::Mut {
            write!(w, "mut ")?;
        }

        write!(w, r#"{:?}: {};
"#,
               Place::from(local), escape(&decl.ty))?;
    }

    for var_debug_info in &body.var_debug_info {
        write!(w, r#"debug {} => {};
"#,
               var_debug_info.name, escape(&var_debug_info.place))?;
    }

    writeln!(w, ">;")
}

fn node(def_id: DefId, block: BasicBlock) -> String {
    format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
}

fn escape<T: Debug>(t: &T) -> String {
    dot::escape_html(&format!("{:?}", t))
}
<|endoftext|>


<commit_before>extern crate alfred;

use config::Config;
use jira_api::SearchIssue;
use jira_client;
use search_response::SearchResponse;
use std::io::{self};

pub struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<(), String> {
        let jira_search = SearchIssue {
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let res = jira_client::send::<SearchIssue>(jira_search).map_err(|e| e.to_string())?;
        Ok(self.write_alfred_items(self.config.hostname(), res)?)
    }

    fn write_alfred_items(&self, base_url: &String, res: SearchResponse) -> Result<(), String> {
        let alfred_items = res.issues.into_iter()
            .map(|issue| alfred::ItemBuilder::new(issue.fields.summary).arg(format!("{}/{}/browse", base_url, issue.key)).into_item())
            .collect::<Vec<alfred::Item>>();

        alfred::json::write_items(io::stdout(), &alfred_items).map_err(|e| e.to_string())
    }
}<commit_msg>:hammer: Fix API endpoint<commit_after>extern crate alfred;

use config::Config;
use jira_api::SearchIssue;
use jira_client;
use search_response::SearchResponse;
use std::io::{self};

pub struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<(), String> {
        let jira_search = SearchIssue {
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let res = jira_client::send::<SearchIssue>(jira_search).map_err(|e| e.to_string())?;
        Ok(self.write_alfred_items(self.config.hostname(), res)?)
    }

    fn write_alfred_items(&self, base_url: &String, res: SearchResponse) -> Result<(), String> {
        let alfred_items = res.issues.into_iter()
            .map(|issue| alfred::ItemBuilder::new(issue.fields.summary).arg(format!("{}/browse/{}", base_url, issue.key)).into_item())
            .collect::<Vec<alfred::Item>>();

        alfred::json::write_items(io::stdout(), &alfred_items).map_err(|e| e.to_string())
    }
}<|endoftext|>