tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

yaml_helper.rs (50328B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use euclid::{Angle, Size2D};
      6 use crate::parse_function::parse_function;
      7 use std::f32;
      8 use std::str::FromStr;
      9 use webrender::api::*;
     10 use webrender::api::units::*;
     11 use yaml_rust::{Yaml, YamlLoader};
     12 use log::Level;
     13 
     14 pub trait YamlHelper {
     15    fn as_f32(&self) -> Option<f32>;
     16    fn as_force_f32(&self) -> Option<f32>;
     17    fn as_vec_f32(&self) -> Option<Vec<f32>>;
     18    fn as_vec_u32(&self) -> Option<Vec<u32>>;
     19    fn as_vec_u64(&self) -> Option<Vec<u64>>;
     20    fn as_pipeline_id(&self) -> Option<PipelineId>;
     21    fn as_rect(&self) -> Option<LayoutRect>;
     22    fn as_size(&self) -> Option<LayoutSize>;
     23    fn as_point(&self) -> Option<LayoutPoint>;
     24    fn as_vector(&self) -> Option<LayoutVector2D>;
     25    fn as_matrix4d(&self) -> Option<LayoutTransform>;
     26    fn as_transform(&self, transform_origin: &LayoutPoint) -> Option<LayoutTransform>;
     27    fn as_colorf(&self) -> Option<ColorF>;
     28    fn as_vec_colorf(&self) -> Option<Vec<ColorF>>;
     29    fn as_pt_to_f32(&self) -> Option<f32>;
     30    fn as_vec_string(&self) -> Option<Vec<String>>;
     31    fn as_border_radius_component(&self) -> LayoutSize;
     32    fn as_border_radius(&self) -> Option<BorderRadius>;
     33    fn as_transform_style(&self) -> Option<TransformStyle>;
     34    fn as_raster_space(&self) -> Option<RasterSpace>;
     35    fn as_clip_mode(&self) -> Option<ClipMode>;
     36    fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference>;
     37    fn as_mix_blend_mode(&self) -> Option<MixBlendMode>;
     38    fn as_filter_op(&self) -> Option<FilterOp>;
     39    fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>>;
     40    fn as_filter_data(&self) -> Option<FilterData>;
     41    fn as_vec_filter_data(&self) -> Option<Vec<FilterData>>;
     42    fn as_complex_clip_region(&self) -> ComplexClipRegion;
     43    fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds;
     44    fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient;
     45    fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient;
     46    fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient;
     47    fn as_complex_clip_regions(&self) -> Vec<ComplexClipRegion>;
     48    fn as_rotation(&self) -> Option<Rotation>;
     49 }
     50 
     51 fn string_to_color(color: &str) -> Option<ColorF> {
     52    match color {
     53        "red" => Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
     54        "green" => Some(ColorF::new(0.0, 1.0, 0.0, 1.0)),
     55        "blue" => Some(ColorF::new(0.0, 0.0, 1.0, 1.0)),
     56        "white" => Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
     57        "black" => Some(ColorF::new(0.0, 0.0, 0.0, 1.0)),
     58        "yellow" => Some(ColorF::new(1.0, 1.0, 0.0, 1.0)),
     59        "cyan" => Some(ColorF::new(0.0, 1.0, 1.0, 1.0)),
     60        "magenta" => Some(ColorF::new(1.0, 0.0, 1.0, 1.0)),
     61        "transparent" => Some(ColorF::new(1.0, 1.0, 1.0, 0.0)),
     62        s => {
     63            let items: Vec<f32> = s.split_whitespace()
     64                .map(|s| f32::from_str(s).unwrap())
     65                .collect();
     66            if items.len() == 3 {
     67                Some(ColorF::new(
     68                    items[0] / 255.0,
     69                    items[1] / 255.0,
     70                    items[2] / 255.0,
     71                    1.0,
     72                ))
     73            } else if items.len() == 4 {
     74                Some(ColorF::new(
     75                    items[0] / 255.0,
     76                    items[1] / 255.0,
     77                    items[2] / 255.0,
     78                    items[3],
     79                ))
     80            } else {
     81                None
     82            }
     83        }
     84    }
     85 }
     86 
     87 pub trait StringEnum: Sized {
     88    fn from_str(_: &str) -> Option<Self>;
     89 }
     90 
     91 macro_rules! define_string_enum {
     92    ($T:ident, [ $( $y:ident = $x:expr ),* ]) => {
     93        impl StringEnum for $T {
     94            fn from_str(text: &str) -> Option<$T> {
     95                match text {
     96                $( $x => Some($T::$y), )*
     97                    _ => {
     98                        println!("Unrecognized {} value '{}'", stringify!($T), text);
     99                        None
    100                    }
    101                }
    102            }
    103        }
    104    }
    105 }
    106 
    107 define_string_enum!(TransformStyle, [Flat = "flat", Preserve3D = "preserve-3d"]);
    108 
    109 define_string_enum!(
    110    MixBlendMode,
    111    [
    112        Normal = "normal",
    113        Multiply = "multiply",
    114        Screen = "screen",
    115        Overlay = "overlay",
    116        Darken = "darken",
    117        Lighten = "lighten",
    118        ColorDodge = "color-dodge",
    119        ColorBurn = "color-burn",
    120        HardLight = "hard-light",
    121        SoftLight = "soft-light",
    122        Difference = "difference",
    123        Exclusion = "exclusion",
    124        Hue = "hue",
    125        Saturation = "saturation",
    126        Color = "color",
    127        Luminosity = "luminosity",
    128        PlusLighter = "plus-lighter"
    129    ]
    130 );
    131 
    132 define_string_enum!(
    133    LineOrientation,
    134    [Horizontal = "horizontal", Vertical = "vertical"]
    135 );
    136 
    137 define_string_enum!(
    138    LineStyle,
    139    [
    140        Solid = "solid",
    141        Dotted = "dotted",
    142        Dashed = "dashed",
    143        Wavy = "wavy"
    144    ]
    145 );
    146 
    147 define_string_enum!(ClipMode, [Clip = "clip", ClipOut = "clip-out"]);
    148 
    149 define_string_enum!(
    150    ComponentTransferFuncType,
    151    [
    152        Identity = "Identity",
    153        Table = "Table",
    154        Discrete = "Discrete",
    155        Linear = "Linear",
    156        Gamma = "Gamma"
    157    ]
    158 );
    159 
    160 define_string_enum!(
    161    ColorSpace,
    162    [
    163        Srgb = "srgb",
    164        LinearRgb = "linear-rgb"
    165    ]
    166 );
    167 
    168 // Rotate around `axis` by `degrees` angle
    169 fn make_rotation(
    170    origin: &LayoutPoint,
    171    degrees: f32,
    172    axis_x: f32,
    173    axis_y: f32,
    174    axis_z: f32,
    175 ) -> LayoutTransform {
    176    let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0);
    177    let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0);
    178 
    179    let theta = 2.0f32 * f32::consts::PI - degrees.to_radians();
    180    let transform =
    181        LayoutTransform::identity().pre_rotate(axis_x, axis_y, axis_z, Angle::radians(theta));
    182 
    183    pre_transform.then(&transform).then(&post_transform)
    184 }
    185 
    186 pub fn make_perspective(
    187    origin: LayoutPoint,
    188    perspective: f32,
    189 ) -> LayoutTransform {
    190    let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0);
    191    let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0);
    192    let transform = LayoutTransform::perspective(perspective);
    193    pre_transform.then(&transform).then(&post_transform)
    194 }
    195 
    196 // Create a skew matrix, specified in degrees.
    197 fn make_skew(
    198    skew_x: f32,
    199    skew_y: f32,
    200 ) -> LayoutTransform {
    201    let alpha = Angle::radians(skew_x.to_radians());
    202    let beta = Angle::radians(skew_y.to_radians());
    203    LayoutTransform::skew(alpha, beta)
    204 }
    205 
    206 impl YamlHelper for Yaml {
    207    fn as_f32(&self) -> Option<f32> {
    208        match *self {
    209            Yaml::Integer(iv) => Some(iv as f32),
    210            Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(),
    211            _ => None,
    212        }
    213    }
    214 
    215    fn as_force_f32(&self) -> Option<f32> {
    216        match *self {
    217            Yaml::Integer(iv) => Some(iv as f32),
    218            Yaml::String(ref sv) | Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(),
    219            _ => None,
    220        }
    221    }
    222 
    223    fn as_vec_f32(&self) -> Option<Vec<f32>> {
    224        match *self {
    225            Yaml::String(ref s) | Yaml::Real(ref s) => s.split_whitespace()
    226                .map(f32::from_str)
    227                .collect::<Result<Vec<_>, _>>()
    228                .ok(),
    229            Yaml::Array(ref v) => v.iter()
    230                .map(|v| match *v {
    231                    Yaml::Integer(k) => Ok(k as f32),
    232                    Yaml::String(ref k) | Yaml::Real(ref k) => f32::from_str(k).map_err(|_| false),
    233                    _ => Err(false),
    234                })
    235                .collect::<Result<Vec<_>, _>>()
    236                .ok(),
    237            Yaml::Integer(k) => Some(vec![k as f32]),
    238            _ => None,
    239        }
    240    }
    241 
    242    fn as_vec_u32(&self) -> Option<Vec<u32>> {
    243        self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u32).collect())
    244    }
    245 
    246    fn as_vec_u64(&self) -> Option<Vec<u64>> {
    247        self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u64).collect())
    248    }
    249 
    250    fn as_pipeline_id(&self) -> Option<PipelineId> {
    251        if let Some(v) = self.as_vec() {
    252            let a = v.get(0).and_then(|v| v.as_i64()).map(|v| v as u32);
    253            let b = v.get(1).and_then(|v| v.as_i64()).map(|v| v as u32);
    254            match (a, b) {
    255                (Some(a), Some(b)) if v.len() == 2 => Some(PipelineId(a, b)),
    256                _ => None,
    257            }
    258        } else {
    259            None
    260        }
    261    }
    262 
    263    fn as_pt_to_f32(&self) -> Option<f32> {
    264        self.as_force_f32().map(|fv| fv * 16. / 12.)
    265    }
    266 
    267    fn as_rect(&self) -> Option<LayoutRect> {
    268        self.as_vec_f32().and_then(|v| match v.as_slice() {
    269            &[x, y, width, height] => Some(LayoutRect::from_origin_and_size(
    270                LayoutPoint::new(x, y),
    271                LayoutSize::new(width, height),
    272            )),
    273            _ => None,
    274        })
    275    }
    276 
    277    fn as_size(&self) -> Option<LayoutSize> {
    278        if self.is_badvalue() {
    279            return None;
    280        }
    281 
    282        if let Some(nums) = self.as_vec_f32() {
    283            if nums.len() == 2 {
    284                return Some(LayoutSize::new(nums[0], nums[1]));
    285            }
    286        }
    287 
    288        None
    289    }
    290 
    291    fn as_point(&self) -> Option<LayoutPoint> {
    292        if self.is_badvalue() {
    293            return None;
    294        }
    295 
    296        if let Some(nums) = self.as_vec_f32() {
    297            if nums.len() == 2 {
    298                return Some(LayoutPoint::new(nums[0], nums[1]));
    299            }
    300        }
    301 
    302        None
    303    }
    304 
    305    fn as_vector(&self) -> Option<LayoutVector2D> {
    306        self.as_point().map(|p| p.to_vector())
    307    }
    308 
    309    fn as_matrix4d(&self) -> Option<LayoutTransform> {
    310        if let Some(nums) = self.as_vec_f32() {
    311            assert_eq!(nums.len(), 16, "expected 16 floats, got '{:?}'", self);
    312            Some(LayoutTransform::new(
    313                nums[0], nums[1], nums[2], nums[3],
    314                nums[4], nums[5], nums[6], nums[7],
    315                nums[8], nums[9], nums[10], nums[11],
    316                nums[12], nums[13], nums[14], nums[15],
    317            ))
    318        } else {
    319            None
    320        }
    321    }
    322 
    323    fn as_transform(&self, transform_origin: &LayoutPoint) -> Option<LayoutTransform> {
    324        if let Some(transform) = self.as_matrix4d() {
    325            return Some(transform);
    326        }
    327 
    328        match *self {
    329            Yaml::String(ref string) => {
    330                let mut slice = string.as_str();
    331                let mut transform = LayoutTransform::identity();
    332                while !slice.is_empty() {
    333                    let (function, ref args, reminder) = parse_function(slice);
    334                    slice = reminder;
    335                    let mx = match function {
    336                        "identity" => {
    337                            LayoutTransform::identity()
    338                        }
    339                        "translate" if args.len() >= 2 => {
    340                            let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(0.);
    341                            LayoutTransform::translation(
    342                                args[0].parse().unwrap(),
    343                                args[1].parse().unwrap(),
    344                                z,
    345                            )
    346                        }
    347                        "rotate" | "rotate-z" if args.len() == 1 => {
    348                            make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 0.0, 1.0)
    349                        }
    350                        "rotate-x" if args.len() == 1 => {
    351                            make_rotation(transform_origin, args[0].parse().unwrap(), 1.0, 0.0, 0.0)
    352                        }
    353                        "rotate-y" if args.len() == 1 => {
    354                            make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 1.0, 0.0)
    355                        }
    356                        "scale" if !args.is_empty() => {
    357                            let x = args[0].parse().unwrap();
    358                            // Default to uniform X/Y scale if Y unspecified.
    359                            let y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(x);
    360                            // Default to no Z scale if unspecified.
    361                            let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(1.0);
    362                            LayoutTransform::scale(x, y, z)
    363                        }
    364                        "scale-x" if args.len() == 1 => {
    365                            LayoutTransform::scale(args[0].parse().unwrap(), 1.0, 1.0)
    366                        }
    367                        "scale-y" if args.len() == 1 => {
    368                            LayoutTransform::scale(1.0, args[0].parse().unwrap(), 1.0)
    369                        }
    370                        "scale-z" if args.len() == 1 => {
    371                            LayoutTransform::scale(1.0, 1.0, args[0].parse().unwrap())
    372                        }
    373                        "skew" if !args.is_empty() => {
    374                            // Default to no Y skew if unspecified.
    375                            let skew_y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(0.0);
    376                            make_skew(args[0].parse().unwrap(), skew_y)
    377                        }
    378                        "skew-x" if args.len() == 1 => {
    379                            make_skew(args[0].parse().unwrap(), 0.0)
    380                        }
    381                        "skew-y" if args.len() == 1 => {
    382                            make_skew(0.0, args[0].parse().unwrap())
    383                        }
    384                        "perspective" if args.len() == 1 => {
    385                            LayoutTransform::perspective(args[0].parse().unwrap())
    386                        }
    387                        _ => {
    388                            println!("unknown function {}", function);
    389                            break;
    390                        }
    391                    };
    392                    transform = transform.then(&mx);
    393                }
    394                Some(transform)
    395            }
    396            Yaml::Array(ref array) => {
    397                let transform = array.iter().fold(
    398                    LayoutTransform::identity(),
    399                    |u, yaml| if let Some(transform) = yaml.as_transform(transform_origin) {
    400                        transform.then(&u)
    401                    } else {
    402                        u
    403                    },
    404                );
    405                Some(transform)
    406            }
    407            Yaml::BadValue => None,
    408            _ => {
    409                println!("unknown transform {:?}", self);
    410                None
    411            }
    412        }
    413    }
    414 
    415    /// Inputs for r, g, b channels are floats or ints in the range [0, 255].
    416    /// If included, the alpha channel is in the range [0, 1].
    417    /// This matches CSS-style, but requires conversion for `ColorF`.
    418    fn as_colorf(&self) -> Option<ColorF> {
    419        if let Some(nums) = self.as_vec_f32() {
    420            assert!(nums.iter().take(3).all(|x| (0.0 ..= 255.0).contains(x)),
    421                "r, g, b values should be in the 0-255 range, got {:?}", nums);
    422 
    423            let color: ColorF = match *nums.as_slice() {
    424                [r, g, b] => ColorF { r, g, b, a: 1.0 },
    425                [r, g, b, a] => ColorF { r, g, b, a },
    426                _ => panic!("color expected a color name, or 3-4 floats; got '{:?}'", self),
    427            }.scale_rgb(1.0 / 255.0);
    428 
    429            assert!((0.0 ..= 1.0).contains(&color.a),
    430                    "alpha value should be in the 0-1 range, got {:?}",
    431                    color.a);
    432 
    433            Some(color)
    434        } else if let Some(s) = self.as_str() {
    435            string_to_color(s)
    436        } else {
    437            None
    438        }
    439    }
    440 
    441    fn as_vec_colorf(&self) -> Option<Vec<ColorF>> {
    442        if let Some(v) = self.as_vec() {
    443            Some(v.iter().map(|v| v.as_colorf().unwrap()).collect())
    444        } else { self.as_colorf().map(|color| vec![color]) }
    445    }
    446 
    447    fn as_vec_string(&self) -> Option<Vec<String>> {
    448        if let Some(v) = self.as_vec() {
    449            Some(v.iter().map(|v| v.as_str().unwrap().to_owned()).collect())
    450        } else { self.as_str().map(|s| vec![s.to_owned()]) }
    451    }
    452 
    453    fn as_border_radius_component(&self) -> LayoutSize {
    454        if let Yaml::Integer(integer) = *self {
    455            return LayoutSize::new(integer as f32, integer as f32);
    456        }
    457        self.as_size().unwrap_or_else(Size2D::zero)
    458    }
    459 
    460    fn as_border_radius(&self) -> Option<BorderRadius> {
    461        if let Some(size) = self.as_size() {
    462            return Some(BorderRadius::uniform_size(size));
    463        }
    464 
    465        match *self {
    466            Yaml::BadValue => None,
    467            Yaml::String(ref s) | Yaml::Real(ref s) => {
    468                let fv = f32::from_str(s).unwrap();
    469                Some(BorderRadius::uniform(fv))
    470            }
    471            Yaml::Integer(v) => Some(BorderRadius::uniform(v as f32)),
    472            Yaml::Array(ref array) if array.len() == 4 => {
    473                let top_left = array[0].as_border_radius_component();
    474                let top_right = array[1].as_border_radius_component();
    475                let bottom_left = array[2].as_border_radius_component();
    476                let bottom_right = array[3].as_border_radius_component();
    477                Some(BorderRadius {
    478                    top_left,
    479                    top_right,
    480                    bottom_left,
    481                    bottom_right,
    482                })
    483            }
    484            Yaml::Hash(_) => {
    485                let top_left = self["top-left"].as_border_radius_component();
    486                let top_right = self["top-right"].as_border_radius_component();
    487                let bottom_left = self["bottom-left"].as_border_radius_component();
    488                let bottom_right = self["bottom-right"].as_border_radius_component();
    489                Some(BorderRadius {
    490                    top_left,
    491                    top_right,
    492                    bottom_left,
    493                    bottom_right,
    494                })
    495            }
    496            _ => {
    497                panic!("Invalid border radius specified: {:?}", self);
    498            }
    499        }
    500    }
    501 
    502    fn as_transform_style(&self) -> Option<TransformStyle> {
    503        self.as_str().and_then(StringEnum::from_str)
    504    }
    505 
    506    fn as_raster_space(&self) -> Option<RasterSpace> {
    507        self.as_str().map(|s| {
    508            match parse_function(s) {
    509                ("screen", _, _) => {
    510                    RasterSpace::Screen
    511                }
    512                ("local", ref args, _) if args.len() == 1 => {
    513                    RasterSpace::Local(args[0].parse().unwrap())
    514                }
    515                f => {
    516                    panic!("error parsing raster space {:?}", f);
    517                }
    518            }
    519        })
    520    }
    521 
    522    fn as_mix_blend_mode(&self) -> Option<MixBlendMode> {
    523        self.as_str().and_then(StringEnum::from_str)
    524    }
    525 
    526    fn as_clip_mode(&self) -> Option<ClipMode> {
    527        self.as_str().and_then(StringEnum::from_str)
    528    }
    529 
    530    fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference> {
    531        match self.as_i64() {
    532            Some(n) => Some(FilterOpGraphPictureReference{
    533                buffer_id: FilterOpGraphPictureBufferId::BufferId(n as i16),
    534            }),
    535            None => None,
    536        }
    537    }
    538 
    539    fn as_filter_op(&self) -> Option<FilterOp> {
    540        if let Some(filter_op) = self["svgfe"].as_str() {
    541            let subregion = self["subregion"].as_rect().unwrap_or(
    542                LayoutRect::new(
    543                    LayoutPoint::new(0.0, 0.0),
    544                    LayoutPoint::new(1024.0, 1024.0),
    545                ));
    546 
    547            let node = FilterOpGraphNode {
    548                linear: self["linear"].as_bool().unwrap_or(true),
    549                subregion,
    550                input: self["in"].as_graph_picture_reference().unwrap_or(
    551                    FilterOpGraphPictureReference{
    552                        buffer_id: FilterOpGraphPictureBufferId::None,
    553                    }),
    554                input2: self["in2"].as_graph_picture_reference().unwrap_or(
    555                    FilterOpGraphPictureReference{
    556                        buffer_id: FilterOpGraphPictureBufferId::None,
    557                    }),
    558            };
    559            let debug_print_input = |input: FilterOpGraphPictureReference| -> String {
    560                match input.buffer_id {
    561                    FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId{}", id),
    562                    FilterOpGraphPictureBufferId::None => "None".into(),
    563                }
    564            };
    565            log!(Level::Debug, "svgfe parsed: {} linear: {} in: {} in2: {} subregion: [{}, {}, {}, {}]",
    566                filter_op, node.linear,
    567                debug_print_input(node.input), debug_print_input(node.input2),
    568                node.subregion.min.x, node.subregion.min.y, node.subregion.max.x, node.subregion.max.y,
    569            );
    570            return match filter_op {
    571                "identity" => Some(FilterOp::SVGFEIdentity{node}),
    572                "opacity" => {
    573                    let value = self["value"].as_f32().unwrap();
    574                    Some(FilterOp::SVGFEOpacity{node, valuebinding: value.into(), value})
    575                },
    576                "toalpha" => Some(FilterOp::SVGFEToAlpha{node}),
    577                "blendcolor" => Some(FilterOp::SVGFEBlendColor{node}),
    578                "blendcolorburn" => Some(FilterOp::SVGFEBlendColorBurn{node}),
    579                "blendcolordodge" => Some(FilterOp::SVGFEBlendColorDodge{node}),
    580                "blenddarken" => Some(FilterOp::SVGFEBlendDarken{node}),
    581                "blenddifference" => Some(FilterOp::SVGFEBlendDifference{node}),
    582                "blendexclusion" => Some(FilterOp::SVGFEBlendExclusion{node}),
    583                "blendhardlight" => Some(FilterOp::SVGFEBlendHardLight{node}),
    584                "blendhue" => Some(FilterOp::SVGFEBlendHue{node}),
    585                "blendlighten" => Some(FilterOp::SVGFEBlendLighten{node}),
    586                "blendluminosity" => Some(FilterOp::SVGFEBlendLuminosity{node}),
    587                "blendmultiply" => Some(FilterOp::SVGFEBlendMultiply{node}),
    588                "blendnormal" => Some(FilterOp::SVGFEBlendNormal{node}),
    589                "blendoverlay" => Some(FilterOp::SVGFEBlendOverlay{node}),
    590                "blendsaturation" => Some(FilterOp::SVGFEBlendSaturation{node}),
    591                "blendscreen" => Some(FilterOp::SVGFEBlendScreen{node}),
    592                "blendsoftlight" => Some(FilterOp::SVGFEBlendSoftLight{node}),
    593                "colormatrix" => {
    594                    let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap();
    595                    let mut matrix: [f32; 20] = [0.0; 20];
    596                    matrix.clone_from_slice(&m);
    597                    Some(FilterOp::SVGFEColorMatrix{node, values: matrix})
    598                }
    599                "componenttransfer" => Some(FilterOp::SVGFEComponentTransfer{node}),
    600                "compositearithmetic" => {
    601                    let k: Vec<f32> = self["k"].as_vec_f32().unwrap();
    602                    Some(FilterOp::SVGFECompositeArithmetic{
    603                        node,
    604                        k1: k[0],
    605                        k2: k[1],
    606                        k3: k[2],
    607                        k4: k[3],
    608                    })
    609                }
    610                "compositeatop" => Some(FilterOp::SVGFECompositeATop{node}),
    611                "compositein" => Some(FilterOp::SVGFECompositeIn{node}),
    612                "compositelighter" => Some(FilterOp::SVGFECompositeLighter{node}),
    613                "compositeout" => Some(FilterOp::SVGFECompositeOut{node}),
    614                "compositeover" => Some(FilterOp::SVGFECompositeOver{node}),
    615                "compositexor" => Some(FilterOp::SVGFECompositeXOR{node}),
    616                "convolvematrixedgemodeduplicate" => {
    617                    let order_x = self["order_x"].as_i64().unwrap() as i32;
    618                    let order_y = self["order_y"].as_i64().unwrap() as i32;
    619                    let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
    620                    let mut kernel: [f32; 25] = [0.0; 25];
    621                    kernel.clone_from_slice(&m);
    622                    let divisor = self["divisor"].as_f32().unwrap();
    623                    let bias = self["bias"].as_f32().unwrap();
    624                    let target_x = self["target_x"].as_i64().unwrap() as i32;
    625                    let target_y = self["target_y"].as_i64().unwrap() as i32;
    626                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    627                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    628                    let preserve_alpha = match self["preserve_alpha"].as_bool() {
    629                        Some(true) => 1,
    630                        Some(false) => 0,
    631                        _ => 1,
    632                    };
    633                    Some(FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate{
    634                         node, order_x, order_y, kernel, divisor, bias,
    635                         target_x, target_y, kernel_unit_length_x,
    636                         kernel_unit_length_y, preserve_alpha})
    637                },
    638                "convolvematrixedgemodenone" => {
    639                    let order_x = self["order_x"].as_i64().unwrap() as i32;
    640                    let order_y = self["order_y"].as_i64().unwrap() as i32;
    641                    let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
    642                    let mut kernel: [f32; 25] = [0.0; 25];
    643                    kernel.clone_from_slice(&m);
    644                    let divisor = self["divisor"].as_f32().unwrap();
    645                    let bias = self["bias"].as_f32().unwrap();
    646                    let target_x = self["target_x"].as_i64().unwrap() as i32;
    647                    let target_y = self["target_y"].as_i64().unwrap() as i32;
    648                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    649                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    650                    let preserve_alpha = match self["preserve_alpha"].as_bool() {
    651                        Some(true) => 1,
    652                        Some(false) => 0,
    653                        _ => 1,
    654                    };
    655                    Some(FilterOp::SVGFEConvolveMatrixEdgeModeNone{
    656                         node, order_x, order_y, kernel, divisor, bias,
    657                         target_x, target_y, kernel_unit_length_x,
    658                         kernel_unit_length_y, preserve_alpha})
    659                },
    660                "convolvematrixedgemodewrap" => {
    661                    let order_x = self["order_x"].as_i64().unwrap() as i32;
    662                    let order_y = self["order_y"].as_i64().unwrap() as i32;
    663                    let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
    664                    let mut kernel: [f32; 25] = [0.0; 25];
    665                    kernel.clone_from_slice(&m);
    666                    let divisor = self["divisor"].as_f32().unwrap();
    667                    let bias = self["bias"].as_f32().unwrap();
    668                    let target_x = self["target_x"].as_i64().unwrap() as i32;
    669                    let target_y = self["target_y"].as_i64().unwrap() as i32;
    670                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    671                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    672                    let preserve_alpha = match self["preserve_alpha"].as_bool() {
    673                        Some(true) => 1,
    674                        Some(false) => 0,
    675                        _ => 1,
    676                    };
    677                    Some(FilterOp::SVGFEConvolveMatrixEdgeModeWrap{
    678                         node, order_x, order_y, kernel, divisor, bias,
    679                         target_x, target_y, kernel_unit_length_x,
    680                         kernel_unit_length_y, preserve_alpha})
    681                },
    682                "diffuselightingdistant" => {
    683                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    684                    let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
    685                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    686                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    687                    let azimuth = self["azimuth"].as_f32().unwrap();
    688                    let elevation = self["elevation"].as_f32().unwrap();
    689                    Some(FilterOp::SVGFEDiffuseLightingDistant{
    690                         node, surface_scale, diffuse_constant,
    691                         kernel_unit_length_x, kernel_unit_length_y,
    692                         azimuth, elevation})
    693                },
    694                "diffuselightingpoint" => {
    695                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    696                    let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
    697                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    698                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    699                    let x = self["x"].as_f32().unwrap();
    700                    let y = self["y"].as_f32().unwrap();
    701                    let z = self["z"].as_f32().unwrap();
    702                    Some(FilterOp::SVGFEDiffuseLightingPoint{
    703                         node, surface_scale, diffuse_constant,
    704                         kernel_unit_length_x, kernel_unit_length_y, x, y, z})
    705                },
    706                "diffuselightingspot" => {
    707                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    708                    let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
    709                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    710                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    711                    let x = self["x"].as_f32().unwrap();
    712                    let y = self["y"].as_f32().unwrap();
    713                    let z = self["z"].as_f32().unwrap();
    714                    let points_at_x = self["points_at_x"].as_f32().unwrap();
    715                    let points_at_y = self["points_at_y"].as_f32().unwrap();
    716                    let points_at_z = self["points_at_z"].as_f32().unwrap();
    717                    let cone_exponent = self["cone_exponent"].as_f32().unwrap();
    718                    let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap();
    719                    Some(FilterOp::SVGFEDiffuseLightingSpot{
    720                         node, surface_scale, diffuse_constant,
    721                         kernel_unit_length_x, kernel_unit_length_y, x, y, z,
    722                         points_at_x, points_at_y, points_at_z, cone_exponent,
    723                         limiting_cone_angle})
    724                },
    725                "displacementmap" => {
    726                    let scale = self["scale"].as_f32().unwrap();
    727                    let x_channel_selector = self["x_channel_selector"].as_i64().unwrap() as u32;
    728                    let y_channel_selector = self["y_channel_selector"].as_i64().unwrap() as u32;
    729                    Some(FilterOp::SVGFEDisplacementMap{node, scale, x_channel_selector, y_channel_selector})
    730                },
    731                "dropshadow" => {
    732                    let color = self["color"].as_colorf().unwrap();
    733                    let dx = self["dx"].as_f32().unwrap();
    734                    let dy = self["dy"].as_f32().unwrap();
    735                    let std_deviation_x = self["std_deviation_x"].as_f32().unwrap();
    736                    let std_deviation_y = self["std_deviation_y"].as_f32().unwrap();
    737                    Some(FilterOp::SVGFEDropShadow{node, color, dx, dy, std_deviation_x, std_deviation_y})
    738                },
    739                "flood" => Some(FilterOp::SVGFEFlood{node, color: self["color"].as_colorf().unwrap()}),
    740                "gaussianblur" => {
    741                    let std_deviation_x = self["std_deviation_x"].as_f32().unwrap();
    742                    let std_deviation_y = self["std_deviation_y"].as_f32().unwrap();
    743                    Some(FilterOp::SVGFEGaussianBlur{node, std_deviation_x, std_deviation_y})
    744                },
    745                "image" => {
    746                    let sampling_filter = match self["sampling_filter"].as_str() {
    747                        Some("GOOD") => 0,
    748                        Some("LINEAR") => 1,
    749                        Some("POINT") => 2,
    750                        _ => 0,
    751                    };
    752                    let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap();
    753                    let mut matrix: [f32; 6] = [0.0; 6];
    754                    matrix.clone_from_slice(&m);
    755                    Some(FilterOp::SVGFEImage{node, sampling_filter, matrix})
    756                },
    757                "morphologydilate" => {
    758                    let radius_x = self["radius_x"].as_f32().unwrap();
    759                    let radius_y = self["radius_y"].as_f32().unwrap();
    760                    Some(FilterOp::SVGFEMorphologyDilate{node, radius_x, radius_y})
    761                },
    762                "morphologyerode" => {
    763                    let radius_x = self["radius_x"].as_f32().unwrap();
    764                    let radius_y = self["radius_y"].as_f32().unwrap();
    765                    Some(FilterOp::SVGFEMorphologyErode{node, radius_x, radius_y})
    766                },
    767                "offset" => {
    768                    let offset = self["offset"].as_vec_f32().unwrap();
    769                    Some(FilterOp::SVGFEOffset{node, offset_x: offset[0], offset_y: offset[1]})
    770                },
    771                "SourceAlpha" => Some(FilterOp::SVGFESourceAlpha{node}),
    772                "SourceGraphic" => Some(FilterOp::SVGFESourceGraphic{node}),
    773                "sourcealpha" => Some(FilterOp::SVGFESourceAlpha{node}),
    774                "sourcegraphic" => Some(FilterOp::SVGFESourceGraphic{node}),
    775                "specularlightingdistant" => {
    776                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    777                    let specular_constant = self["specular_constant"].as_f32().unwrap();
    778                    let specular_exponent = self["specular_exponent"].as_f32().unwrap();
    779                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    780                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    781                    let azimuth = self["azimuth"].as_f32().unwrap();
    782                    let elevation = self["elevation"].as_f32().unwrap();
    783                    Some(FilterOp::SVGFESpecularLightingDistant{
    784                        node, surface_scale, specular_constant,
    785                         specular_exponent, kernel_unit_length_x,
    786                         kernel_unit_length_y, azimuth, elevation})
    787                },
    788                "specularlightingpoint" => {
    789                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    790                    let specular_constant = self["specular_constant"].as_f32().unwrap();
    791                    let specular_exponent = self["specular_exponent"].as_f32().unwrap();
    792                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    793                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    794                    let x = self["x"].as_f32().unwrap();
    795                    let y = self["y"].as_f32().unwrap();
    796                    let z = self["z"].as_f32().unwrap();
    797                    Some(FilterOp::SVGFESpecularLightingPoint{
    798                         node, surface_scale, specular_constant,
    799                         specular_exponent, kernel_unit_length_x,
    800                         kernel_unit_length_y, x, y, z})
    801                },
    802                "specularlightingspot" => {
    803                    let surface_scale = self["surface_scale"].as_f32().unwrap();
    804                    let specular_constant = self["specular_constant"].as_f32().unwrap();
    805                    let specular_exponent = self["specular_exponent"].as_f32().unwrap();
    806                    let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
    807                    let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
    808                    let x = self["x"].as_f32().unwrap();
    809                    let y = self["y"].as_f32().unwrap();
    810                    let z = self["z"].as_f32().unwrap();
    811                    let points_at_x = self["points_at_x"].as_f32().unwrap();
    812                    let points_at_y = self["points_at_y"].as_f32().unwrap();
    813                    let points_at_z = self["points_at_z"].as_f32().unwrap();
    814                    let cone_exponent = self["cone_exponent"].as_f32().unwrap();
    815                    let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap();
    816                    Some(FilterOp::SVGFESpecularLightingSpot{
    817                         node, surface_scale, specular_constant,
    818                         specular_exponent, kernel_unit_length_x,
    819                         kernel_unit_length_y, x, y, z, points_at_x,
    820                         points_at_y, points_at_z, limiting_cone_angle,
    821                         cone_exponent})
    822                },
    823                "tile" => Some(FilterOp::SVGFETile{node}),
    824                "turbulencewithfractalnoisewithnostitching" => {
    825                    let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
    826                    let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
    827                    let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
    828                    let seed = self["seed"].as_i64().unwrap() as u32;
    829                    Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
    830                        node, base_frequency_x, base_frequency_y, num_octaves, seed})
    831                },
    832                "turbulencewithfractalnoisewithstitching" => {
    833                    let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
    834                    let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
    835                    let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
    836                    let seed = self["seed"].as_i64().unwrap() as u32;
    837                    Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching{
    838                        node, base_frequency_x, base_frequency_y, num_octaves, seed})
    839                },
    840                "turbulencewithturbulencenoisewithnostitching" => {
    841                    let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
    842                    let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
    843                    let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
    844                    let seed = self["seed"].as_i64().unwrap() as u32;
    845                    Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{
    846                        node, base_frequency_x, base_frequency_y, num_octaves, seed})
    847                },
    848                "turbulencewithturbulencenoisewithstitching" => {
    849                    let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
    850                    let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
    851                    let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
    852                    let seed = self["seed"].as_i64().unwrap() as u32;
    853                    Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{
    854                        node, base_frequency_x, base_frequency_y, num_octaves, seed})
    855                },
    856                _ => None,
    857            }
    858        }
    859        if let Some(s) = self.as_str() {
    860            match parse_function(s) {
    861                ("identity", _, _) => {
    862                    Some(FilterOp::Identity)
    863                }
    864                ("component-transfer", _, _) => {
    865                    Some(FilterOp::ComponentTransfer)
    866                }
    867                ("blur", ref args, _) if args.len() == 2 => {
    868                    Some(FilterOp::Blur(args[0].parse().unwrap(), args[1].parse().unwrap()))
    869                }
    870                ("brightness", ref args, _) if args.len() == 1 => {
    871                    Some(FilterOp::Brightness(args[0].parse().unwrap()))
    872                }
    873                ("contrast", ref args, _) if args.len() == 1 => {
    874                    Some(FilterOp::Contrast(args[0].parse().unwrap()))
    875                }
    876                ("grayscale", ref args, _) if args.len() == 1 => {
    877                    Some(FilterOp::Grayscale(args[0].parse().unwrap()))
    878                }
    879                ("hue-rotate", ref args, _) if args.len() == 1 => {
    880                    Some(FilterOp::HueRotate(args[0].parse().unwrap()))
    881                }
    882                ("invert", ref args, _) if args.len() == 1 => {
    883                    Some(FilterOp::Invert(args[0].parse().unwrap()))
    884                }
    885                ("opacity", ref args, _) if args.len() == 1 => {
    886                    let amount: f32 = args[0].parse().unwrap();
    887                    Some(FilterOp::Opacity(amount.into(), amount))
    888                }
    889                ("saturate", ref args, _) if args.len() == 1 => {
    890                    Some(FilterOp::Saturate(args[0].parse().unwrap()))
    891                }
    892                ("sepia", ref args, _) if args.len() == 1 => {
    893                    Some(FilterOp::Sepia(args[0].parse().unwrap()))
    894                }
    895                ("srgb-to-linear", _, _)  => Some(FilterOp::SrgbToLinear),
    896                ("linear-to-srgb", _, _)  => Some(FilterOp::LinearToSrgb),
    897                ("drop-shadow", ref args, _) if args.len() == 3 => {
    898                    let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]);
    899                    let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow");
    900                    let yaml = yaml_doc.pop().unwrap();
    901                    Some(FilterOp::DropShadow(Shadow {
    902                        offset: yaml["offset"].as_vector().unwrap(),
    903                        blur_radius: yaml["blur-radius"].as_f32().unwrap(),
    904                        color: yaml["color"].as_colorf().unwrap()
    905                    }))
    906                }
    907                ("color-matrix", ref args, _) if args.len() == 20 => {
    908                    let m: Vec<f32> = args.iter().map(|f| f.parse().unwrap()).collect();
    909                    let mut matrix: [f32; 20] = [0.0; 20];
    910                    matrix.clone_from_slice(&m);
    911                    Some(FilterOp::ColorMatrix(matrix))
    912                }
    913                ("flood", ref args, _) if args.len() == 1 => {
    914                    let str = format!("---\ncolor: {}\n", args[0]);
    915                    let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse flood");
    916                    let yaml = yaml_doc.pop().unwrap();
    917                    Some(FilterOp::Flood(yaml["color"].as_colorf().unwrap()))
    918                }
    919                (_, _, _) => None,
    920            }
    921        } else {
    922            None
    923        }
    924    }
    925 
    926    fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>> {
    927        if let Some(v) = self.as_vec() {
    928            Some(v.iter().map(|x| x.as_filter_op().unwrap()).collect())
    929        } else {
    930            self.as_filter_op().map(|op| vec![op])
    931        }
    932    }
    933 
    934    fn as_filter_data(&self) -> Option<FilterData> {
    935        // Parse an array with five entries. First entry is an array of func types (4).
    936        // The remaining entries are arrays of floats.
    937        if let Yaml::Array(ref array) = *self {
    938            if array.len() != 5 {
    939                panic!("Invalid filter data specified, base array doesn't have five entries: {:?}", self);
    940            }
    941            if let Some(func_types_p) = array[0].as_vec_string() {
    942                if func_types_p.len() != 4 {
    943                    panic!("Invalid filter data specified, func type array doesn't have five entries: {:?}", self);
    944                }
    945                let func_types: Vec<ComponentTransferFuncType> =
    946                    func_types_p.into_iter().map(|x|
    947                        StringEnum::from_str(&x).unwrap_or_else(||
    948                            panic!("Invalid filter data specified, invalid func type name: {:?}", self))
    949                    ).collect();
    950                if let Some(r_values_p) = array[1].as_vec_f32() {
    951                    if let Some(g_values_p) = array[2].as_vec_f32() {
    952                        if let Some(b_values_p) = array[3].as_vec_f32() {
    953                            if let Some(a_values_p) = array[4].as_vec_f32() {
    954                                let filter_data = FilterData {
    955                                    func_r_type: func_types[0],
    956                                    r_values: r_values_p,
    957                                    func_g_type: func_types[1],
    958                                    g_values: g_values_p,
    959                                    func_b_type: func_types[2],
    960                                    b_values: b_values_p,
    961                                    func_a_type: func_types[3],
    962                                    a_values: a_values_p,
    963                                };
    964                                return Some(filter_data)
    965                            }
    966                        }
    967                    }
    968                }
    969            }
    970        }
    971        None
    972    }
    973 
    974    fn as_vec_filter_data(&self) -> Option<Vec<FilterData>> {
    975        if let Some(v) = self.as_vec() {
    976            Some(v.iter().map(|x| x.as_filter_data().unwrap()).collect())
    977        } else {
    978            self.as_filter_data().map(|data| vec![data])
    979        }
    980    }
    981 
    982    fn as_complex_clip_region(&self) -> ComplexClipRegion {
    983        let rect = self["rect"]
    984            .as_rect()
    985            .expect("Complex clip entry must have rect");
    986        let radius = self["radius"]
    987            .as_border_radius()
    988            .unwrap_or_else(BorderRadius::zero);
    989        let mode = self["clip-mode"]
    990            .as_clip_mode()
    991            .unwrap_or(ClipMode::Clip);
    992        ComplexClipRegion::new(rect, radius, mode)
    993    }
    994 
    995    fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds {
    996        match *self {
    997            Yaml::Array(ref array) => StickyOffsetBounds::new(
    998                array[0].as_f32().unwrap_or(0.0),
    999                array[1].as_f32().unwrap_or(0.0),
   1000            ),
   1001            _ => StickyOffsetBounds::new(0.0, 0.0),
   1002        }
   1003    }
   1004 
   1005    fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient {
   1006        let start = self["start"].as_point().expect("gradient must have start");
   1007        let end = self["end"].as_point().expect("gradient must have end");
   1008        let stops = self["stops"]
   1009            .as_vec()
   1010            .expect("gradient must have stops")
   1011            .chunks(2)
   1012            .map(|chunk| {
   1013                GradientStop {
   1014                    offset: chunk[0]
   1015                        .as_force_f32()
   1016                        .expect("gradient stop offset is not f32"),
   1017                    color: chunk[1]
   1018                        .as_colorf()
   1019                        .expect("gradient stop color is not color"),
   1020                }
   1021            })
   1022            .collect::<Vec<_>>();
   1023        let extend_mode = if self["repeat"].as_bool().unwrap_or(false) {
   1024            ExtendMode::Repeat
   1025        } else {
   1026            ExtendMode::Clamp
   1027        };
   1028 
   1029        dl.create_gradient(start, end, stops, extend_mode)
   1030    }
   1031 
   1032    fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient {
   1033        let center = self["center"].as_point().expect("radial gradient must have center");
   1034        let radius = self["radius"].as_size().expect("radial gradient must have a radius");
   1035        let stops = self["stops"]
   1036            .as_vec()
   1037            .expect("radial gradient must have stops")
   1038            .chunks(2)
   1039            .map(|chunk| {
   1040                GradientStop {
   1041                    offset: chunk[0]
   1042                        .as_force_f32()
   1043                        .expect("gradient stop offset is not f32"),
   1044                    color: chunk[1]
   1045                        .as_colorf()
   1046                        .expect("gradient stop color is not color"),
   1047                }
   1048            })
   1049            .collect::<Vec<_>>();
   1050        let extend_mode = if self["repeat"].as_bool().unwrap_or(false) {
   1051            ExtendMode::Repeat
   1052        } else {
   1053            ExtendMode::Clamp
   1054        };
   1055 
   1056        dl.create_radial_gradient(center, radius, stops, extend_mode)
   1057    }
   1058 
   1059    fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient {
   1060        let center = self["center"].as_point().expect("conic gradient must have center");
   1061        let angle = self["angle"].as_force_f32().expect("conic gradient must have an angle");
   1062        let stops = self["stops"]
   1063            .as_vec()
   1064            .expect("conic gradient must have stops")
   1065            .chunks(2)
   1066            .map(|chunk| {
   1067                GradientStop {
   1068                    offset: chunk[0]
   1069                        .as_force_f32()
   1070                        .expect("gradient stop offset is not f32"),
   1071                    color: chunk[1]
   1072                        .as_colorf()
   1073                        .expect("gradient stop color is not color"),
   1074                }
   1075            })
   1076            .collect::<Vec<_>>();
   1077        let extend_mode = if self["repeat"].as_bool().unwrap_or(false) {
   1078            ExtendMode::Repeat
   1079        } else {
   1080            ExtendMode::Clamp
   1081        };
   1082 
   1083        dl.create_conic_gradient(center, angle, stops, extend_mode)
   1084    }
   1085 
   1086    fn as_complex_clip_regions(&self) -> Vec<ComplexClipRegion> {
   1087        match *self {
   1088            Yaml::Array(ref array) => array
   1089                .iter()
   1090                .map(Yaml::as_complex_clip_region)
   1091                .collect(),
   1092            Yaml::BadValue => vec![],
   1093            _ => {
   1094                println!("Unable to parse complex clip region {:?}", self);
   1095                vec![]
   1096            }
   1097        }
   1098    }
   1099 
   1100    fn as_rotation(&self) -> Option<Rotation> {
   1101        match *self {
   1102            Yaml::Integer(0) => Some(Rotation::Degree0),
   1103            Yaml::Integer(90) => Some(Rotation::Degree90),
   1104            Yaml::Integer(180) => Some(Rotation::Degree180),
   1105            Yaml::Integer(270) => Some(Rotation::Degree270),
   1106            Yaml::BadValue => None,
   1107            _ => {
   1108                println!("Unable to parse rotation {:?}", self);
   1109                None
   1110            }
   1111        }
   1112    }
   1113 }