/* 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/. */ //! This module contains conversion helpers between Servo and Gecko types //! Ideally, it would be in geckolib itself, but coherence //! forces us to keep the traits and implementations here #![allow(unsafe_code)] use app_units::Au; use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible}; use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue}; use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement}; use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue}; use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType}; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut}; use std::f32::consts::PI; use stylesheets::{Origin, RulesMutateError}; use values::computed::{Angle, CalcLengthOrPercentage, ComputedImageUrl, Gradient, Image}; use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, Percentage, TextAlign}; use values::generics::box_::VerticalAlign; use values::generics::grid::{TrackListValue, TrackSize}; use values::generics::image::{CompatMode, Image as GenericImage, GradientItem}; use values::generics::rect::Rect; impl From for nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { let has_percentage = other.percentage.is_some(); nsStyleCoord_CalcValue { mLength: other.unclamped_length().to_i32_au(), mPercent: other.percentage.map_or(0., |p| p.0), mHasPercent: has_percentage, } } } impl From for CalcLengthOrPercentage { fn from(other: nsStyleCoord_CalcValue) -> CalcLengthOrPercentage { let percentage = if other.mHasPercent { Some(Percentage(other.mPercent)) } else { None }; Self::new(Au(other.mLength).into(), percentage) } } impl From for nsStyleCoord_CalcValue { fn from(other: LengthOrPercentage) -> nsStyleCoord_CalcValue { match other { LengthOrPercentage::Length(px) => { nsStyleCoord_CalcValue { mLength: px.to_i32_au(), mPercent: 0.0, mHasPercent: false, } }, LengthOrPercentage::Percentage(pc) => { nsStyleCoord_CalcValue { mLength: 0, mPercent: pc.0, mHasPercent: true, } }, LengthOrPercentage::Calc(calc) => calc.into(), } } } impl LengthOrPercentageOrAuto { /// Convert this value in an appropriate `nsStyleCoord::CalcValue`. pub fn to_calc_value(&self) -> Option { match *self { LengthOrPercentageOrAuto::Length(px) => { Some(nsStyleCoord_CalcValue { mLength: px.to_i32_au(), mPercent: 0.0, mHasPercent: false, }) }, LengthOrPercentageOrAuto::Percentage(pc) => { Some(nsStyleCoord_CalcValue { mLength: 0, mPercent: pc.0, mHasPercent: true, }) }, LengthOrPercentageOrAuto::Calc(calc) => Some(calc.into()), LengthOrPercentageOrAuto::Auto => None, } } } impl From for LengthOrPercentage { fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage { match (other.mHasPercent, other.mLength) { (false, _) => LengthOrPercentage::Length(Au(other.mLength).into()), (true, 0) => LengthOrPercentage::Percentage(Percentage(other.mPercent)), _ => LengthOrPercentage::Calc(other.into()), } } } impl From for LengthOrPercentageOrAuto { fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentageOrAuto { match (other.mHasPercent, other.mLength) { (false, _) => LengthOrPercentageOrAuto::Length(Au(other.mLength).into()), (true, 0) => LengthOrPercentageOrAuto::Percentage(Percentage(other.mPercent)), _ => LengthOrPercentageOrAuto::Calc(other.into()), } } } impl From for CoordDataValue { fn from(reference: Angle) -> Self { match reference { Angle::Deg(val) => CoordDataValue::Degree(val), Angle::Grad(val) => CoordDataValue::Grad(val), Angle::Rad(val) => CoordDataValue::Radian(val), Angle::Turn(val) => CoordDataValue::Turn(val), } } } impl Angle { /// Converts Angle struct into (value, unit) pair. pub fn to_gecko_values(&self) -> (f32, nsCSSUnit) { match *self { Angle::Deg(val) => (val, nsCSSUnit::eCSSUnit_Degree), Angle::Grad(val) => (val, nsCSSUnit::eCSSUnit_Grad), Angle::Rad(val) => (val, nsCSSUnit::eCSSUnit_Radian), Angle::Turn(val) => (val, nsCSSUnit::eCSSUnit_Turn), } } /// Converts gecko (value, unit) pair into Angle struct pub fn from_gecko_values(value: f32, unit: nsCSSUnit) -> Angle { match unit { nsCSSUnit::eCSSUnit_Degree => Angle::Deg(value), nsCSSUnit::eCSSUnit_Grad => Angle::Grad(value), nsCSSUnit::eCSSUnit_Radian => Angle::Rad(value), nsCSSUnit::eCSSUnit_Turn => Angle::Turn(value), _ => panic!("Unexpected unit for angle"), } } } impl nsStyleImage { /// Set a given Servo `Image` value into this `nsStyleImage`. pub fn set(&mut self, image: Image) { match image { GenericImage::Gradient(boxed_gradient) => { self.set_gradient(*boxed_gradient) }, GenericImage::Url(ref url) => { unsafe { Gecko_SetLayerImageImageValue(self, url.image_value.get()); } }, GenericImage::Rect(ref image_rect) => { unsafe { Gecko_SetLayerImageImageValue(self, image_rect.url.image_value.get()); Gecko_InitializeImageCropRect(self); // Set CropRect let ref mut rect = *self.mCropRect.mPtr; image_rect.top.to_gecko_style_coord(&mut rect.data_at_mut(0)); image_rect.right.to_gecko_style_coord(&mut rect.data_at_mut(1)); image_rect.bottom.to_gecko_style_coord(&mut rect.data_at_mut(2)); image_rect.left.to_gecko_style_coord(&mut rect.data_at_mut(3)); } } GenericImage::Element(ref element) => { unsafe { Gecko_SetImageElement(self, element.as_ptr()); } } } } fn set_gradient(&mut self, gradient: Gradient) { use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE}; use gecko_bindings::structs::nsStyleCoord; use values::computed::image::LineDirection; use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent}; use values::specified::position::{X, Y}; let stop_count = gradient.items.len(); if stop_count >= ::std::u32::MAX as usize { warn!("stylo: Prevented overflow due to too many gradient stops"); return; } let gecko_gradient = match gradient.kind { GradientKind::Linear(direction) => { let gecko_gradient = unsafe { Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, gradient.repeating, gradient.compat_mode != CompatMode::Modern, gradient.compat_mode == CompatMode::Moz, stop_count as u32) }; match direction { LineDirection::Angle(angle) => { // PI radians (180deg) is ignored because it is the default value. if angle.radians() != PI { unsafe { (*gecko_gradient).mAngle.set(angle); } } }, LineDirection::Horizontal(x) => { let x = match x { X::Left => 0.0, X::Right => 1.0, }; unsafe { (*gecko_gradient).mBgPosX .set_value(CoordDataValue::Percent(x)); (*gecko_gradient).mBgPosY .set_value(CoordDataValue::Percent(0.5)); } }, LineDirection::Vertical(y) => { // Although bottom is the default value, we can not ignore // it here, because the rendering code of Gecko relies on // this to behave correctly for legacy mode. let y = match y { Y::Top => 0.0, Y::Bottom => 1.0, }; unsafe { (*gecko_gradient).mBgPosX .set_value(CoordDataValue::Percent(0.5)); (*gecko_gradient).mBgPosY .set_value(CoordDataValue::Percent(y)); } }, LineDirection::Corner(horiz, vert) => { let percent_x = match horiz { X::Left => 0.0, X::Right => 1.0, }; let percent_y = match vert { Y::Top => 0.0, Y::Bottom => 1.0, }; unsafe { (*gecko_gradient).mBgPosX .set_value(CoordDataValue::Percent(percent_x)); (*gecko_gradient).mBgPosY .set_value(CoordDataValue::Percent(percent_y)); } }, #[cfg(feature = "gecko")] LineDirection::MozPosition(position, angle) => { unsafe { if let Some(position) = position { (*gecko_gradient).mBgPosX.set(position.horizontal); (*gecko_gradient).mBgPosY.set(position.vertical); } if let Some(angle) = angle { (*gecko_gradient).mAngle.set(angle); } } }, } gecko_gradient }, GradientKind::Radial(shape, position, angle) => { let keyword_to_gecko_size = |keyword| { match keyword { ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, ShapeExtent::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, ShapeExtent::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, ShapeExtent::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, ShapeExtent::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, ShapeExtent::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, } }; let (gecko_shape, gecko_size) = match shape { EndingShape::Circle(ref circle) => { let size = match *circle { Circle::Extent(extent) => { keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) }, EndingShape::Ellipse(ref ellipse) => { let size = match *ellipse { Ellipse::Extent(extent) => { keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; (NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL as u8, size as u8) } }; let gecko_gradient = unsafe { Gecko_CreateGradient(gecko_shape, gecko_size, gradient.repeating, gradient.compat_mode == CompatMode::Moz, gradient.compat_mode == CompatMode::Moz, stop_count as u32) }; // Clear mBgPos field and set mAngle if angle is set. Otherwise clear it. unsafe { if let Some(angle) = angle { (*gecko_gradient).mAngle.set(angle); } } // Setting radius values depending shape match shape { EndingShape::Circle(Circle::Radius(length)) => { unsafe { let au = length.to_i32_au(); (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(au)); (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(au)); } }, EndingShape::Ellipse(Ellipse::Radii(x, y)) => { unsafe { (*gecko_gradient).mRadiusX.set(x); (*gecko_gradient).mRadiusY.set(y); } }, _ => {}, } unsafe { (*gecko_gradient).mBgPosX.set(position.horizontal); (*gecko_gradient).mBgPosY.set(position.vertical); } gecko_gradient }, }; for (index, item) in gradient.items.iter().enumerate() { // NB: stops are guaranteed to be none in the gecko side by // default. let gecko_stop = unsafe { &mut (*gecko_gradient).mStops[index] }; let mut coord = nsStyleCoord::null(); match *item { GradientItem::ColorStop(ref stop) => { gecko_stop.mColor = convert_rgba_to_nscolor(&stop.color); gecko_stop.mIsInterpolationHint = false; coord.set(stop.position); }, GradientItem::InterpolationHint(hint) => { gecko_stop.mIsInterpolationHint = true; coord.set(Some(hint)); } } gecko_stop.mLocation.move_from(coord); } unsafe { Gecko_SetGradientImageValue(self, gecko_gradient); } } /// Converts into Image. pub unsafe fn into_image(self: &nsStyleImage) -> Option { use gecko_bindings::bindings::Gecko_GetImageElement; use gecko_bindings::structs::nsStyleImageType; use values::computed::{NumberOrPercentage, MozImageRect}; match self.mType { nsStyleImageType::eStyleImageType_Null => { None }, nsStyleImageType::eStyleImageType_Image => { let url = self.get_image_url(); if self.mCropRect.mPtr.is_null() { Some(GenericImage::Url(url)) } else { let ref rect = *self.mCropRect.mPtr; match (NumberOrPercentage::from_gecko_style_coord(&rect.data_at(0)), NumberOrPercentage::from_gecko_style_coord(&rect.data_at(1)), NumberOrPercentage::from_gecko_style_coord(&rect.data_at(2)), NumberOrPercentage::from_gecko_style_coord(&rect.data_at(3))) { (Some(top), Some(right), Some(bottom), Some(left)) => Some(GenericImage::Rect(Box::new(MozImageRect { url, top, right, bottom, left } ))), _ => { debug_assert!(false, "mCropRect could not convert to NumberOrPercentage"); None } } } }, nsStyleImageType::eStyleImageType_Gradient => { Some(GenericImage::Gradient(self.get_gradient())) }, nsStyleImageType::eStyleImageType_Element => { use gecko_string_cache::Atom; let atom = Gecko_GetImageElement(self); Some(GenericImage::Element(Atom::from_raw(atom))) }, _ => panic!("Unexpected image type") } } unsafe fn get_image_url(self: &nsStyleImage) -> ComputedImageUrl { use gecko_bindings::bindings::Gecko_GetURLValue; let url_value = Gecko_GetURLValue(self); ComputedImageUrl::from_url_value_data(url_value.as_ref().unwrap()) .expect("Could not convert to ComputedUrl") } unsafe fn get_gradient(self: &nsStyleImage) -> Box { use gecko::values::convert_nscolor_to_rgba; use gecko_bindings::bindings::Gecko_GetGradientImageValue; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE}; use values::computed::{Length, LengthOrPercentage}; use values::computed::image::LineDirection; use values::computed::position::Position; use values::generics::image::{ColorStop, CompatMode, Circle, Ellipse, EndingShape, GradientKind, ShapeExtent}; use values::specified::position::{X, Y}; let gecko_gradient = Gecko_GetGradientImageValue(self).as_ref().unwrap(); let angle = Angle::from_gecko_style_coord(&gecko_gradient.mAngle); let horizontal_style = LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosX); let vertical_style = LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosY); let kind = match gecko_gradient.mShape as u32 { NS_STYLE_GRADIENT_SHAPE_LINEAR => { let line_direction = match (angle, horizontal_style, vertical_style) { (Some(a), None, None) => LineDirection::Angle(a), (None, Some(horizontal), Some(vertical)) => { let horizontal_as_corner = match horizontal { LengthOrPercentage::Percentage(percentage) => { if percentage.0 == 0.0 { Some(X::Left) } else if percentage.0 == 1.0 { Some(X::Right) } else { None } }, _ => None }; let vertical_as_corner = match vertical { LengthOrPercentage::Percentage(percentage) => { if percentage.0 == 0.0 { Some(Y::Top) } else if percentage.0 == 1.0 { Some(Y::Bottom) } else { None } }, _ => None }; match (horizontal_as_corner, vertical_as_corner) { (Some(hc), Some(vc)) => LineDirection::Corner(hc, vc), _ => LineDirection::MozPosition( Some(Position { horizontal, vertical }), None) } }, (Some(_), Some(horizontal), Some(vertical)) => LineDirection::MozPosition( Some(Position { horizontal, vertical }), angle), _ => { debug_assert!(horizontal_style.is_none() && vertical_style.is_none(), "Unexpected linear gradient direction"); LineDirection::MozPosition(None, None) } }; GradientKind::Linear(line_direction) }, _ => { let gecko_size_to_keyword = |gecko_size| { match gecko_size { NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE => ShapeExtent::ClosestSide, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE => ShapeExtent::FarthestSide, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER => ShapeExtent::ClosestCorner, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER => ShapeExtent::FarthestCorner, // FIXME: We should support ShapeExtent::Contain and ShapeExtent::Cover. // But we can't choose those yet since Gecko does not support both values. // https://bugzilla.mozilla.org/show_bug.cgi?id=1217664 _ => panic!("Found unexpected gecko_size"), } }; let shape = match gecko_gradient.mShape as u32 { NS_STYLE_GRADIENT_SHAPE_CIRCULAR => { let circle = match gecko_gradient.mSize as u32 { NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => { let radius = Length::from_gecko_style_coord(&gecko_gradient.mRadiusX) .expect("mRadiusX could not convert to Length"); debug_assert_eq!(radius, Length::from_gecko_style_coord(&gecko_gradient.mRadiusY).unwrap()); Circle::Radius(radius) }, size => Circle::Extent(gecko_size_to_keyword(size)) }; EndingShape::Circle(circle) }, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL => { let length_percentage_keyword = match gecko_gradient.mSize as u32 { NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => { match (LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusX), LengthOrPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusY)) { (Some(x), Some(y)) => Ellipse::Radii(x, y), _ => { debug_assert!(false, "mRadiusX, mRadiusY could not convert to LengthOrPercentage"); Ellipse::Radii(LengthOrPercentage::zero(), LengthOrPercentage::zero()) } } }, size => Ellipse::Extent(gecko_size_to_keyword(size)) }; EndingShape::Ellipse(length_percentage_keyword) }, _ => panic!("Found unexpected mShape"), }; let position = match (horizontal_style, vertical_style) { (Some(horizontal), Some(vertical)) => Position { horizontal, vertical }, _ => { debug_assert!(false, "mRadiusX, mRadiusY could not convert to LengthOrPercentage"); Position { horizontal: LengthOrPercentage::zero(), vertical: LengthOrPercentage::zero() } } }; GradientKind::Radial(shape, position, angle) } }; let items = gecko_gradient.mStops.iter().map(|ref stop| { if stop.mIsInterpolationHint { GradientItem::InterpolationHint( LengthOrPercentage::from_gecko_style_coord(&stop.mLocation) .expect("mLocation could not convert to LengthOrPercentage") ) } else { GradientItem::ColorStop(ColorStop { color: convert_nscolor_to_rgba(stop.mColor), position: LengthOrPercentage::from_gecko_style_coord(&stop.mLocation) }) } }).collect(); let compat_mode = if gecko_gradient.mMozLegacySyntax { CompatMode::Moz } else if gecko_gradient.mLegacySyntax { CompatMode::WebKit } else { CompatMode::Modern }; Box::new(Gradient { items, repeating: gecko_gradient.mRepeating, kind, compat_mode }) } } pub mod basic_shape { //! Conversions from and to CSS shape representations. use gecko::values::GeckoStyleCoordConvertible; use gecko_bindings::structs; use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule}; use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType}; use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners}; use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue}; use std::borrow::Borrow; use values::computed::ComputedUrl; use values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape, ShapeRadius}; use values::computed::border::{BorderCornerRadius, BorderRadius}; use values::computed::length::LengthOrPercentage; use values::computed::position; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; use values::generics::basic_shape::{Circle, Ellipse, FillRule}; use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use values::generics::border::BorderRadius as GenericBorderRadius; use values::generics::rect::Rect; impl StyleShapeSource { /// Convert StyleShapeSource to ShapeSource except URL and Image /// types. fn into_shape_source( &self ) -> Option> where ReferenceBox: From { match self.mType { StyleShapeSourceType::None => Some(ShapeSource::None), StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())), StyleShapeSourceType::Shape => { let other_shape = unsafe { &*self.mBasicShape.mPtr }; let shape = other_shape.into(); let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox { None } else { Some(self.mReferenceBox.into()) }; Some(ShapeSource::Shape(shape, reference_box)) }, StyleShapeSourceType::URL | StyleShapeSourceType::Image => None, } } } impl<'a> From<&'a StyleShapeSource> for ClippingShape { fn from(other: &'a StyleShapeSource) -> Self { match other.mType { StyleShapeSourceType::URL => { unsafe { let shape_image = &*other.mShapeImage.mPtr; let other_url = &(**shape_image.__bindgen_anon_1.mURLValue.as_ref()); let url = ComputedUrl::from_url_value_data(&other_url._base).unwrap(); ShapeSource::ImageOrUrl(url) } }, StyleShapeSourceType::Image => { unreachable!("ClippingShape doesn't support Image!"); } _ => other.into_shape_source().expect("Couldn't convert to StyleSource!") } } } impl<'a> From<&'a StyleShapeSource> for FloatAreaShape { fn from(other: &'a StyleShapeSource) -> Self { match other.mType { StyleShapeSourceType::URL => { unreachable!("FloatAreaShape doesn't support URL!"); }, StyleShapeSourceType::Image => { unsafe { let shape_image = &*other.mShapeImage.mPtr; let image = shape_image.into_image().expect("Cannot convert to Image"); ShapeSource::ImageOrUrl(image) } } _ => other.into_shape_source().expect("Couldn't convert to StyleSource!") } } } impl<'a> From<&'a StyleBasicShape> for BasicShape { fn from(other: &'a StyleBasicShape) -> Self { match other.mType { StyleBasicShapeType::Inset => { let t = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[0]); let r = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[1]); let b = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[2]); let l = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[3]); let round = (&other.mRadius).into(); let rect = Rect::new( t.expect("inset() offset should be a length, percentage, or calc value"), r.expect("inset() offset should be a length, percentage, or calc value"), b.expect("inset() offset should be a length, percentage, or calc value"), l.expect("inset() offset should be a length, percentage, or calc value"), ); GenericBasicShape::Inset(InsetRect { rect: rect, round: Some(round), }) } StyleBasicShapeType::Circle => { GenericBasicShape::Circle(Circle { radius: (&other.mCoordinates[0]).into(), position: (&other.mPosition).into() }) } StyleBasicShapeType::Ellipse => { GenericBasicShape::Ellipse(Ellipse { semiaxis_x: (&other.mCoordinates[0]).into(), semiaxis_y: (&other.mCoordinates[1]).into(), position: (&other.mPosition).into() }) } StyleBasicShapeType::Polygon => { let fill_rule = if other.mFillRule == StyleFillRule::Evenodd { FillRule::Evenodd } else { FillRule::Nonzero }; let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2); for i in 0..(other.mCoordinates.len() / 2) { let x = 2 * i; let y = x + 1; coords.push((LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[x]) .expect("polygon() coordinate should be a length, percentage, or calc value"), LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[y]) .expect("polygon() coordinate should be a length, percentage, or calc value") )) } GenericBasicShape::Polygon(Polygon { fill: fill_rule, coordinates: coords, }) } } } } impl<'a> From<&'a nsStyleCorners> for BorderRadius { fn from(other: &'a nsStyleCorners) -> Self { let get_corner = |index| { BorderCornerRadius::new( LengthOrPercentage::from_gecko_style_coord(&other.data_at(index)) .expect(" should be a length, percentage, or calc value"), LengthOrPercentage::from_gecko_style_coord(&other.data_at(index + 1)) .expect(" should be a length, percentage, or calc value")) }; GenericBorderRadius { top_left: get_corner(0), top_right: get_corner(2), bottom_right: get_corner(4), bottom_left: get_corner(6), } } } // Can't be a From impl since we need to set an existing // nsStyleCorners, not create a new one impl BorderRadius { /// Set this `BorderRadius` into a given `nsStyleCoord`. pub fn set_corners(&self, other: &mut nsStyleCorners) { let mut set_corner = |field: &BorderCornerRadius, index| { field.0.width().to_gecko_style_coord(&mut other.data_at_mut(index)); field.0.height().to_gecko_style_coord(&mut other.data_at_mut(index + 1)); }; set_corner(&self.top_left, 0); set_corner(&self.top_right, 2); set_corner(&self.bottom_right, 4); set_corner(&self.bottom_left, 6); } } /// We use None for a nonexistant radius, but Gecko uses (0 0 0 0 / 0 0 0 0) pub fn set_corners_from_radius(radius: Option, other: &mut nsStyleCorners) { if let Some(radius) = radius { radius.set_corners(other); } else { for i in 0..8 { other.data_at_mut(i).set_value(CoordDataValue::Coord(0)); } } } // Can't be a From impl since we need to set an existing // Position, not create a new one impl From for structs::Position { fn from(other: position::Position) -> Self { structs::Position { mXPosition: other.horizontal.into(), mYPosition: other.vertical.into() } } } impl<'a> From<&'a nsStyleCoord> for ShapeRadius { fn from(other: &'a nsStyleCoord) -> Self { let other = other.borrow(); ShapeRadius::from_gecko_style_coord(other) .expect(" should be a length, percentage, calc, or keyword value") } } impl<'a> From<&'a structs::Position> for position::Position { fn from(other: &'a structs::Position) -> Self { position::Position { horizontal: other.mXPosition.into(), vertical: other.mYPosition.into(), } } } impl From for StyleGeometryBox { fn from(reference: ShapeBox) -> Self { use gecko_bindings::structs::StyleGeometryBox::*; match reference { ShapeBox::ContentBox => ContentBox, ShapeBox::PaddingBox => PaddingBox, ShapeBox::BorderBox => BorderBox, ShapeBox::MarginBox => MarginBox, } } } impl From for StyleGeometryBox { fn from(reference: GeometryBox) -> Self { use gecko_bindings::structs::StyleGeometryBox::*; match reference { GeometryBox::ShapeBox(shape_box) => From::from(shape_box), GeometryBox::FillBox => FillBox, GeometryBox::StrokeBox => StrokeBox, GeometryBox::ViewBox => ViewBox, } } } // Will panic on NoBox // Ideally these would be implemented on Option, // but coherence doesn't like that and TryFrom isn't stable impl From for GeometryBox { fn from(reference: StyleGeometryBox) -> Self { use gecko_bindings::structs::StyleGeometryBox::*; match reference { ContentBox => GeometryBox::ShapeBox(ShapeBox::ContentBox), PaddingBox => GeometryBox::ShapeBox(ShapeBox::PaddingBox), BorderBox => GeometryBox::ShapeBox(ShapeBox::BorderBox), MarginBox => GeometryBox::ShapeBox(ShapeBox::MarginBox), FillBox => GeometryBox::FillBox, StrokeBox => GeometryBox::StrokeBox, ViewBox => GeometryBox::ViewBox, _ => panic!("Unexpected StyleGeometryBox while converting to GeometryBox"), } } } impl From for ShapeBox { fn from(reference: StyleGeometryBox) -> Self { use gecko_bindings::structs::StyleGeometryBox::*; match reference { ContentBox => ShapeBox::ContentBox, PaddingBox => ShapeBox::PaddingBox, BorderBox => ShapeBox::BorderBox, MarginBox => ShapeBox::MarginBox, _ => panic!("Unexpected StyleGeometryBox while converting to ShapeBox"), } } } } impl From for nsresult { fn from(other: RulesMutateError) -> Self { match other { RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR, RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR, RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR, } } } impl From for SheetType { fn from(other: Origin) -> Self { match other { Origin::UserAgent => SheetType::Agent, Origin::Author => SheetType::Doc, Origin::User => SheetType::User, } } } impl TrackSize { /// Return TrackSize from given two nsStyleCoord pub fn from_gecko_style_coords(gecko_min: &T, gecko_max: &T) -> Self { use gecko_bindings::structs::root::nsStyleUnit; use values::computed::length::LengthOrPercentage; use values::generics::grid::{TrackBreadth, TrackSize}; if gecko_min.unit() == nsStyleUnit::eStyleUnit_None { debug_assert!(gecko_max.unit() == nsStyleUnit::eStyleUnit_Coord || gecko_max.unit() == nsStyleUnit::eStyleUnit_Percent || gecko_max.unit() == nsStyleUnit::eStyleUnit_Calc); return TrackSize::FitContent(LengthOrPercentage::from_gecko_style_coord(gecko_max) .expect("gecko_max could not convert to LengthOrPercentage")); } let min = TrackBreadth::from_gecko_style_coord(gecko_min) .expect("gecko_min could not convert to TrackBreadth"); let max = TrackBreadth::from_gecko_style_coord(gecko_max) .expect("gecko_max could not convert to TrackBreadth"); if min == max { TrackSize::Breadth(max) } else { TrackSize::Minmax(min, max) } } /// Save TrackSize to given gecko fields. pub fn to_gecko_style_coords(&self, gecko_min: &mut T, gecko_max: &mut T) { use values::generics::grid::TrackSize; match *self { TrackSize::FitContent(ref lop) => { // Gecko sets min value to None and max value to the actual value in fit-content // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8221 gecko_min.set_value(CoordDataValue::None); lop.to_gecko_style_coord(gecko_max); }, TrackSize::Breadth(ref breadth) => { // Set the value to both fields if there's one breadth value // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8230 breadth.to_gecko_style_coord(gecko_min); breadth.to_gecko_style_coord(gecko_max); }, TrackSize::Minmax(ref min, ref max) => { min.to_gecko_style_coord(gecko_min); max.to_gecko_style_coord(gecko_max); }, } } } impl TrackListValue { /// Return TrackSize from given two nsStyleCoord pub fn from_gecko_style_coords(gecko_min: &T, gecko_max: &T) -> Self { TrackListValue::TrackSize(TrackSize::from_gecko_style_coords(gecko_min, gecko_max)) } /// Save TrackSize to given gecko fields. pub fn to_gecko_style_coords(&self, gecko_min: &mut T, gecko_max: &mut T) { use values::generics::grid::TrackListValue; match *self { TrackListValue::TrackSize(ref size) => size.to_gecko_style_coords(gecko_min, gecko_max), _ => unreachable!("Should only transform from track-size computed values"), } } } impl Rect where T: GeckoStyleCoordConvertible { /// Convert this generic Rect to given Gecko fields. pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) { self.0.to_gecko_style_coord(&mut sides.data_at_mut(0)); self.1.to_gecko_style_coord(&mut sides.data_at_mut(1)); self.2.to_gecko_style_coord(&mut sides.data_at_mut(2)); self.3.to_gecko_style_coord(&mut sides.data_at_mut(3)); } /// Convert from given Gecko data to generic Rect. pub fn from_gecko_rect(sides: &::gecko_bindings::structs::nsStyleSides) -> Option<::values::generics::rect::Rect> { use values::generics::rect::Rect; Some( Rect::new( T::from_gecko_style_coord(&sides.data_at(0)).expect("coord[0] cound not convert"), T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"), T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"), T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert") ) ) } } impl VerticalAlign { /// Converts an enumerated value coming from Gecko to a `VerticalAlign`. pub fn from_gecko_keyword(value: u32) -> Self { match value { structs::NS_STYLE_VERTICAL_ALIGN_BASELINE => VerticalAlign::Baseline, structs::NS_STYLE_VERTICAL_ALIGN_SUB => VerticalAlign::Sub, structs::NS_STYLE_VERTICAL_ALIGN_SUPER => VerticalAlign::Super, structs::NS_STYLE_VERTICAL_ALIGN_TOP => VerticalAlign::Top, structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP => VerticalAlign::TextTop, structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE => VerticalAlign::Middle, structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM => VerticalAlign::Bottom, structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM => VerticalAlign::TextBottom, structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE => { VerticalAlign::MozMiddleWithBaseline }, _ => panic!("unexpected enumerated value for vertical-align"), } } } impl TextAlign { /// Obtain a specified value from a Gecko keyword value /// /// Intended for use with presentation attributes, not style structs pub fn from_gecko_keyword(kw: u32) -> Self { match kw { structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left, structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right, structs::NS_STYLE_TEXT_ALIGN_CENTER => TextAlign::Center, structs::NS_STYLE_TEXT_ALIGN_JUSTIFY => TextAlign::Justify, structs::NS_STYLE_TEXT_ALIGN_MOZ_LEFT => TextAlign::MozLeft, structs::NS_STYLE_TEXT_ALIGN_MOZ_RIGHT => TextAlign::MozRight, structs::NS_STYLE_TEXT_ALIGN_MOZ_CENTER => TextAlign::MozCenter, structs::NS_STYLE_TEXT_ALIGN_CHAR => TextAlign::Char, structs::NS_STYLE_TEXT_ALIGN_END => TextAlign::End, _ => panic!("Found unexpected value in style struct for text-align property"), } } } /// Convert to String from given chars pointer. pub unsafe fn string_from_chars_pointer(p: *const u16) -> String { use std::slice; let mut length = 0; let mut iter = p; while *iter != 0 { length += 1; iter = iter.offset(1); } let char_vec = slice::from_raw_parts(p, length as usize); String::from_utf16_lossy(char_vec) }