/* 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 Atom; use app_units::Au; use counter_style::{Symbol, Symbols}; use cssparser::RGBA; use gecko_bindings::structs::{self, 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, nsCStr}; use std::cmp::max; use values::{Auto, Either, None_, Normal}; use values::computed::{Angle, ExtremumLength, Length, LengthOrPercentage, LengthOrPercentageOrAuto}; use values::computed::{LengthOrPercentageOrNone, Number, NumberOrPercentage}; use values::computed::{MaxLength, MozLength, Percentage}; use values::computed::{NonNegativeLength, NonNegativeLengthOrPercentage, NonNegativeNumber}; use values::computed::FlexBasis as ComputedFlexBasis; use values::computed::basic_shape::ShapeRadius as ComputedShapeRadius; use values::generics::{CounterStyleOrNone, NonNegative}; use values::generics::basic_shape::ShapeRadius; use values::generics::box_::Perspective; use values::generics::flex::FlexBasis; 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 ComputedFlexBasis { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { FlexBasis::Content => coord.set_value(CoordDataValue::Enumerated( structs::NS_STYLE_FLEX_BASIS_CONTENT, )), FlexBasis::Width(ref w) => w.to_gecko_style_coord(coord), } } fn from_gecko_style_coord(coord: &T) -> Option { if let Some(width) = MozLength::from_gecko_style_coord(coord) { return Some(FlexBasis::Width(width)); } if let CoordDataValue::Enumerated(structs::NS_STYLE_FLEX_BASIS_CONTENT) = coord.as_value() { return Some(FlexBasis::Content); } None } } 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(px) => CoordDataValue::Coord(px.to_i32_au()), 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).into())), 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 Length { fn to_gecko_style_coord(&self, coord: &mut T) { coord.set_value(CoordDataValue::Coord(self.to_i32_au())); } fn from_gecko_style_coord(coord: &T) -> Option { match coord.as_value() { CoordDataValue::Coord(coord) => Some(Au(coord).into()), _ => None, } } } impl GeckoStyleCoordConvertible for NonNegativeLength { fn to_gecko_style_coord(&self, coord: &mut T) { self.0.to_gecko_style_coord(coord); } fn from_gecko_style_coord(coord: &T) -> Option { Length::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(px) => CoordDataValue::Coord(px.to_i32_au()), 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).into())) }, 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(px) => CoordDataValue::Coord(px.to_i32_au()), 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).into())) }, 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::Fr(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::Fr(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::Deg(val)), CoordDataValue::Grad(val) => Some(Angle::Grad(val)), CoordDataValue::Radian(val) => Some(Angle::Rad(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::MozMaxContent => NS_STYLE_WIDTH_MAX_CONTENT, ExtremumLength::MozMinContent => NS_STYLE_WIDTH_MIN_CONTENT, ExtremumLength::MozFitContent => NS_STYLE_WIDTH_FIT_CONTENT, ExtremumLength::MozAvailable => 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::MozMaxContent) }, CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) => { Some(ExtremumLength::MozMinContent) }, CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) => { Some(ExtremumLength::MozFitContent) }, CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => { Some(ExtremumLength::MozAvailable) }, _ => 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"), ), }) } } impl GeckoStyleCoordConvertible for Perspective where L: GeckoStyleCoordConvertible, { fn to_gecko_style_coord(&self, coord: &mut T) { match *self { Perspective::None => coord.set_value(CoordDataValue::None), Perspective::Length(ref l) => l.to_gecko_style_coord(coord), }; } fn from_gecko_style_coord(coord: &T) -> Option { use gecko_bindings::structs::root::nsStyleUnit; if coord.unit() == nsStyleUnit::eStyleUnit_None { return Some(Perspective::None); } Some(Perspective::Length(L::from_gecko_style_coord(coord)?)) } } /// 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) => nsCStr::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 or String. pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either { use gecko_bindings::bindings; use values::CustomIdent; use values::generics::SymbolsType; let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) }; if !name.is_null() { let name = unsafe { Atom::from_raw(name) }; if name == atom!("none") { Either::First(CounterStyleOrNone::None) } else { Either::First(CounterStyleOrNone::Name(CustomIdent(name))) } } else { let anonymous = unsafe { bindings::Gecko_CounterStyle_GetAnonymous(gecko_value).as_ref() }.unwrap(); let symbols = &anonymous.mSymbols; if anonymous.mSingleString { debug_assert_eq!(symbols.len(), 1); Either::Second(symbols[0].to_string()) } else { let symbol_type = SymbolsType::from_gecko_keyword(anonymous.mSystem as u32); let symbols = symbols .iter() .map(|gecko_symbol| Symbol::String(gecko_symbol.to_string())) .collect(); Either::First(CounterStyleOrNone::Symbols(symbol_type, Symbols(symbols))) } } } }