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

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "SVGOpacity", "Default::default()",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "SVGWidth",
    "::values::computed::NonNegativeAu::from_px(1).into()",
    products="gecko",
    boxed="True",
    animation_value_type="::values::computed::SVGWidth",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "GreaterThanOrEqualToOneNumber",
                          "From::from(4.0)",
                          products="gecko",
                          animation_value_type="::values::computed::GreaterThanOrEqualToOneNumber",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "SVGOpacity", "Default::default()",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type(
    "stroke-dasharray",
    "SVGStrokeDashArray",
    "Default::default()",
    products="gecko",
    animation_value_type="::values::computed::SVGStrokeDashArray",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
)}

${helpers.predefined_type(
    "stroke-dashoffset", "SVGLength",
    "Au(0).into()",
    products="gecko",
    boxed="True",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="discrete"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(Clone, Copy, Debug, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>
<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   allow_empty="True">
    use values::CustomIdent;

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<commit_msg>Do not use CVAS for the paint-order property<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "SVGOpacity", "Default::default()",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "SVGWidth",
    "::values::computed::NonNegativeAu::from_px(1).into()",
    products="gecko",
    boxed="True",
    animation_value_type="::values::computed::SVGWidth",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "GreaterThanOrEqualToOneNumber",
                          "From::from(4.0)",
                          products="gecko",
                          animation_value_type="::values::computed::GreaterThanOrEqualToOneNumber",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "SVGOpacity", "Default::default()",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type(
    "stroke-dasharray",
    "SVGStrokeDashArray",
    "Default::default()",
    products="gecko",
    animation_value_type="::values::computed::SVGStrokeDashArray",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
)}

${helpers.predefined_type(
    "stroke-dashoffset", "SVGLength",
    "Au(0).into()",
    products="gecko",
    boxed="True",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="discrete"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }
</%helpers:longhand>

<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   allow_empty="True">
    use values::CustomIdent;

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the hover feature
use crate::lsp::{HoverContents, LanguageString, MarkedString};
use crate::utils::{NodeKind, NodeResolutionInfo};
use graphql_ir::{Program, Value};
use graphql_text_printer::print_value;
use interner::StringKey;
use schema::Schema;
use schema_print::print_directive;
use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

fn graphql_marked_string(value: String) -> MarkedString {
    MarkedString::LanguageString(LanguageString {
        language: "graphql".to_string(),
        value,
    })
}

fn hover_content_wrapper(value: String) -> HoverContents {
    HoverContents::Scalar(graphql_marked_string(value))
}

/// This will provide a more accurate information about some of the specific Relay directives
/// that cannot be expressed via SDL
fn argument_definition_hover_info(directive_name: &str) -> Option<HoverContents> {
    let content = match directive_name {
        "argumentDefinitions" => Some(
            r#"
`@argumentDefinitions` is a directive used to specify arguments taken by a fragment.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#argumentdefinitions
"#,
        ),
        "arguments" => Some(
            r#"
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        "uncheckedArguments_DEPRECATED" => Some(
            r#"
DEPRECATED version of `@arguments` directive.
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        _ => None,
    };

    content.map(|value| HoverContents::Scalar(MarkedString::String(value.to_string())))
}

pub fn get_hover_response_contents(
    node_resolution_info: NodeResolutionInfo,
    schema: &Schema,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
) -> Option<HoverContents> {
    let kind = node_resolution_info.kind;

    match kind {
        NodeKind::Variable(type_name) => Some(hover_content_wrapper(type_name)),
        NodeKind::Directive(directive_name, argument_name) => {
            if let Some(argument_definition_hover_info) =
                argument_definition_hover_info(directive_name.lookup())
            {
                return Some(argument_definition_hover_info);
            }

            let schema_directive = schema.get_directive(directive_name)?;

            if let Some(argument_name) = argument_name {
                let argument = schema_directive.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                let directive_text = print_directive(schema, &schema_directive);
                Some(hover_content_wrapper(directive_text))
            }
        }
        NodeKind::FieldName => {
            let field = node_resolution_info
                .type_path
                .resolve_current_field(schema)?;

            let type_name = schema.get_type_string(&field.type_);
            let mut hover_contents: Vec<MarkedString> = vec![graphql_marked_string(format!(
                "{}: {}",
                field.name, type_name
            ))];

            if !field.arguments.is_empty() {
                let mut args_string: Vec<String> =
                    vec!["This field accepts following arguments:".to_string()];
                args_string.push("```".to_string());
                for arg in field.arguments.iter() {
                    let default_value = match &arg.default_value {
                        Some(default_value) => format!(" = {}", default_value),
                        None => "".to_string(),
                    };

                    args_string.push(format!(
                        "- {}: {}{}",
                        arg.name,
                        schema.get_type_string(&arg.type_),
                        default_value,
                    ));
                }
                args_string.push("```".to_string());
                hover_contents.push(MarkedString::String(args_string.join("\n")))
            }
            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::FieldArgument(field_name, argument_name) => {
            let type_ref = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;

            if type_ref.inner().is_object() || type_ref.inner().is_interface() {
                let field_id = schema.named_field(type_ref.inner(), field_name)?;
                let field = schema.field(field_id);
                let argument = field.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                None
            }
        }
        NodeKind::FragmentSpread(fragment_name) => {
            let project_name = node_resolution_info.project_name;
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                let fragment = source_program.fragment(fragment_name)?;
                let mut hover_contents: Vec<MarkedString> = vec![];
                hover_contents.push(graphql_marked_string(format!(
                    "fragment {} on {} {{ ... }}",
                    fragment.name.item,
                    schema.get_type_name(fragment.type_condition),
                )));

                if !fragment.variable_definitions.is_empty() {
                    let mut variables_string: Vec<String> =
                        vec!["This fragment accepts following arguments:".to_string()];
                    variables_string.push("```".to_string());
                    for var in &fragment.variable_definitions {
                        let default_value = match var.default_value.clone() {
                            Some(default_value) => {
                                format!(
                                    ", default_value = {}",
                                    print_value(schema, &Value::Constant(default_value))
                                )
                            }
                            None => "".to_string(),
                        };
                        variables_string.push(format!(
                            "- {}: {}{}",
                            var.name.item,
                            schema.get_type_string(&var.type_),
                            default_value,
                        ));
                    }
                    variables_string.push("```".to_string());
                    hover_contents.push(MarkedString::String(variables_string.join("\n")))
                }

                let fragment_name_details: Vec<&str> = fragment_name.lookup().split('_').collect();
                // We expect the fragment name to be `ComponentName_propName`
                if fragment_name_details.len() == 2 {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        r#"
To consume this fragment spread,
pass it to the component that defined it.

For example:
```js
    <{} {}={{data.{}}} />
```
"#,
                        fragment_name_details[0],
                        fragment_name_details[1],
                        fragment_name_details[1],
                    )));
                } // We may log an error (later), if that is not the case.

                hover_contents.push(MarkedString::String(
                    "@see: https://relay.dev/docs/en/thinking-in-relay#data-masking".to_string(),
                ));
                Some(HoverContents::Array(hover_contents))
            } else {
                None
            }
        }
        NodeKind::OperationDefinition => None,
        NodeKind::FragmentDefinition(_) => None,
    }
}
<commit_msg>Not sure, but maybe let's show a link to GraphQL docs about fragments?<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the hover feature
use crate::lsp::{HoverContents, LanguageString, MarkedString};
use crate::utils::{NodeKind, NodeResolutionInfo};
use graphql_ir::{Program, Value};
use graphql_text_printer::print_value;
use interner::StringKey;
use schema::Schema;
use schema_print::print_directive;
use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

fn graphql_marked_string(value: String) -> MarkedString {
    MarkedString::LanguageString(LanguageString {
        language: "graphql".to_string(),
        value,
    })
}

fn hover_content_wrapper(value: String) -> HoverContents {
    HoverContents::Scalar(graphql_marked_string(value))
}

/// This will provide a more accurate information about some of the specific Relay directives
/// that cannot be expressed via SDL
fn argument_definition_hover_info(directive_name: &str) -> Option<HoverContents> {
    let content = match directive_name {
        "argumentDefinitions" => Some(
            r#"
`@argumentDefinitions` is a directive used to specify arguments taken by a fragment.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#argumentdefinitions
"#,
        ),
        "arguments" => Some(
            r#"
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        "uncheckedArguments_DEPRECATED" => Some(
            r#"
DEPRECATED version of `@arguments` directive.
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        _ => None,
    };

    content.map(|value| HoverContents::Scalar(MarkedString::String(value.to_string())))
}

pub fn get_hover_response_contents(
    node_resolution_info: NodeResolutionInfo,
    schema: &Schema,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
) -> Option<HoverContents> {
    let kind = node_resolution_info.kind;

    match kind {
        NodeKind::Variable(type_name) => Some(hover_content_wrapper(type_name)),
        NodeKind::Directive(directive_name, argument_name) => {
            if let Some(argument_definition_hover_info) =
                argument_definition_hover_info(directive_name.lookup())
            {
                return Some(argument_definition_hover_info);
            }

            let schema_directive = schema.get_directive(directive_name)?;

            if let Some(argument_name) = argument_name {
                let argument = schema_directive.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                let directive_text = print_directive(schema, &schema_directive);
                Some(hover_content_wrapper(directive_text))
            }
        }
        NodeKind::FieldName => {
            let field = node_resolution_info
                .type_path
                .resolve_current_field(schema)?;

            let type_name = schema.get_type_string(&field.type_);
            let mut hover_contents: Vec<MarkedString> = vec![graphql_marked_string(format!(
                "{}: {}",
                field.name, type_name
            ))];

            if !field.arguments.is_empty() {
                let mut args_string: Vec<String> =
                    vec!["This field accepts following arguments:".to_string()];
                args_string.push("```".to_string());
                for arg in field.arguments.iter() {
                    let default_value = match &arg.default_value {
                        Some(default_value) => format!(" = {}", default_value),
                        None => "".to_string(),
                    };

                    args_string.push(format!(
                        "- {}: {}{}",
                        arg.name,
                        schema.get_type_string(&arg.type_),
                        default_value,
                    ));
                }
                args_string.push("```".to_string());
                hover_contents.push(MarkedString::String(args_string.join("\n")))
            }
            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::FieldArgument(field_name, argument_name) => {
            let type_ref = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;

            if type_ref.inner().is_object() || type_ref.inner().is_interface() {
                let field_id = schema.named_field(type_ref.inner(), field_name)?;
                let field = schema.field(field_id);
                let argument = field.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                None
            }
        }
        NodeKind::FragmentSpread(fragment_name) => {
            let project_name = node_resolution_info.project_name;
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                let fragment = source_program.fragment(fragment_name)?;
                let mut hover_contents: Vec<MarkedString> = vec![];
                hover_contents.push(graphql_marked_string(format!(
                    "fragment {} on {} {{ ... }}",
                    fragment.name.item,
                    schema.get_type_name(fragment.type_condition),
                )));

                if !fragment.variable_definitions.is_empty() {
                    let mut variables_string: Vec<String> =
                        vec!["This fragment accepts following arguments:".to_string()];
                    variables_string.push("```".to_string());
                    for var in &fragment.variable_definitions {
                        let default_value = match var.default_value.clone() {
                            Some(default_value) => {
                                format!(
                                    ", default_value = {}",
                                    print_value(schema, &Value::Constant(default_value))
                                )
                            }
                            None => "".to_string(),
                        };
                        variables_string.push(format!(
                            "- {}: {}{}",
                            var.name.item,
                            schema.get_type_string(&var.type_),
                            default_value,
                        ));
                    }
                    variables_string.push("```".to_string());
                    hover_contents.push(MarkedString::String(variables_string.join("\n")))
                }

                let fragment_name_details: Vec<&str> = fragment_name.lookup().split('_').collect();
                // We expect the fragment name to be `ComponentName_propName`
                if fragment_name_details.len() == 2 {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        r#"
To consume this fragment spread,
pass it to the component that defined it.

For example:
```js
    <{} {}={{data.{}}} />
```
"#,
                        fragment_name_details[0],
                        fragment_name_details[1],
                        fragment_name_details[1],
                    )));
                } // We may log an error (later), if that is not the case.

                hover_contents.push(MarkedString::String(
                    "@see: https://relay.dev/docs/en/thinking-in-relay#data-masking".to_string(),
                ));
                Some(HoverContents::Array(hover_contents))
            } else {
                None
            }
        }
        NodeKind::OperationDefinition => None,
        NodeKind::FragmentDefinition(name) => {
            let type_ = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;
            let title = graphql_marked_string(format!(
                "fragment {} on {} {{ .. }}",
                name,
                schema.get_type_name(type_.inner())
            ));

            let hover_contents = vec![
                title,
                MarkedString::String(
                    r#"Fragments let you construct sets of fields,
and then include them in queries where you need to.

---
@see: https://graphql.org/learn/queries/#fragments
"#
                    .to_string(),
                ),
            ];

            Some(HoverContents::Array(hover_contents))
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use CompositorProxy;
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
use script_traits::{LayoutControlMsg, ScriptControlChan, ScriptTaskFactory};
use script_traits::{NewLayoutInfo, ConstellationControlMsg};

use compositor_task;
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
use euclid::rect::{TypedRect};
use euclid::scale_factor::ScaleFactor;
use gfx::paint_task::{ChromeToPaintMsg, LayoutToPaintMsg, PaintTask};
use gfx::font_cache_task::FontCacheTask;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use layers::geometry::DevicePixel;
use msg::compositor_msg::ScriptListener;
use msg::constellation_msg::{ConstellationChan, Failure, FrameId, PipelineId, SubpageId};
use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType, MozBrowserEvent};
use profile_traits::mem as profile_mem;
use profile_traits::time;
use net_traits::ResourceTask;
use net_traits::image_cache_task::ImageCacheTask;
use net_traits::storage_task::StorageTask;
use std::any::Any;
use std::mem;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use url::Url;
use util;
use util::geometry::{PagePx, ViewportPx};
use util::ipc::OptionalIpcSender;
use util::opts;

/// A uniquely-identifiable pipeline of script task, layout task, and paint task.
pub struct Pipeline {
    pub id: PipelineId,
    pub parent_info: Option<(PipelineId, SubpageId)>,
    pub script_chan: ScriptControlChan,
    /// A channel to layout, for performing reflows and shutdown.
    pub layout_chan: LayoutControlChan,
    pub chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
    pub layout_shutdown_port: Receiver<()>,
    pub paint_shutdown_port: Receiver<()>,
    /// URL corresponding to the most recently-loaded page.
    pub url: Url,
    /// The title of the most recently-loaded page.
    pub title: Option<String>,
    pub rect: Option<TypedRect<PagePx, f32>>,
    /// Whether this pipeline is currently running animations. Pipelines that are running
    /// animations cause composites to be continually scheduled.
    pub running_animations: bool,
    pub children: Vec<FrameId>,
}

/// The subset of the pipeline that is needed for layer composition.
#[derive(Clone)]
pub struct CompositionPipeline {
    pub id: PipelineId,
    pub script_chan: ScriptControlChan,
    pub layout_chan: LayoutControlChan,
    pub chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
}

impl Pipeline {
    /// Starts a paint task, layout task, and possibly a script task.
    /// Returns the channels wrapped in a struct.
    /// If script_pipeline is not None, then subpage_id must also be not None.
    pub fn create<LTF,STF>(id: PipelineId,
                           parent_info: Option<(PipelineId, SubpageId)>,
                           constellation_chan: ConstellationChan,
                           compositor_proxy: Box<CompositorProxy+'static+Send>,
                           devtools_chan: Option<Sender<DevtoolsControlMsg>>,
                           image_cache_task: ImageCacheTask,
                           font_cache_task: FontCacheTask,
                           resource_task: ResourceTask,
                           storage_task: StorageTask,
                           time_profiler_chan: time::ProfilerChan,
                           mem_profiler_chan: profile_mem::ProfilerChan,
                           window_rect: Option<TypedRect<PagePx, f32>>,
                           script_chan: Option<ScriptControlChan>,
                           load_data: LoadData,
                           device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>)
                           -> (Pipeline, PipelineContent)
                           where LTF: LayoutTaskFactory, STF:ScriptTaskFactory {
        let (layout_to_paint_chan, layout_to_paint_port) = util::ipc::optional_ipc_channel();
        let (chrome_to_paint_chan, chrome_to_paint_port) = channel();
        let (paint_shutdown_chan, paint_shutdown_port) = channel();
        let (layout_shutdown_chan, layout_shutdown_port) = channel();
        let (pipeline_chan, pipeline_port) = ipc::channel().unwrap();
        let mut pipeline_port = Some(pipeline_port);

        let failure = Failure {
            pipeline_id: id,
            parent_info: parent_info,
        };

        let window_size = window_rect.map(|rect| {
            WindowSizeData {
                visible_viewport: rect.size,
                initial_viewport: rect.size * ScaleFactor::new(1.0),
                device_pixel_ratio: device_pixel_ratio,
            }
        });

        // Route messages coming from content to devtools as appropriate.
        let script_to_devtools_chan = devtools_chan.as_ref().map(|devtools_chan| {
            let (script_to_devtools_chan, script_to_devtools_port) = ipc::channel().unwrap();
            let devtools_chan = (*devtools_chan).clone();
            ROUTER.add_route(script_to_devtools_port.to_opaque(), box move |message| {
                let message: ScriptToDevtoolsControlMsg = message.to().unwrap();
                devtools_chan.send(DevtoolsControlMsg::FromScript(message)).unwrap()
            });
            script_to_devtools_chan
        });

        let (script_chan, script_port) = match script_chan {
            Some(script_chan) => {
                let (containing_pipeline_id, subpage_id) =
                    parent_info.expect("script_pipeline != None but subpage_id == None");
                let new_layout_info = NewLayoutInfo {
                    containing_pipeline_id: containing_pipeline_id,
                    new_pipeline_id: id,
                    subpage_id: subpage_id,
                    load_data: load_data.clone(),
                    paint_chan: box layout_to_paint_chan.clone() as Box<Any + Send>,
                    failure: failure,
                    pipeline_port: mem::replace(&mut pipeline_port, None).unwrap(),
                    layout_shutdown_chan: layout_shutdown_chan.clone(),
                };

                script_chan.0
                           .send(ConstellationControlMsg::AttachLayout(new_layout_info))
                           .unwrap();
                (script_chan, None)
            }
            None => {
                let (script_chan, script_port) = channel();
                (ScriptControlChan(script_chan), Some(script_port))
            }
        };

        let pipeline = Pipeline::new(id,
                                     parent_info,
                                     script_chan.clone(),
                                     LayoutControlChan(pipeline_chan),
                                     chrome_to_paint_chan.clone(),
                                     layout_shutdown_port,
                                     paint_shutdown_port,
                                     load_data.url.clone(),
                                     window_rect);

        let pipeline_content = PipelineContent {
            id: id,
            parent_info: parent_info,
            constellation_chan: constellation_chan,
            compositor_proxy: compositor_proxy,
            devtools_chan: script_to_devtools_chan,
            image_cache_task: image_cache_task,
            font_cache_task: font_cache_task,
            resource_task: resource_task,
            storage_task: storage_task,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
            window_size: window_size,
            script_chan: script_chan,
            load_data: load_data,
            failure: failure,
            script_port: script_port,
            layout_to_paint_chan: layout_to_paint_chan,
            chrome_to_paint_chan: chrome_to_paint_chan,
            layout_to_paint_port: Some(layout_to_paint_port),
            chrome_to_paint_port: Some(chrome_to_paint_port),
            pipeline_port: pipeline_port,
            paint_shutdown_chan: paint_shutdown_chan,
            layout_shutdown_chan: layout_shutdown_chan,
        };

        (pipeline, pipeline_content)
    }

    pub fn new(id: PipelineId,
               parent_info: Option<(PipelineId, SubpageId)>,
               script_chan: ScriptControlChan,
               layout_chan: LayoutControlChan,
               chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
               layout_shutdown_port: Receiver<()>,
               paint_shutdown_port: Receiver<()>,
               url: Url,
               rect: Option<TypedRect<PagePx, f32>>)
               -> Pipeline {
        Pipeline {
            id: id,
            parent_info: parent_info,
            script_chan: script_chan,
            layout_chan: layout_chan,
            chrome_to_paint_chan: chrome_to_paint_chan,
            layout_shutdown_port: layout_shutdown_port,
            paint_shutdown_port: paint_shutdown_port,
            url: url,
            title: None,
            children: vec!(),
            rect: rect,
            running_animations: false,
        }
    }

    pub fn grant_paint_permission(&self) {
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::PaintPermissionGranted);
    }

    pub fn revoke_paint_permission(&self) {
        debug!("pipeline revoking paint channel paint permission");
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::PaintPermissionRevoked);
    }

    pub fn exit(&self, exit_type: PipelineExitType) {
        debug!("pipeline {:?} exiting", self.id);

        // Script task handles shutting down layout, and layout handles shutting down the painter.
        // For now, if the script task has failed, we give up on clean shutdown.
        let ScriptControlChan(ref chan) = self.script_chan;
        if chan.send(ConstellationControlMsg::ExitPipeline(self.id, exit_type)).is_ok() {
            // Wait until all slave tasks have terminated and run destructors
            // NOTE: We don't wait for script task as we don't always own it
            let _ = self.paint_shutdown_port.recv();
            let _ = self.layout_shutdown_port.recv();
        }

    }

    pub fn freeze(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(ConstellationControlMsg::Freeze(self.id)).unwrap();
    }

    pub fn thaw(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(ConstellationControlMsg::Thaw(self.id)).unwrap();
    }

    pub fn force_exit(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(
            ConstellationControlMsg::ExitPipeline(self.id,
                                                  PipelineExitType::PipelineOnly)).unwrap();
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::Exit(
                    None,
                    PipelineExitType::PipelineOnly));
        let LayoutControlChan(ref layout_channel) = self.layout_chan;
        let _ = layout_channel.send(
            LayoutControlMsg::ExitNow(PipelineExitType::PipelineOnly)).unwrap();
    }

    pub fn to_sendable(&self) -> CompositionPipeline {
        CompositionPipeline {
            id: self.id.clone(),
            script_chan: self.script_chan.clone(),
            layout_chan: self.layout_chan.clone(),
            chrome_to_paint_chan: self.chrome_to_paint_chan.clone(),
        }
    }

    pub fn add_child(&mut self, frame_id: FrameId) {
        self.children.push(frame_id);
    }

    pub fn remove_child(&mut self, frame_id: FrameId) {
        let index = self.children.iter().position(|id| *id == frame_id).unwrap();
        self.children.remove(index);
    }

    pub fn trigger_mozbrowser_event(&self,
                                     subpage_id: SubpageId,
                                     event: MozBrowserEvent) {
        assert!(opts::experimental_enabled());

        let ScriptControlChan(ref script_channel) = self.script_chan;
        let event = ConstellationControlMsg::MozBrowserEvent(self.id,
                                                             subpage_id,
                                                             event);
        script_channel.send(event).unwrap();
    }
}

pub struct PipelineContent {
    id: PipelineId,
    parent_info: Option<(PipelineId, SubpageId)>,
    constellation_chan: ConstellationChan,
    compositor_proxy: Box<CompositorProxy + Send + 'static>,
    devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
    image_cache_task: ImageCacheTask,
    font_cache_task: FontCacheTask,
    resource_task: ResourceTask,
    storage_task: StorageTask,
    time_profiler_chan: time::ProfilerChan,
    mem_profiler_chan: profile_mem::ProfilerChan,
    window_size: Option<WindowSizeData>,
    script_chan: ScriptControlChan,
    load_data: LoadData,
    failure: Failure,
    script_port: Option<Receiver<ConstellationControlMsg>>,
    layout_to_paint_chan: OptionalIpcSender<LayoutToPaintMsg>,
    chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
    layout_to_paint_port: Option<Receiver<LayoutToPaintMsg>>,
    chrome_to_paint_port: Option<Receiver<ChromeToPaintMsg>>,
    paint_shutdown_chan: Sender<()>,
    pipeline_port: Option<IpcReceiver<LayoutControlMsg>>,
    layout_shutdown_chan: Sender<()>,
}

impl PipelineContent {
    pub fn start_all<LTF,STF>(mut self) where LTF: LayoutTaskFactory, STF: ScriptTaskFactory {
        let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>);
        let (script_to_compositor_chan, script_to_compositor_port) = ipc::channel().unwrap();

        self.start_paint_task();

        let compositor_proxy_for_script_listener_thread =
            self.compositor_proxy.clone_compositor_proxy();
        thread::spawn(move || {
            compositor_task::run_script_listener_thread(
                compositor_proxy_for_script_listener_thread,
                script_to_compositor_port)
        });

        ScriptTaskFactory::create(None::<&mut STF>,
                                  self.id,
                                  self.parent_info,
                                  ScriptListener::new(script_to_compositor_chan),
                                  &layout_pair,
                                  self.script_chan.0.clone(),
                                  mem::replace(&mut self.script_port, None).unwrap(),
                                  self.constellation_chan.clone(),
                                  self.failure.clone(),
                                  self.resource_task,
                                  self.storage_task.clone(),
                                  self.image_cache_task.clone(),
                                  self.mem_profiler_chan.clone(),
                                  self.devtools_chan,
                                  self.window_size,
                                  self.load_data.clone());

        LayoutTaskFactory::create(None::<&mut LTF>,
                                  self.id,
                                  self.load_data.url.clone(),
                                  self.parent_info.is_some(),
                                  layout_pair,
                                  self.pipeline_port.unwrap(),
                                  self.constellation_chan,
                                  self.failure,
                                  self.script_chan.0.clone(),
                                  self.layout_to_paint_chan.clone(),
                                  self.image_cache_task,
                                  self.font_cache_task,
                                  self.time_profiler_chan,
                                  self.mem_profiler_chan,
                                  self.layout_shutdown_chan);
    }

    pub fn start_paint_task(&mut self) {
        PaintTask::create(self.id,
                          self.load_data.url.clone(),
                          self.chrome_to_paint_chan.clone(),
                          mem::replace(&mut self.layout_to_paint_port, None).unwrap(),
                          mem::replace(&mut self.chrome_to_paint_port, None).unwrap(),
                          self.compositor_proxy.clone_compositor_proxy(),
                          self.constellation_chan.clone(),
                          self.font_cache_task.clone(),
                          self.failure.clone(),
                          self.time_profiler_chan.clone(),
                          self.mem_profiler_chan.clone(),
                          self.paint_shutdown_chan.clone());

    }
}

<commit_msg>Store a Sender<ConstellationControlMsg> in PipelineContent.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use CompositorProxy;
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
use script_traits::{LayoutControlMsg, ScriptControlChan, ScriptTaskFactory};
use script_traits::{NewLayoutInfo, ConstellationControlMsg};

use compositor_task;
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
use euclid::rect::{TypedRect};
use euclid::scale_factor::ScaleFactor;
use gfx::paint_task::{ChromeToPaintMsg, LayoutToPaintMsg, PaintTask};
use gfx::font_cache_task::FontCacheTask;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use layers::geometry::DevicePixel;
use msg::compositor_msg::ScriptListener;
use msg::constellation_msg::{ConstellationChan, Failure, FrameId, PipelineId, SubpageId};
use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType, MozBrowserEvent};
use profile_traits::mem as profile_mem;
use profile_traits::time;
use net_traits::ResourceTask;
use net_traits::image_cache_task::ImageCacheTask;
use net_traits::storage_task::StorageTask;
use std::any::Any;
use std::mem;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use url::Url;
use util;
use util::geometry::{PagePx, ViewportPx};
use util::ipc::OptionalIpcSender;
use util::opts;

/// A uniquely-identifiable pipeline of script task, layout task, and paint task.
pub struct Pipeline {
    pub id: PipelineId,
    pub parent_info: Option<(PipelineId, SubpageId)>,
    pub script_chan: ScriptControlChan,
    /// A channel to layout, for performing reflows and shutdown.
    pub layout_chan: LayoutControlChan,
    pub chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
    pub layout_shutdown_port: Receiver<()>,
    pub paint_shutdown_port: Receiver<()>,
    /// URL corresponding to the most recently-loaded page.
    pub url: Url,
    /// The title of the most recently-loaded page.
    pub title: Option<String>,
    pub rect: Option<TypedRect<PagePx, f32>>,
    /// Whether this pipeline is currently running animations. Pipelines that are running
    /// animations cause composites to be continually scheduled.
    pub running_animations: bool,
    pub children: Vec<FrameId>,
}

/// The subset of the pipeline that is needed for layer composition.
#[derive(Clone)]
pub struct CompositionPipeline {
    pub id: PipelineId,
    pub script_chan: ScriptControlChan,
    pub layout_chan: LayoutControlChan,
    pub chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
}

impl Pipeline {
    /// Starts a paint task, layout task, and possibly a script task.
    /// Returns the channels wrapped in a struct.
    /// If script_pipeline is not None, then subpage_id must also be not None.
    pub fn create<LTF,STF>(id: PipelineId,
                           parent_info: Option<(PipelineId, SubpageId)>,
                           constellation_chan: ConstellationChan,
                           compositor_proxy: Box<CompositorProxy+'static+Send>,
                           devtools_chan: Option<Sender<DevtoolsControlMsg>>,
                           image_cache_task: ImageCacheTask,
                           font_cache_task: FontCacheTask,
                           resource_task: ResourceTask,
                           storage_task: StorageTask,
                           time_profiler_chan: time::ProfilerChan,
                           mem_profiler_chan: profile_mem::ProfilerChan,
                           window_rect: Option<TypedRect<PagePx, f32>>,
                           script_chan: Option<ScriptControlChan>,
                           load_data: LoadData,
                           device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>)
                           -> (Pipeline, PipelineContent)
                           where LTF: LayoutTaskFactory, STF:ScriptTaskFactory {
        let (layout_to_paint_chan, layout_to_paint_port) = util::ipc::optional_ipc_channel();
        let (chrome_to_paint_chan, chrome_to_paint_port) = channel();
        let (paint_shutdown_chan, paint_shutdown_port) = channel();
        let (layout_shutdown_chan, layout_shutdown_port) = channel();
        let (pipeline_chan, pipeline_port) = ipc::channel().unwrap();
        let mut pipeline_port = Some(pipeline_port);

        let failure = Failure {
            pipeline_id: id,
            parent_info: parent_info,
        };

        let window_size = window_rect.map(|rect| {
            WindowSizeData {
                visible_viewport: rect.size,
                initial_viewport: rect.size * ScaleFactor::new(1.0),
                device_pixel_ratio: device_pixel_ratio,
            }
        });

        // Route messages coming from content to devtools as appropriate.
        let script_to_devtools_chan = devtools_chan.as_ref().map(|devtools_chan| {
            let (script_to_devtools_chan, script_to_devtools_port) = ipc::channel().unwrap();
            let devtools_chan = (*devtools_chan).clone();
            ROUTER.add_route(script_to_devtools_port.to_opaque(), box move |message| {
                let message: ScriptToDevtoolsControlMsg = message.to().unwrap();
                devtools_chan.send(DevtoolsControlMsg::FromScript(message)).unwrap()
            });
            script_to_devtools_chan
        });

        let (script_chan, script_port) = match script_chan {
            Some(script_chan) => {
                let (containing_pipeline_id, subpage_id) =
                    parent_info.expect("script_pipeline != None but subpage_id == None");
                let new_layout_info = NewLayoutInfo {
                    containing_pipeline_id: containing_pipeline_id,
                    new_pipeline_id: id,
                    subpage_id: subpage_id,
                    load_data: load_data.clone(),
                    paint_chan: box layout_to_paint_chan.clone() as Box<Any + Send>,
                    failure: failure,
                    pipeline_port: mem::replace(&mut pipeline_port, None).unwrap(),
                    layout_shutdown_chan: layout_shutdown_chan.clone(),
                };

                script_chan.0
                           .send(ConstellationControlMsg::AttachLayout(new_layout_info))
                           .unwrap();
                (script_chan, None)
            }
            None => {
                let (script_chan, script_port) = channel();
                (ScriptControlChan(script_chan), Some(script_port))
            }
        };

        let pipeline = Pipeline::new(id,
                                     parent_info,
                                     script_chan.clone(),
                                     LayoutControlChan(pipeline_chan),
                                     chrome_to_paint_chan.clone(),
                                     layout_shutdown_port,
                                     paint_shutdown_port,
                                     load_data.url.clone(),
                                     window_rect);

        let pipeline_content = PipelineContent {
            id: id,
            parent_info: parent_info,
            constellation_chan: constellation_chan,
            compositor_proxy: compositor_proxy,
            devtools_chan: script_to_devtools_chan,
            image_cache_task: image_cache_task,
            font_cache_task: font_cache_task,
            resource_task: resource_task,
            storage_task: storage_task,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
            window_size: window_size,
            script_chan: script_chan.0,
            load_data: load_data,
            failure: failure,
            script_port: script_port,
            layout_to_paint_chan: layout_to_paint_chan,
            chrome_to_paint_chan: chrome_to_paint_chan,
            layout_to_paint_port: Some(layout_to_paint_port),
            chrome_to_paint_port: Some(chrome_to_paint_port),
            pipeline_port: pipeline_port,
            paint_shutdown_chan: paint_shutdown_chan,
            layout_shutdown_chan: layout_shutdown_chan,
        };

        (pipeline, pipeline_content)
    }

    pub fn new(id: PipelineId,
               parent_info: Option<(PipelineId, SubpageId)>,
               script_chan: ScriptControlChan,
               layout_chan: LayoutControlChan,
               chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
               layout_shutdown_port: Receiver<()>,
               paint_shutdown_port: Receiver<()>,
               url: Url,
               rect: Option<TypedRect<PagePx, f32>>)
               -> Pipeline {
        Pipeline {
            id: id,
            parent_info: parent_info,
            script_chan: script_chan,
            layout_chan: layout_chan,
            chrome_to_paint_chan: chrome_to_paint_chan,
            layout_shutdown_port: layout_shutdown_port,
            paint_shutdown_port: paint_shutdown_port,
            url: url,
            title: None,
            children: vec!(),
            rect: rect,
            running_animations: false,
        }
    }

    pub fn grant_paint_permission(&self) {
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::PaintPermissionGranted);
    }

    pub fn revoke_paint_permission(&self) {
        debug!("pipeline revoking paint channel paint permission");
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::PaintPermissionRevoked);
    }

    pub fn exit(&self, exit_type: PipelineExitType) {
        debug!("pipeline {:?} exiting", self.id);

        // Script task handles shutting down layout, and layout handles shutting down the painter.
        // For now, if the script task has failed, we give up on clean shutdown.
        let ScriptControlChan(ref chan) = self.script_chan;
        if chan.send(ConstellationControlMsg::ExitPipeline(self.id, exit_type)).is_ok() {
            // Wait until all slave tasks have terminated and run destructors
            // NOTE: We don't wait for script task as we don't always own it
            let _ = self.paint_shutdown_port.recv();
            let _ = self.layout_shutdown_port.recv();
        }

    }

    pub fn freeze(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(ConstellationControlMsg::Freeze(self.id)).unwrap();
    }

    pub fn thaw(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(ConstellationControlMsg::Thaw(self.id)).unwrap();
    }

    pub fn force_exit(&self) {
        let ScriptControlChan(ref script_channel) = self.script_chan;
        let _ = script_channel.send(
            ConstellationControlMsg::ExitPipeline(self.id,
                                                  PipelineExitType::PipelineOnly)).unwrap();
        let _ = self.chrome_to_paint_chan.send(ChromeToPaintMsg::Exit(
                    None,
                    PipelineExitType::PipelineOnly));
        let LayoutControlChan(ref layout_channel) = self.layout_chan;
        let _ = layout_channel.send(
            LayoutControlMsg::ExitNow(PipelineExitType::PipelineOnly)).unwrap();
    }

    pub fn to_sendable(&self) -> CompositionPipeline {
        CompositionPipeline {
            id: self.id.clone(),
            script_chan: self.script_chan.clone(),
            layout_chan: self.layout_chan.clone(),
            chrome_to_paint_chan: self.chrome_to_paint_chan.clone(),
        }
    }

    pub fn add_child(&mut self, frame_id: FrameId) {
        self.children.push(frame_id);
    }

    pub fn remove_child(&mut self, frame_id: FrameId) {
        let index = self.children.iter().position(|id| *id == frame_id).unwrap();
        self.children.remove(index);
    }

    pub fn trigger_mozbrowser_event(&self,
                                     subpage_id: SubpageId,
                                     event: MozBrowserEvent) {
        assert!(opts::experimental_enabled());

        let ScriptControlChan(ref script_channel) = self.script_chan;
        let event = ConstellationControlMsg::MozBrowserEvent(self.id,
                                                             subpage_id,
                                                             event);
        script_channel.send(event).unwrap();
    }
}

pub struct PipelineContent {
    id: PipelineId,
    parent_info: Option<(PipelineId, SubpageId)>,
    constellation_chan: ConstellationChan,
    compositor_proxy: Box<CompositorProxy + Send + 'static>,
    devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
    image_cache_task: ImageCacheTask,
    font_cache_task: FontCacheTask,
    resource_task: ResourceTask,
    storage_task: StorageTask,
    time_profiler_chan: time::ProfilerChan,
    mem_profiler_chan: profile_mem::ProfilerChan,
    window_size: Option<WindowSizeData>,
    script_chan: Sender<ConstellationControlMsg>,
    load_data: LoadData,
    failure: Failure,
    script_port: Option<Receiver<ConstellationControlMsg>>,
    layout_to_paint_chan: OptionalIpcSender<LayoutToPaintMsg>,
    chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
    layout_to_paint_port: Option<Receiver<LayoutToPaintMsg>>,
    chrome_to_paint_port: Option<Receiver<ChromeToPaintMsg>>,
    paint_shutdown_chan: Sender<()>,
    pipeline_port: Option<IpcReceiver<LayoutControlMsg>>,
    layout_shutdown_chan: Sender<()>,
}

impl PipelineContent {
    pub fn start_all<LTF,STF>(mut self) where LTF: LayoutTaskFactory, STF: ScriptTaskFactory {
        let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>);
        let (script_to_compositor_chan, script_to_compositor_port) = ipc::channel().unwrap();

        self.start_paint_task();

        let compositor_proxy_for_script_listener_thread =
            self.compositor_proxy.clone_compositor_proxy();
        thread::spawn(move || {
            compositor_task::run_script_listener_thread(
                compositor_proxy_for_script_listener_thread,
                script_to_compositor_port)
        });

        ScriptTaskFactory::create(None::<&mut STF>,
                                  self.id,
                                  self.parent_info,
                                  ScriptListener::new(script_to_compositor_chan),
                                  &layout_pair,
                                  self.script_chan.clone(),
                                  mem::replace(&mut self.script_port, None).unwrap(),
                                  self.constellation_chan.clone(),
                                  self.failure.clone(),
                                  self.resource_task,
                                  self.storage_task.clone(),
                                  self.image_cache_task.clone(),
                                  self.mem_profiler_chan.clone(),
                                  self.devtools_chan,
                                  self.window_size,
                                  self.load_data.clone());

        LayoutTaskFactory::create(None::<&mut LTF>,
                                  self.id,
                                  self.load_data.url.clone(),
                                  self.parent_info.is_some(),
                                  layout_pair,
                                  self.pipeline_port.unwrap(),
                                  self.constellation_chan,
                                  self.failure,
                                  self.script_chan.clone(),
                                  self.layout_to_paint_chan.clone(),
                                  self.image_cache_task,
                                  self.font_cache_task,
                                  self.time_profiler_chan,
                                  self.mem_profiler_chan,
                                  self.layout_shutdown_chan);
    }

    pub fn start_paint_task(&mut self) {
        PaintTask::create(self.id,
                          self.load_data.url.clone(),
                          self.chrome_to_paint_chan.clone(),
                          mem::replace(&mut self.layout_to_paint_port, None).unwrap(),
                          mem::replace(&mut self.chrome_to_paint_port, None).unwrap(),
                          self.compositor_proxy.clone_compositor_proxy(),
                          self.constellation_chan.clone(),
                          self.font_cache_task.clone(),
                          self.failure.clone(),
                          self.time_profiler_chan.clone(),
                          self.mem_profiler_chan.clone(),
                          self.paint_shutdown_chan.clone());

    }
}

<|endoftext|>


<commit_before>// Copyright 2015, Igor Shaula
// Licensed under the MIT License <LICENSE or
// http://opensource.org/licenses/MIT>. This file
// may not be copied, modified, or distributed
// except according to those terms.
extern crate winreg;
use std::io;
use winreg::RegKey;
use winreg::enums::*;
use winreg::transaction::Transaction;

fn main() {
    let t = Transaction::new().unwrap();
    let hkcu = RegKey::predef(HKEY_CURRENT_USER);
    let key = hkcu.create_subkey_transacted("Software\\RustTransaction", &t).unwrap();
    key.set_value("TestQWORD", &1234567891011121314u64).unwrap();
    key.set_value("TestDWORD", &1234567890u32).unwrap();

    println!("Commit transaction? [y/N]:");
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    input = input.trim_right().to_owned();
    if input == "y" || input == "Y" {
        t.commit().unwrap();
        println!("Transaction commited.");
    }
    else {
        // this is optional, if transaction wasn't commited,
        // it will be rolled back on disposal
        t.rollback().unwrap();

        println!("Transaction wasn't commited, it will be rolled back.");
    }
}
<commit_msg>Fix typo ('commited', needs 2nd 't')<commit_after>// Copyright 2015, Igor Shaula
// Licensed under the MIT License <LICENSE or
// http://opensource.org/licenses/MIT>. This file
// may not be copied, modified, or distributed
// except according to those terms.
extern crate winreg;
use std::io;
use winreg::RegKey;
use winreg::enums::*;
use winreg::transaction::Transaction;

fn main() {
    let t = Transaction::new().unwrap();
    let hkcu = RegKey::predef(HKEY_CURRENT_USER);
    let key = hkcu.create_subkey_transacted("Software\\RustTransaction", &t).unwrap();
    key.set_value("TestQWORD", &1234567891011121314u64).unwrap();
    key.set_value("TestDWORD", &1234567890u32).unwrap();

    println!("Commit transaction? [y/N]:");
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    input = input.trim_right().to_owned();
    if input == "y" || input == "Y" {
        t.commit().unwrap();
        println!("Transaction committed.");
    }
    else {
        // this is optional, if transaction wasn't committed,
        // it will be rolled back on disposal
        t.rollback().unwrap();

        println!("Transaction wasn't committed, it will be rolled back.");
    }
}
<|endoftext|>


<commit_before>use common::context::*;
use common::scheduler::*;

use graphics::bmp::*;

use programs::common::*;
use programs::editor::*;
use programs::executor::*;
use programs::filemanager::*;
use programs::viewer::*;

pub struct Session {
    pub display: Display,
    pub background: BMP,
    pub cursor: BMP,
    pub icon: BMP,
    pub mouse_point: Point,
    last_mouse_event: MouseEvent,
    pub items: Vec<Box<SessionItem>>,
    pub windows: Vec<*mut Window>,
    pub windows_ordered: Vec<*mut Window>,
    pub redraw: usize
}

impl Session {
    pub fn new() -> Session {
        unsafe {
            Session {
                display: Display::root(),
                background: BMP::new(),
                cursor: BMP::new(),
                icon: BMP::new(),
                mouse_point: Point::new(0, 0),
                last_mouse_event: MouseEvent {
                    x: 0,
                    y: 0,
                    left_button: false,
                    middle_button: false,
                    right_button: false,
                    valid: false
                },
                items: Vec::new(),
                windows: Vec::new(),
                windows_ordered: Vec::new(),
                redraw: REDRAW_ALL
            }
        }
    }

    pub unsafe fn add_window(&mut self, add_window_ptr: *mut Window){
        self.windows.push(add_window_ptr);
        self.windows_ordered.push(add_window_ptr);
        self.redraw = max(self.redraw, REDRAW_ALL);
    }

    pub unsafe fn remove_window(&mut self, remove_window_ptr: *mut Window){
        let mut i = 0;
        while i < self.windows.len() {
            let mut remove = false;

            match self.windows.get(i) {
                Option::Some(window_ptr) => if *window_ptr == remove_window_ptr {
                    remove = true;
                }else{
                    i += 1;
                },
                Option::None => break
            }

            if remove {
                self.windows.remove(i);
                self.redraw = max(self.redraw, REDRAW_ALL);
            }
        }

        i = 0;
        while i < self.windows_ordered.len() {
            let mut remove = false;

            match self.windows_ordered.get(i) {
                Option::Some(window_ptr) => if *window_ptr == remove_window_ptr {
                    remove = true;
                }else{
                    i += 1;
                },
                Option::None => break
            }

            if remove {
                self.windows_ordered.remove(i);
                self.redraw = max(self.redraw, REDRAW_ALL);
            }
        }
    }

    pub unsafe fn on_irq(&mut self, irq: u8){
        for item in self.items.iter() {
            let reenable = start_no_ints();
            item.on_irq(irq);
            end_no_ints(reenable);
        }
    }

    pub unsafe fn on_poll(&mut self){
        for item in self.items.iter() {
            let reenable = start_no_ints();
            item.on_poll();
            end_no_ints(reenable);
        }
    }

    pub fn open(&self, url: &URL) -> Box<Resource>{
        if url.scheme().len() == 0 {
            let mut list = String::new();

            for item in self.items.iter() {
                let scheme = item.scheme();
                if scheme.len() > 0 {
                    if list.len() > 0 {
                        list = list + "\n" + scheme;
                    }else{
                        list = scheme;
                    }
                }
            }

            return box VecResource::new(URL::new(), ResourceType::Dir, list.to_utf8());
        }else{
            for item in self.items.iter() {
                if item.scheme() == url.scheme() {
                    return item.open(url);
                }
            }
            return box NoneResource;
        }
    }

    fn item_main(&mut self, mut item: Box<SessionItem>, url: URL){
        Context::spawn(box move ||{
            item.main(url);
        });
    }

    fn on_key(&mut self, key_event: KeyEvent){
        if self.windows.len() > 0 {
            match self.windows.get(self.windows.len() - 1){
                Option::Some(window_ptr) => {
                    unsafe{
                        (**window_ptr).on_key(key_event);
                        self.redraw = max(self.redraw, REDRAW_ALL);
                    }
                },
                Option::None => ()
            }
        }
    }

    fn on_mouse(&mut self, mouse_event: MouseEvent){
        let mut catcher = -1;
        for reverse_i in 0..self.windows.len() {
            let i = self.windows.len() - 1 - reverse_i;
            match self.windows.get(i){
                Option::Some(window_ptr) => unsafe{
                    if (**window_ptr).on_mouse(mouse_event, catcher < 0) {
                        catcher = i as isize;

                        self.redraw = max(self.redraw, REDRAW_ALL);
                    }
                },
                Option::None => ()
            }
        }

        //Not caught, can be caught by task bar
        if catcher < 0 {
            if mouse_event.left_button &&  !self.last_mouse_event.left_button && mouse_event.y >= self.display.height as isize - 32 {
                if mouse_event.x <= 56 {
                    self.item_main(box FileManager::new(), URL::from_string(&"file:///".to_string()));
                }else{
                    for i in 0..self.windows_ordered.len() {
                        let chars = 16;
                        let x = 5*8 + 2*8 + (chars*8 + 3*8) * i as isize;
                        let w = chars*8 + 2*8;
                        if mouse_event.x >= x && mouse_event.x < x + w {
                            match self.windows_ordered.get(i) {
                                Option::Some(window_ptr) => unsafe {
                                    for j in 0..self.windows.len() {
                                        match self.windows.get(j){
                                            Option::Some(catcher_window_ptr) => if catcher_window_ptr == window_ptr {
                                                if j == self.windows.len() - 1 {
                                                    (**window_ptr).minimized = !(**window_ptr).minimized;
                                                }else{
                                                    catcher = j as isize;
                                                    (**window_ptr).minimized = false;
                                                }
                                                break;
                                            },
                                            Option::None => break
                                        }
                                    }
                                    self.redraw = max(self.redraw, REDRAW_ALL);
                                },
                                Option::None => ()
                            }
                            break;
                        }
                    }
                }
            }
        }

        if catcher >= 0 && catcher < self.windows.len() as isize - 1 {
            match self.windows.remove(catcher as usize){
                Option::Some(window_ptr) => self.windows.push(window_ptr),
                Option::None => ()
            }
        }

        self.last_mouse_event = mouse_event;
    }

    pub unsafe fn redraw(&mut self){
        if self.redraw > REDRAW_NONE {
            if self.redraw >= REDRAW_ALL {
                self.display.set(Color::new(64, 64, 64));
                if self.background.data > 0 {
                    self.display.image(Point::new((self.display.width as isize - self.background.size.width as isize)/2, (self.display.height as isize - self.background.size.height as isize)/2), self.background.data, self.background.size);
                }

                for i in 0..self.windows.len(){
                    match self.windows.get(i) {
                        Option::Some(window_ptr) => {
                            (**window_ptr).focused = i == self.windows.len() - 1;
                            (**window_ptr).draw(&self.display);
                        },
                        Option::None => ()
                    }
                }

                self.display.rect(Point::new(0, self.display.height as isize - 32), Size::new(self.display.width, 32), Color::new(0, 0, 0));
                if self.icon.data > 0 {
                    self.display.image_alpha(Point::new(12, self.display.height as isize - 32), self.icon.data, self.icon.size);
                }else{
                    self.display.text(Point::new(8, self.display.height as isize - 24), &String::from_str("Redox"), Color::new(255, 255, 255));
                }

                for i in 0..self.windows_ordered.len() {
                    match self.windows_ordered.get(i) {
                        Option::Some(window_ptr) => {
                            let chars = 16;
                            let x = 5*8 + 2*8 + (chars*8 + 3*8) * i as isize;
                            let w = chars*8 + 2*8;
                            self.display.rect(Point::new(x, self.display.height as isize - 32), Size::new(w as usize, 32), (**window_ptr).border_color);
                            self.display.text(Point::new(x + 8, self.display.height as isize - 24), &(**window_ptr).title.substr(0, chars as usize), (**window_ptr).title_color);
                        },
                        Option::None => ()
                    }
                }
            }

            let reenable = start_no_ints();

            self.display.flip();

            if self.cursor.data > 0 {
                self.display.image_alpha_onscreen(self.mouse_point, self.cursor.data, self.cursor.size);
            }else{
                self.display.char_onscreen(Point::new(self.mouse_point.x - 3, self.mouse_point.y - 9), 'X', Color::new(255, 255, 255));
            }

            self.redraw = REDRAW_NONE;

            end_no_ints(reenable);
        }
    }

    pub fn event(&mut self, event: Event){
        match event.to_option() {
            EventOption::Mouse(mouse_event) => self.on_mouse(mouse_event),
            EventOption::Key(key_event) => self.on_key(key_event),
            EventOption::Redraw(redraw_event) => self.redraw = max(self.redraw, redraw_event.redraw),
            EventOption::Open(open_event) => {
                let url_string = open_event.url_string;
                let url = URL::from_string(&url_string);

                if url_string.ends_with(".md".to_string()) || url_string.ends_with(".rs".to_string()) || url_string.ends_with(".sh".to_string()){
                    self.item_main(box Editor::new(), url);
                }else if url_string.ends_with(".bin".to_string()){
                    self.item_main(box Executor::new(), url);
                }else if url_string.ends_with(".bmp".to_string()){
                    self.item_main(box Viewer::new(), url);
                }else{
                    d("No program found: ");
                    url.d();
                    dl();
                }
            }
            _ => ()
        }
    }
}
<commit_msg>Modify size of taskbar items<commit_after>use common::context::*;
use common::scheduler::*;

use graphics::bmp::*;

use programs::common::*;
use programs::editor::*;
use programs::executor::*;
use programs::filemanager::*;
use programs::viewer::*;

pub struct Session {
    pub display: Display,
    pub background: BMP,
    pub cursor: BMP,
    pub icon: BMP,
    pub mouse_point: Point,
    last_mouse_event: MouseEvent,
    pub items: Vec<Box<SessionItem>>,
    pub windows: Vec<*mut Window>,
    pub windows_ordered: Vec<*mut Window>,
    pub redraw: usize
}

impl Session {
    pub fn new() -> Session {
        unsafe {
            Session {
                display: Display::root(),
                background: BMP::new(),
                cursor: BMP::new(),
                icon: BMP::new(),
                mouse_point: Point::new(0, 0),
                last_mouse_event: MouseEvent {
                    x: 0,
                    y: 0,
                    left_button: false,
                    middle_button: false,
                    right_button: false,
                    valid: false
                },
                items: Vec::new(),
                windows: Vec::new(),
                windows_ordered: Vec::new(),
                redraw: REDRAW_ALL
            }
        }
    }

    pub unsafe fn add_window(&mut self, add_window_ptr: *mut Window){
        self.windows.push(add_window_ptr);
        self.windows_ordered.push(add_window_ptr);
        self.redraw = max(self.redraw, REDRAW_ALL);
    }

    pub unsafe fn remove_window(&mut self, remove_window_ptr: *mut Window){
        let mut i = 0;
        while i < self.windows.len() {
            let mut remove = false;

            match self.windows.get(i) {
                Option::Some(window_ptr) => if *window_ptr == remove_window_ptr {
                    remove = true;
                }else{
                    i += 1;
                },
                Option::None => break
            }

            if remove {
                self.windows.remove(i);
                self.redraw = max(self.redraw, REDRAW_ALL);
            }
        }

        i = 0;
        while i < self.windows_ordered.len() {
            let mut remove = false;

            match self.windows_ordered.get(i) {
                Option::Some(window_ptr) => if *window_ptr == remove_window_ptr {
                    remove = true;
                }else{
                    i += 1;
                },
                Option::None => break
            }

            if remove {
                self.windows_ordered.remove(i);
                self.redraw = max(self.redraw, REDRAW_ALL);
            }
        }
    }

    pub unsafe fn on_irq(&mut self, irq: u8){
        for item in self.items.iter() {
            let reenable = start_no_ints();
            item.on_irq(irq);
            end_no_ints(reenable);
        }
    }

    pub unsafe fn on_poll(&mut self){
        for item in self.items.iter() {
            let reenable = start_no_ints();
            item.on_poll();
            end_no_ints(reenable);
        }
    }

    pub fn open(&self, url: &URL) -> Box<Resource>{
        if url.scheme().len() == 0 {
            let mut list = String::new();

            for item in self.items.iter() {
                let scheme = item.scheme();
                if scheme.len() > 0 {
                    if list.len() > 0 {
                        list = list + "\n" + scheme;
                    }else{
                        list = scheme;
                    }
                }
            }

            return box VecResource::new(URL::new(), ResourceType::Dir, list.to_utf8());
        }else{
            for item in self.items.iter() {
                if item.scheme() == url.scheme() {
                    return item.open(url);
                }
            }
            return box NoneResource;
        }
    }

    fn item_main(&mut self, mut item: Box<SessionItem>, url: URL){
        Context::spawn(box move ||{
            item.main(url);
        });
    }

    fn on_key(&mut self, key_event: KeyEvent){
        if self.windows.len() > 0 {
            match self.windows.get(self.windows.len() - 1){
                Option::Some(window_ptr) => {
                    unsafe{
                        (**window_ptr).on_key(key_event);
                        self.redraw = max(self.redraw, REDRAW_ALL);
                    }
                },
                Option::None => ()
            }
        }
    }

    fn on_mouse(&mut self, mouse_event: MouseEvent){
        let mut catcher = -1;
        for reverse_i in 0..self.windows.len() {
            let i = self.windows.len() - 1 - reverse_i;
            match self.windows.get(i){
                Option::Some(window_ptr) => unsafe{
                    if (**window_ptr).on_mouse(mouse_event, catcher < 0) {
                        catcher = i as isize;

                        self.redraw = max(self.redraw, REDRAW_ALL);
                    }
                },
                Option::None => ()
            }
        }

        //Not caught, can be caught by task bar
        if catcher < 0 {
            if mouse_event.left_button &&  !self.last_mouse_event.left_button && mouse_event.y >= self.display.height as isize - 32 {
                if mouse_event.x <= 56 {
                    self.item_main(box FileManager::new(), URL::from_string(&"file:///".to_string()));
                }else{
                    let mut chars = 32;
                    while chars > 4 && (chars*8 + 3*4) * self.windows.len() > self.display.width {
                        chars -= 1;
                    }

                    for i in 0..self.windows_ordered.len() {
                        let x = (5*8 + 2*8 + (chars*8 + 3*4) * i) as isize;
                        let w = (chars*8 + 2*4) as usize;
                        if mouse_event.x >= x && mouse_event.x < x + w as isize {
                            match self.windows_ordered.get(i) {
                                Option::Some(window_ptr) => unsafe {
                                    for j in 0..self.windows.len() {
                                        match self.windows.get(j){
                                            Option::Some(catcher_window_ptr) => if catcher_window_ptr == window_ptr {
                                                if j == self.windows.len() - 1 {
                                                    (**window_ptr).minimized = !(**window_ptr).minimized;
                                                }else{
                                                    catcher = j as isize;
                                                    (**window_ptr).minimized = false;
                                                }
                                                break;
                                            },
                                            Option::None => break
                                        }
                                    }
                                    self.redraw = max(self.redraw, REDRAW_ALL);
                                },
                                Option::None => ()
                            }
                            break;
                        }
                    }
                }
            }
        }

        if catcher >= 0 && catcher < self.windows.len() as isize - 1 {
            match self.windows.remove(catcher as usize){
                Option::Some(window_ptr) => self.windows.push(window_ptr),
                Option::None => ()
            }
        }

        self.last_mouse_event = mouse_event;
    }

    pub unsafe fn redraw(&mut self){
        if self.redraw > REDRAW_NONE {
            if self.redraw >= REDRAW_ALL {
                self.display.set(Color::new(64, 64, 64));
                if self.background.data > 0 {
                    self.display.image(Point::new((self.display.width as isize - self.background.size.width as isize)/2, (self.display.height as isize - self.background.size.height as isize)/2), self.background.data, self.background.size);
                }

                for i in 0..self.windows.len(){
                    match self.windows.get(i) {
                        Option::Some(window_ptr) => {
                            (**window_ptr).focused = i == self.windows.len() - 1;
                            (**window_ptr).draw(&self.display);
                        },
                        Option::None => ()
                    }
                }

                self.display.rect(Point::new(0, self.display.height as isize - 32), Size::new(self.display.width, 32), Color::new(0, 0, 0));
                if self.icon.data > 0 {
                    self.display.image_alpha(Point::new(12, self.display.height as isize - 32), self.icon.data, self.icon.size);
                }else{
                    self.display.text(Point::new(8, self.display.height as isize - 24), &String::from_str("Redox"), Color::new(255, 255, 255));
                }

                let mut chars = 32;
                while chars > 4 && (chars*8 + 3*4) * self.windows.len() > self.display.width {
                    chars -= 1;
                }

                for i in 0..self.windows_ordered.len() {
                    match self.windows_ordered.get(i) {
                        Option::Some(window_ptr) => {
                            let x = (5*8 + 2*8 + (chars*8 + 3*4) * i) as isize;
                            let w = (chars*8 + 2*4) as usize;
                            self.display.rect(Point::new(x, self.display.height as isize - 32), Size::new(w, 32), (**window_ptr).border_color);
                            self.display.text(Point::new(x + 4, self.display.height as isize - 24), &(**window_ptr).title.substr(0, chars as usize), (**window_ptr).title_color);
                        },
                        Option::None => ()
                    }
                }
            }

            let reenable = start_no_ints();

            self.display.flip();

            if self.cursor.data > 0 {
                self.display.image_alpha_onscreen(self.mouse_point, self.cursor.data, self.cursor.size);
            }else{
                self.display.char_onscreen(Point::new(self.mouse_point.x - 3, self.mouse_point.y - 9), 'X', Color::new(255, 255, 255));
            }

            self.redraw = REDRAW_NONE;

            end_no_ints(reenable);
        }
    }

    pub fn event(&mut self, event: Event){
        match event.to_option() {
            EventOption::Mouse(mouse_event) => self.on_mouse(mouse_event),
            EventOption::Key(key_event) => self.on_key(key_event),
            EventOption::Redraw(redraw_event) => self.redraw = max(self.redraw, redraw_event.redraw),
            EventOption::Open(open_event) => {
                let url_string = open_event.url_string;
                let url = URL::from_string(&url_string);

                if url_string.ends_with(".md".to_string()) || url_string.ends_with(".rs".to_string()) || url_string.ends_with(".sh".to_string()){
                    self.item_main(box Editor::new(), url);
                }else if url_string.ends_with(".bin".to_string()){
                    self.item_main(box Executor::new(), url);
                }else if url_string.ends_with(".bmp".to_string()){
                    self.item_main(box Viewer::new(), url);
                }else{
                    d("No program found: ");
                    url.d();
                    dl();
                }
            }
            _ => ()
        }
    }
}
<|endoftext|>