/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![allow(unsafe_code)] //! Different kind of helpers to interact with Gecko values. use app_units::Au; use counter_style::Symbol; use cssparser::RGBA; use gecko_bindings::structs::{CounterStylePtr, nsStyleCoord}; use gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius}; use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue}; use media_queries::Device; use nsstring::{nsACString, nsCString}; use std::cmp::max; use values::{Auto, Either, ExtremumLength, None_, Normal}; use values::computed::{Angle, LengthOrPercentage, LengthOrPercentageOrAuto}; use values::computed::{LengthOrPercentageOrNone, Number, NumberOrPercentage}; use values::computed::{MaxLength, MozLength, Percentage}; use values::computed::{NonNegativeAu, NonNegativeLengthOrPercentage, NonNegativeNumber}; use values::computed::basic_shape::ShapeRadius as ComputedShapeRadius; use values::generics::{CounterStyleOrNone, NonNegative}; use values::generics::basic_shape::ShapeRadius; use values::generics::gecko::ScrollSnapPoint; use values::generics::grid::{TrackBreadth, TrackKeyword}; /// A trait that defines an interface to convert from and to `nsStyleCoord`s. pub trait GeckoStyleCoordConvertible : Sized { /// Convert this to a `nsStyleCoord`. fn to_gecko_style_coord(&self, coord: &mut T); /// Given a `nsStyleCoord`, try to get a value of this type.. fn from_gecko_style_coord(coord: &T) -> Option; } impl nsStyleCoord { #[inline] /// Set this `nsStyleCoord` value to `val`. pub fn set(&mut self, val: T) { val.to_gecko_style_coord(self); } } impl GeckoStyleCoordConvertible for Either { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { Either::First(ref v) => v.to_gecko_style_coord(coord), Either::Second(ref v) => v.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { A::from_gecko_style_coord(coord) .map(Either::First) .or_else(|| B::from_gecko_style_coord(coord).map(Either::Second)) } } impl GeckoStyleCoordConvertible for Number { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Factor(*self)); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Factor(f) => Some(f), _ => None, } } } impl GeckoStyleCoordConvertible for Percentage { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Percent(self.0)); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Percent(p) => Some(Percentage(p)), _ => None, } } } impl GeckoStyleCoordConvertible for NumberOrPercentage { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { NumberOrPercentage::Number(ref n) => n.to_gecko_style_coord(coord), NumberOrPercentage::Percentage(ref p) => p.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Factor(f) => Some(NumberOrPercentage::Number(f)), CoordDataValue::Percent(p) => { Some(NumberOrPercentage::Percentage(Percentage(p))) }, _ => None, } } } impl GeckoStyleCoordConvertible for LengthOrPercentage { fn to_gecko_style_coord(&self, coord: &mut T) { let value = match *self { LengthOrPercentage::Length(au) => CoordDataValue::Coord(au.0), LengthOrPercentage::Percentage(p) => CoordDataValue::Percent(p.0), LengthOrPercentage::Calc(calc) => CoordDataValue::Calc(calc.into()), }; coord.set_value(value); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Coord(coord) => Some(LengthOrPercentage::Length(Au(coord))), CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(Percentage(p))), CoordDataValue::Calc(calc) => Some(LengthOrPercentage::Calc(calc.into())), _ => None, } } } impl GeckoStyleCoordConvertible for NonNegativeLengthOrPercentage { fn to_gecko_style_coord(&self, coord: &mut T) { self.0.to_gecko_style_coord(coord); } fn from_gecko_style_coord(coord: &T) -> Option { LengthOrPercentage::from_gecko_style_coord(coord).map(NonNegative::) } } impl GeckoStyleCoordConvertible for Au { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Coord(self.0)); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Coord(coord) => Some(Au(coord)), _ => None, } } } impl GeckoStyleCoordConvertible for NonNegativeAu { fn to_gecko_style_coord(&self, coord: &mut T) { self.0.to_gecko_style_coord(coord); } fn from_gecko_style_coord(coord: &T) -> Option { Au::from_gecko_style_coord(coord).map(NonNegative::) } } impl GeckoStyleCoordConvertible for NonNegativeNumber { fn to_gecko_style_coord(&self, coord: &mut T) { self.0.to_gecko_style_coord(coord); } fn from_gecko_style_coord(coord: &T) -> Option { Number::from_gecko_style_coord(coord).map(NonNegative::) } } impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto { fn to_gecko_style_coord(&self, coord: &mut T) { let value = match *self { LengthOrPercentageOrAuto::Length(au) => CoordDataValue::Coord(au.0), LengthOrPercentageOrAuto::Percentage(p) => CoordDataValue::Percent(p.0), LengthOrPercentageOrAuto::Auto => CoordDataValue::Auto, LengthOrPercentageOrAuto::Calc(calc) => CoordDataValue::Calc(calc.into()), }; coord.set_value(value); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrAuto::Length(Au(coord))), CoordDataValue::Percent(p) => Some(LengthOrPercentageOrAuto::Percentage(Percentage(p))), CoordDataValue::Auto => Some(LengthOrPercentageOrAuto::Auto), CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrAuto::Calc(calc.into())), _ => None, } } } impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone { fn to_gecko_style_coord(&self, coord: &mut T) { let value = match *self { LengthOrPercentageOrNone::Length(au) => CoordDataValue::Coord(au.0), LengthOrPercentageOrNone::Percentage(p) => CoordDataValue::Percent(p.0), LengthOrPercentageOrNone::None => CoordDataValue::None, LengthOrPercentageOrNone::Calc(calc) => CoordDataValue::Calc(calc.into()), }; coord.set_value(value); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrNone::Length(Au(coord))), CoordDataValue::Percent(p) => Some(LengthOrPercentageOrNone::Percentage(Percentage(p))), CoordDataValue::None => Some(LengthOrPercentageOrNone::None), CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrNone::Calc(calc.into())), _ => None, } } } impl GeckoStyleCoordConvertible for TrackBreadth { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { TrackBreadth::Breadth(ref lop) => lop.to_gecko_style_coord(coord), TrackBreadth::Flex(fr) => coord.set_value(CoordDataValue::FlexFraction(fr)), TrackBreadth::Keyword(TrackKeyword::Auto) => coord.set_value(CoordDataValue::Auto), TrackBreadth::Keyword(TrackKeyword::MinContent) => coord.set_value(CoordDataValue::Enumerated(StyleGridTrackBreadth::MinContent as u32)), TrackBreadth::Keyword(TrackKeyword::MaxContent) => coord.set_value(CoordDataValue::Enumerated(StyleGridTrackBreadth::MaxContent as u32)), } } fn from_gecko_style_coord(coord: &T) -> Option { L::from_gecko_style_coord(coord).map(TrackBreadth::Breadth).or_else(|| { match coord.as_value() { CoordDataValue::Enumerated(v) => { if v == StyleGridTrackBreadth::MinContent as u32 { Some(TrackBreadth::Keyword(TrackKeyword::MinContent)) } else if v == StyleGridTrackBreadth::MaxContent as u32 { Some(TrackBreadth::Keyword(TrackKeyword::MaxContent)) } else { None } }, CoordDataValue::FlexFraction(fr) => Some(TrackBreadth::Flex(fr)), CoordDataValue::Auto => Some(TrackBreadth::Keyword(TrackKeyword::Auto)), _ => L::from_gecko_style_coord(coord).map(TrackBreadth::Breadth), } }) } } impl GeckoStyleCoordConvertible for ComputedShapeRadius { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { ShapeRadius::ClosestSide => coord.set_value(CoordDataValue::Enumerated(StyleShapeRadius::ClosestSide as u32)), ShapeRadius::FarthestSide => coord.set_value(CoordDataValue::Enumerated(StyleShapeRadius::FarthestSide as u32)), ShapeRadius::Length(lop) => lop.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Enumerated(v) => { if v == StyleShapeRadius::ClosestSide as u32 { Some(ShapeRadius::ClosestSide) } else if v == StyleShapeRadius::FarthestSide as u32 { Some(ShapeRadius::FarthestSide) } else { None } } _ => LengthOrPercentage::from_gecko_style_coord(coord).map(ShapeRadius::Length), } } } impl GeckoStyleCoordConvertible for Option { fn to_gecko_style_coord(&self, coord: &mut U) { if let Some(ref me) = *self { me.to_gecko_style_coord(coord); } else { coord.set_value(CoordDataValue::None); } } fn from_gecko_style_coord(coord: &U) -> Option { Some(T::from_gecko_style_coord(coord)) } } impl GeckoStyleCoordConvertible for Angle { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::from(*self)); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Degree(val) => Some(Angle::Degree(val)), CoordDataValue::Grad(val) => Some(Angle::Gradian(val)), CoordDataValue::Radian(val) => Some(Angle::Radian(val)), CoordDataValue::Turn(val) => Some(Angle::Turn(val)), _ => None, } } } impl GeckoStyleCoordConvertible for Auto { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Auto) } fn from_gecko_style_coord(coord: &T) -> Option { if let CoordDataValue::Auto = coord.as_value() { Some(Auto) } else { None } } } impl GeckoStyleCoordConvertible for None_ { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::None) } fn from_gecko_style_coord(coord: &T) -> Option { if let CoordDataValue::None = coord.as_value() { Some(None_) } else { None } } } impl GeckoStyleCoordConvertible for Normal { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Normal) } fn from_gecko_style_coord(coord: &T) -> Option { if let CoordDataValue::Normal = coord.as_value() { Some(Normal) } else { None } } } impl GeckoStyleCoordConvertible for ExtremumLength { fn to_gecko_style_coord(&self, coord: &mut T) { use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT}; use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT}; coord.set_value(CoordDataValue::Enumerated( match *self { ExtremumLength::MaxContent => NS_STYLE_WIDTH_MAX_CONTENT, ExtremumLength::MinContent => NS_STYLE_WIDTH_MIN_CONTENT, ExtremumLength::FitContent => NS_STYLE_WIDTH_FIT_CONTENT, ExtremumLength::FillAvailable => NS_STYLE_WIDTH_AVAILABLE, } )) } fn from_gecko_style_coord(coord: &T) -> Option { use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT}; use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT}; match coord.as_value() { CoordDataValue::Enumerated(NS_STYLE_WIDTH_MAX_CONTENT) => Some(ExtremumLength::MaxContent), CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) => Some(ExtremumLength::MinContent), CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) => Some(ExtremumLength::FitContent), CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::FillAvailable), _ => None, } } } impl GeckoStyleCoordConvertible for MozLength { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { MozLength::LengthOrPercentageOrAuto(ref lopoa) => lopoa.to_gecko_style_coord(coord), MozLength::ExtremumLength(ref e) => e.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { LengthOrPercentageOrAuto::from_gecko_style_coord(coord).map(MozLength::LengthOrPercentageOrAuto) .or_else(|| ExtremumLength::from_gecko_style_coord(coord).map(MozLength::ExtremumLength)) } } impl GeckoStyleCoordConvertible for MaxLength { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { MaxLength::LengthOrPercentageOrNone(ref lopon) => lopon.to_gecko_style_coord(coord), MaxLength::ExtremumLength(ref e) => e.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { LengthOrPercentageOrNone::from_gecko_style_coord(coord).map(MaxLength::LengthOrPercentageOrNone) .or_else(|| ExtremumLength::from_gecko_style_coord(coord).map(MaxLength::ExtremumLength)) } } impl GeckoStyleCoordConvertible for ScrollSnapPoint { fn to_gecko_style_coord(&self, coord: &mut T) { match self.repeated() { None => coord.set_value(CoordDataValue::None), Some(l) => l.to_gecko_style_coord(coord), }; } fn from_gecko_style_coord(coord: &T) -> Option { use gecko_bindings::structs::root::nsStyleUnit; use values::generics::gecko::ScrollSnapPoint; Some( match coord.unit() { nsStyleUnit::eStyleUnit_None => ScrollSnapPoint::None, _ => ScrollSnapPoint::Repeat(LengthOrPercentage::from_gecko_style_coord(coord) .expect("coord could not convert to LengthOrPercentage")), } ) } } /// Convert a given RGBA value to `nscolor`. pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 { ((rgba.alpha as u32) << 24) | ((rgba.blue as u32) << 16) | ((rgba.green as u32) << 8) | (rgba.red as u32) } /// Convert a given `nscolor` to a Servo RGBA value. pub fn convert_nscolor_to_rgba(color: u32) -> RGBA { RGBA::new((color & 0xff) as u8, (color >> 8 & 0xff) as u8, (color >> 16 & 0xff) as u8, (color >> 24 & 0xff) as u8) } /// Round `width` down to the nearest device pixel, but any non-zero value that /// would round down to zero is clamped to 1 device pixel. Used for storing /// computed values of border-*-width and outline-width. #[inline] pub fn round_border_to_device_pixels(width: Au, au_per_device_px: Au) -> Au { if width == Au(0) { Au(0) } else { max(au_per_device_px, Au(width.0 / au_per_device_px.0 * au_per_device_px.0)) } } impl CounterStyleOrNone { /// Convert this counter style to a Gecko CounterStylePtr. pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr, device: &Device) { use gecko_bindings::bindings::Gecko_SetCounterStyleToName as set_name; use gecko_bindings::bindings::Gecko_SetCounterStyleToSymbols as set_symbols; let pres_context = device.pres_context(); match self { CounterStyleOrNone::None => unsafe { set_name(gecko_value, atom!("none").into_addrefed(), pres_context); }, CounterStyleOrNone::Name(name) => unsafe { set_name(gecko_value, name.0.into_addrefed(), pres_context); }, CounterStyleOrNone::Symbols(symbols_type, symbols) => { let symbols: Vec<_> = symbols.0.iter().map(|symbol| match *symbol { Symbol::String(ref s) => nsCString::from(s), Symbol::Ident(_) => unreachable!("Should not have identifier in symbols()"), }).collect(); let symbols: Vec<_> = symbols.iter() .map(|symbol| symbol as &nsACString as *const _) .collect(); unsafe { set_symbols(gecko_value, symbols_type.to_gecko_keyword(), symbols.as_ptr(), symbols.len() as u32) }; } } } /// Convert Gecko CounterStylePtr to CounterStyleOrNone. pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Self { use counter_style::{Symbol, Symbols}; use gecko_bindings::bindings::Gecko_CounterStyle_GetName; use gecko_bindings::bindings::Gecko_CounterStyle_GetSymbols; use gecko_bindings::bindings::Gecko_CounterStyle_GetSystem; use gecko_bindings::bindings::Gecko_CounterStyle_IsName; use gecko_bindings::bindings::Gecko_CounterStyle_IsNone; use values::CustomIdent; use values::generics::SymbolsType; if unsafe { Gecko_CounterStyle_IsNone(gecko_value) } { CounterStyleOrNone::None } else if unsafe { Gecko_CounterStyle_IsName(gecko_value) } { ns_auto_string!(name); unsafe { Gecko_CounterStyle_GetName(gecko_value, &mut *name) }; CounterStyleOrNone::Name(CustomIdent((&*name).into())) } else { let system = unsafe { Gecko_CounterStyle_GetSystem(gecko_value) }; let symbol_type = SymbolsType::from_gecko_keyword(system as u32); let symbols = unsafe { let ref gecko_symbols = *Gecko_CounterStyle_GetSymbols(gecko_value); gecko_symbols.iter().map(|gecko_symbol| { Symbol::String(gecko_symbol.to_string()) }).collect() }; CounterStyleOrNone::Symbols(symbol_type, Symbols(symbols)) } } }