diff --git a/servo/components/style/gecko_conversions.rs b/servo/components/style/gecko_conversions.rs index ede574d181eb..4e3a5acf2c90 100644 --- a/servo/components/style/gecko_conversions.rs +++ b/servo/components/style/gecko_conversions.rs @@ -79,3 +79,182 @@ impl From for LengthOrPercentage { } } } + +pub mod basic_shape { + use euclid::size::Size2D; + use gecko_bindings::structs::StyleClipPathGeometryBox; + use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule}; + use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners, nsStyleImageLayers_Position}; + use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue}; + use gecko_values::GeckoStyleCoordConvertible; + use std::borrow::Borrow; + use values::computed::basic_shape::*; + use values::computed::position::Position; + use values::computed::{BorderRadiusSize, LengthOrPercentage}; + + // using Borrow so that we can have a non-moving .into() + impl> From for BasicShape { + fn from(other: T) -> Self { + let other = other.borrow(); + 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(); + BasicShape::Inset(InsetRect { + top: t.expect("inset() offset should be a length, percentage, or calc value"), + right: r.expect("inset() offset should be a length, percentage, or calc value"), + bottom: b.expect("inset() offset should be a length, percentage, or calc value"), + left: l.expect("inset() offset should be a length, percentage, or calc value"), + round: Some(round), + }) + } + StyleBasicShapeType::Circle => { + BasicShape::Circle(Circle { + radius: (&other.mCoordinates[0]).into(), + position: (&other.mPosition).into() + }) + } + StyleBasicShapeType::Ellipse => { + BasicShape::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") + )) + } + BasicShape::Polygon(Polygon { + fill: fill_rule, + coordinates: coords, + }) + } + } + } + } + + impl> From for BorderRadius { + fn from(other: T) -> Self { + let other = other.borrow(); + let get_corner = |index| { + BorderRadiusSize(Size2D::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"))) + }; + + BorderRadius { + 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 { + pub fn set_corners(&self, other: &mut nsStyleCorners) { + let mut set_corner = |field: &BorderRadiusSize, 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 + // nsStyleImageLayers_Position, not create a new one + impl From for nsStyleImageLayers_Position { + fn from(other: Position) -> Self { + nsStyleImageLayers_Position { + mXPosition: other.horizontal.into(), + mYPosition: other.vertical.into() + } + } + } + + impl> From for ShapeRadius { + fn from(other: T) -> Self { + let other = other.borrow(); + ShapeRadius::from_gecko_style_coord(other) + .expect(" should be a length, percentage, calc, or keyword value") + } + } + + impl> From for Position { + fn from(other: T) -> Self { + let other = other.borrow(); + Position { + horizontal: other.mXPosition.into(), + vertical: other.mYPosition.into(), + } + } + } + + impl From for StyleClipPathGeometryBox { + fn from(reference: GeometryBox) -> Self { + use gecko_bindings::structs::StyleClipPathGeometryBox::*; + match reference { + GeometryBox::ShapeBox(ShapeBox::Content) => Content, + GeometryBox::ShapeBox(ShapeBox::Padding) => Padding, + GeometryBox::ShapeBox(ShapeBox::Border) => Border, + GeometryBox::ShapeBox(ShapeBox::Margin) => Margin, + GeometryBox::Fill => Fill, + GeometryBox::Stroke => Stroke, + GeometryBox::View => View, + } + } + } + + // 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: StyleClipPathGeometryBox) -> Self { + use gecko_bindings::structs::StyleClipPathGeometryBox::*; + match reference { + NoBox => panic!("Shouldn't convert NoBox to GeometryBox"), + Content => GeometryBox::ShapeBox(ShapeBox::Content), + Padding => GeometryBox::ShapeBox(ShapeBox::Padding), + Border => GeometryBox::ShapeBox(ShapeBox::Border), + Margin => GeometryBox::ShapeBox(ShapeBox::Margin), + Fill => GeometryBox::Fill, + Stroke => GeometryBox::Stroke, + View => GeometryBox::View, + } + } + } +} diff --git a/servo/components/style/gecko_values.rs b/servo/components/style/gecko_values.rs index efbcb7db93e1..f3c570f34cc5 100644 --- a/servo/components/style/gecko_values.rs +++ b/servo/components/style/gecko_values.rs @@ -7,12 +7,13 @@ use app_units::Au; use cssparser::RGBA; use gecko_bindings::structs::nsStyleCoord; +use gecko_bindings::structs::{NS_RADIUS_CLOSEST_SIDE, NS_RADIUS_FARTHEST_SIDE}; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut}; use std::cmp::max; use values::computed::Angle; +use values::computed::basic_shape::ShapeRadius; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; - pub trait StyleCoordHelpers { fn set(&mut self, val: T); } @@ -94,6 +95,28 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone { } } +impl GeckoStyleCoordConvertible for ShapeRadius { + fn to_gecko_style_coord(&self, coord: &mut T) { + match *self { + ShapeRadius::ClosestSide => { + coord.set_value(CoordDataValue::Enumerated(NS_RADIUS_CLOSEST_SIDE)) + } + ShapeRadius::FarthestSide => { + coord.set_value(CoordDataValue::Enumerated(NS_RADIUS_FARTHEST_SIDE)) + } + ShapeRadius::Length(lop) => lop.to_gecko_style_coord(coord), + } + } + + fn from_gecko_style_coord(coord: &T) -> Option { + match coord.as_value() { + CoordDataValue::Enumerated(NS_RADIUS_CLOSEST_SIDE) => Some(ShapeRadius::ClosestSide), + CoordDataValue::Enumerated(NS_RADIUS_FARTHEST_SIDE) => Some(ShapeRadius::FarthestSide), + _ => 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 { diff --git a/servo/components/style/parser.rs b/servo/components/style/parser.rs index 52ce1d9d5fd1..db281ec24e2c 100644 --- a/servo/components/style/parser.rs +++ b/servo/components/style/parser.rs @@ -81,3 +81,11 @@ impl<'a> ParserContext<'a> { pub fn log_css_error(input: &mut Parser, position: SourcePosition, message: &str, parsercontext: &ParserContext) { parsercontext.error_reporter.report_error(input, position, message); } + +// XXXManishearth Replace all specified value parse impls with impls of this +// trait. This will make it easy to write more generic values in the future. +// There may need to be two traits -- one for parsing with context, and one +// for parsing without +pub trait Parse { + fn parse(input: &mut Parser) -> Result where Self: Sized; +} diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 085c35219c3a..0c717ee678cd 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -1265,7 +1265,7 @@ fn static_assert() { <%self:impl_trait style_struct_name="SVG" - skip_longhands="flood-color lighting-color stop-color" + skip_longhands="flood-color lighting-color stop-color clip-path" skip_additionals="*"> <% impl_color("flood_color", "mFloodColor") %> @@ -1274,6 +1274,140 @@ fn static_assert() { <% impl_color("stop_color", "mStopColor") %> + pub fn set_clip_path(&mut self, v: longhands::clip_path::computed_value::T) { + use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyClipPath}; + use gecko_bindings::structs::StyleClipPathGeometryBox; + use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; + use gecko_bindings::structs::{StyleClipPath, StyleFillRule}; + use gecko_conversions::basic_shape::set_corners_from_radius; + use gecko_values::GeckoStyleCoordConvertible; + use values::computed::basic_shape::*; + let ref mut clip_path = self.gecko.mClipPath; + // clean up existing struct + unsafe { Gecko_DestroyClipPath(clip_path) }; + + clip_path.mType = StyleShapeSourceType::None_; + + match v { + ShapeSource::Url(..) => println!("stylo: clip-path: url() not yet implemented"), + ShapeSource::None => {} // don't change the type + ShapeSource::Box(reference) => { + clip_path.mReferenceBox = reference.into(); + clip_path.mType = StyleShapeSourceType::Box; + } + ShapeSource::Shape(servo_shape, maybe_box) => { + clip_path.mReferenceBox = maybe_box.map(Into::into) + .unwrap_or(StyleClipPathGeometryBox::NoBox); + clip_path.mType = StyleShapeSourceType::Shape; + + fn init_shape(clip_path: &mut StyleClipPath, ty: StyleBasicShapeType) -> &mut StyleBasicShape { + unsafe { + // We have to be very careful to avoid a copy here! + let ref mut union = clip_path.StyleShapeSource_nsStyleStruct_h_unnamed_26; + let mut shape: &mut *mut StyleBasicShape = union.mBasicShape.as_mut(); + *shape = Gecko_NewBasicShape(ty); + &mut **shape + } + } + match servo_shape { + BasicShape::Inset(rect) => { + let mut shape = init_shape(clip_path, StyleBasicShapeType::Inset); + unsafe { shape.mCoordinates.set_len(4) }; + + // set_len() can't call constructors, so the coordinates + // can contain any value. set_value() attempts to free + // allocated coordinates, so we don't want to feed it + // garbage values which it may misinterpret. + // Instead, we use leaky_set_value to blindly overwrite + // the garbage data without + // attempting to clean up. + shape.mCoordinates[0].leaky_set_null(); + rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]); + shape.mCoordinates[1].leaky_set_null(); + rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]); + shape.mCoordinates[2].leaky_set_null(); + rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]); + shape.mCoordinates[3].leaky_set_null(); + rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]); + + set_corners_from_radius(rect.round, &mut shape.mRadius); + } + BasicShape::Circle(circ) => { + let mut shape = init_shape(clip_path, StyleBasicShapeType::Circle); + unsafe { shape.mCoordinates.set_len(1) }; + shape.mCoordinates[0].leaky_set_null(); + circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]); + + shape.mPosition = circ.position.into(); + } + BasicShape::Ellipse(el) => { + let mut shape = init_shape(clip_path, StyleBasicShapeType::Ellipse); + unsafe { shape.mCoordinates.set_len(2) }; + shape.mCoordinates[0].leaky_set_null(); + el.semiaxis_x.to_gecko_style_coord(&mut shape.mCoordinates[0]); + shape.mCoordinates[1].leaky_set_null(); + el.semiaxis_y.to_gecko_style_coord(&mut shape.mCoordinates[1]); + + shape.mPosition = el.position.into(); + } + BasicShape::Polygon(poly) => { + let mut shape = init_shape(clip_path, StyleBasicShapeType::Polygon); + unsafe { + shape.mCoordinates.set_len(poly.coordinates.len() as u32 * 2); + } + for (i, coord) in poly.coordinates.iter().enumerate() { + shape.mCoordinates[2 * i].leaky_set_null(); + shape.mCoordinates[2 * i + 1].leaky_set_null(); + coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]); + coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]); + } + shape.mFillRule = if poly.fill == FillRule::EvenOdd { + StyleFillRule::Evenodd + } else { + StyleFillRule::Nonzero + }; + } + } + } + } + + } + + pub fn copy_clip_path_from(&mut self, other: &Self) { + use gecko_bindings::bindings::Gecko_CopyClipPathValueFrom; + unsafe { + Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath); + } + } + + pub fn clone_clip_path(&self) -> longhands::clip_path::computed_value::T { + use gecko_bindings::structs::StyleShapeSourceType; + use gecko_bindings::structs::StyleClipPathGeometryBox; + use values::computed::basic_shape::*; + let ref clip_path = self.gecko.mClipPath; + + match clip_path.mType { + StyleShapeSourceType::None_ => ShapeSource::None, + StyleShapeSourceType::Box => { + ShapeSource::Box(clip_path.mReferenceBox.into()) + } + StyleShapeSourceType::URL => { + warn!("stylo: clip-path: url() not implemented yet"); + Default::default() + } + StyleShapeSourceType::Shape => { + let reference = if let StyleClipPathGeometryBox::NoBox = clip_path.mReferenceBox { + None + } else { + Some(clip_path.mReferenceBox.into()) + }; + let union = clip_path.StyleShapeSource_nsStyleStruct_h_unnamed_26; + let shape = unsafe { &**union.mBasicShape.as_ref() }; + ShapeSource::Shape(shape.into(), reference) + } + } + } + <%self:impl_trait style_struct_name="Color" diff --git a/servo/components/style/properties/longhand/svg.mako.rs b/servo/components/style/properties/longhand/svg.mako.rs index 49dd2bdbdacf..3d0c5cc48aa6 100644 --- a/servo/components/style/properties/longhand/svg.mako.rs +++ b/servo/components/style/properties/longhand/svg.mako.rs @@ -49,3 +49,31 @@ ${helpers.predefined_type( // https://www.w3.org/TR/css-masking-1/ ${helpers.single_keyword("mask-type", "luminance alpha", products="gecko", animatable=False)} + +<%helpers:longhand name="clip-path" animatable="False" products="gecko"> + use cssparser::ToCss; + use std::fmt; + use values::LocalToCss; + use values::NoViewportPercentage; + use values::specified::basic_shape::{ShapeSource, GeometryBox}; + + pub mod computed_value { + use app_units::Au; + use values::computed::basic_shape::{ShapeSource, GeometryBox}; + + pub type T = ShapeSource; + } + + pub type SpecifiedValue = ShapeSource; + + #[inline] + pub fn get_initial_value() -> computed_value::T { + Default::default() + } + + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + ShapeSource::parse(context, input) + } + + impl NoViewportPercentage for SpecifiedValue {} + diff --git a/servo/components/style/values/computed/basic_shape.rs b/servo/components/style/values/computed/basic_shape.rs index 7431e616665e..8ea013af0730 100644 --- a/servo/components/style/values/computed/basic_shape.rs +++ b/servo/components/style/values/computed/basic_shape.rs @@ -10,10 +10,46 @@ use cssparser::ToCss; use properties::shorthands::serialize_four_sides; use std::fmt; +use url::Url; +use values::computed::UrlExtraData; use values::computed::position::Position; use values::computed::{BorderRadiusSize, LengthOrPercentage}; -pub use values::specified::basic_shape::FillRule; +pub use values::specified::basic_shape::{FillRule, GeometryBox, ShapeBox}; + +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum ShapeSource { + Url(Url, UrlExtraData), + Shape(BasicShape, Option), + Box(T), + None, +} + +impl Default for ShapeSource { + fn default() -> Self { + ShapeSource::None + } +} + +impl ToCss for ShapeSource { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use values::LocalToCss; + match *self { + ShapeSource::Url(ref url, _) => url.to_css(dest), + ShapeSource::Shape(ref shape, Some(ref reference)) => { + try!(shape.to_css(dest)); + try!(dest.write_str(" ")); + reference.to_css(dest) + } + ShapeSource::Shape(ref shape, None) => shape.to_css(dest), + ShapeSource::Box(ref reference) => reference.to_css(dest), + ShapeSource::None => dest.write_str("none"), + + } + } +} + #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] diff --git a/servo/components/style/values/specified/basic_shape.rs b/servo/components/style/values/specified/basic_shape.rs index b50effd85ae1..ff9b88559a61 100644 --- a/servo/components/style/values/specified/basic_shape.rs +++ b/servo/components/style/values/specified/basic_shape.rs @@ -8,13 +8,111 @@ //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape use cssparser::{Parser, ToCss}; +use parser::{ParserContext, Parse}; use properties::shorthands::{parse_four_sides, serialize_four_sides}; use std::fmt; +use url::Url; use values::computed::basic_shape as computed_basic_shape; use values::computed::{Context, ToComputedValue, ComputedValueAsSpecified}; +use values::specified::UrlExtraData; use values::specified::position::Position; use values::specified::{BorderRadiusSize, LengthOrPercentage, Percentage}; +/// A shape source, for some reference box +/// +/// clip-path uses ShapeSource, +/// shape-outside uses ShapeSource +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum ShapeSource { + Url(Url, UrlExtraData), + Shape(BasicShape, Option), + Box(T), + None, +} + +impl Default for ShapeSource { + fn default() -> Self { + ShapeSource::None + } +} + +impl ToCss for ShapeSource { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use values::LocalToCss; + match *self { + ShapeSource::Url(ref url, _) => url.to_css(dest), + ShapeSource::Shape(ref shape, Some(ref reference)) => { + try!(shape.to_css(dest)); + try!(dest.write_str(" ")); + reference.to_css(dest) + } + ShapeSource::Shape(ref shape, None) => shape.to_css(dest), + ShapeSource::Box(ref reference) => reference.to_css(dest), + ShapeSource::None => dest.write_str("none"), + + } + } +} + +impl ShapeSource { + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(_) = input.try(|input| input.expect_ident_matching("none")) { + Ok(ShapeSource::None) + } else if let Ok(url) = input.try(|input| input.expect_url()) { + match UrlExtraData::make_from(context) { + Some(extra_data) => { + Ok(ShapeSource::Url(context.parse_url(&url), extra_data)) + }, + None => Err(()), + } + } else { + fn parse_component(input: &mut Parser, component: &mut Option) -> bool { + if component.is_some() { + return false; // already parsed this component + } + *component = input.try(U::parse).ok(); + component.is_some() + } + + let mut shape = None; + let mut reference = None; + loop { + if !parse_component(input, &mut shape) && + !parse_component(input, &mut reference) { + break; + } + } + match (shape, reference) { + (Some(shape), _) => Ok(ShapeSource::Shape(shape, reference)), + (None, Some(reference)) => Ok(ShapeSource::Box(reference)), + (None, None) => Err(()), + } + } + } +} + +impl ToComputedValue for ShapeSource { + type ComputedValue = computed_basic_shape::ShapeSource; + #[inline] + fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { + match *self { + ShapeSource::Url(ref url, ref data) => { + computed_basic_shape::ShapeSource::Url(url.clone(), data.clone()) + } + ShapeSource::Shape(ref shape, ref reference) => { + computed_basic_shape::ShapeSource::Shape( + shape.to_computed_value(cx), + reference.as_ref().map(|ref r| r.to_computed_value(cx))) + } + ShapeSource::Box(ref reference) => { + computed_basic_shape::ShapeSource::Box(reference.to_computed_value(cx)) + } + ShapeSource::None => computed_basic_shape::ShapeSource::None, + } + } +} + #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum BasicShape { @@ -24,8 +122,8 @@ pub enum BasicShape { Polygon(Polygon), } -impl BasicShape { - pub fn parse(input: &mut Parser) -> Result { +impl Parse for BasicShape { + fn parse(input: &mut Parser) -> Result { match_ignore_ascii_case! { try!(input.expect_function()), "inset" => { Ok(BasicShape::Inset( @@ -528,3 +626,77 @@ impl ToCss for FillRule { } } } + +/// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GeometryBox { + Fill, + Stroke, + View, + ShapeBox(ShapeBox), +} + +impl Parse for GeometryBox { + fn parse(input: &mut Parser) -> Result { + if let Ok(shape_box) = input.try(ShapeBox::parse) { + Ok(GeometryBox::ShapeBox(shape_box)) + } else { + match_ignore_ascii_case! { try!(input.expect_ident()), + "fill-box" => Ok(GeometryBox::Fill), + "stroke-box" => Ok(GeometryBox::Stroke), + "view-box" => Ok(GeometryBox::View), + _ => Err(()) + } + } + } +} + +impl ToCss for GeometryBox { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + GeometryBox::Fill => dest.write_str("fill-box"), + GeometryBox::Stroke => dest.write_str("stroke-box"), + GeometryBox::View => dest.write_str("view-box"), + GeometryBox::ShapeBox(s) => s.to_css(dest), + } + } +} + +impl ComputedValueAsSpecified for GeometryBox {} + +// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum ShapeBox { + Margin, + // https://drafts.csswg.org/css-backgrounds-3/#box + Border, + Padding, + Content, +} + +impl Parse for ShapeBox { + fn parse(input: &mut Parser) -> Result { + match_ignore_ascii_case! { try!(input.expect_ident()), + "margin-box" => Ok(ShapeBox::Margin), + "border-box" => Ok(ShapeBox::Border), + "padding-box" => Ok(ShapeBox::Padding), + "content-box" => Ok(ShapeBox::Content), + _ => Err(()) + } + } +} + +impl ToCss for ShapeBox { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + ShapeBox::Margin => dest.write_str("margin-box"), + ShapeBox::Border => dest.write_str("border-box"), + ShapeBox::Padding => dest.write_str("padding-box"), + ShapeBox::Content => dest.write_str("content-box"), + } + } +} + +impl ComputedValueAsSpecified for ShapeBox {} diff --git a/servo/ports/geckolib/binding_tools/regen.py b/servo/ports/geckolib/binding_tools/regen.py index 88c423931d83..0d8a2514aae6 100755 --- a/servo/ports/geckolib/binding_tools/regen.py +++ b/servo/ports/geckolib/binding_tools/regen.py @@ -141,7 +141,8 @@ COMPILATION_TARGETS = { "nsStyleCoord::Calc", "nsRestyleHint", "ServoElementSnapshot", "nsChangeHint", "SheetParsingMode", "nsMainThreadPtrHandle", "nsMainThreadPtrHolder", "nscolor", "nsFont", "FontFamilyList", - "FontFamilyType", "nsIAtom", "nsStyleContext" + "FontFamilyType", "nsIAtom", "nsStyleContext", "StyleClipPath", + "StyleBasicShapeType", "StyleBasicShape" ], "void_types": [ "nsINode", "nsIDocument", "nsIPrincipal", "nsIURI", diff --git a/servo/ports/geckolib/gecko_bindings/bindings.rs b/servo/ports/geckolib/gecko_bindings/bindings.rs index 1acaeb729252..3d03ab527c69 100644 --- a/servo/ports/geckolib/gecko_bindings/bindings.rs +++ b/servo/ports/geckolib/gecko_bindings/bindings.rs @@ -152,6 +152,9 @@ use structs::nsStyleContext; unsafe impl Send for nsStyleContext {} unsafe impl Sync for nsStyleContext {} impl HeapSizeOf for nsStyleContext { fn heap_size_of_children(&self) -> usize { 0 } } +use structs::StyleClipPath; +use structs::StyleBasicShapeType; +use structs::StyleBasicShape; pub type RawGeckoNode = nsINode; pub enum Element { } @@ -326,6 +329,11 @@ extern "C" { pub fn Gecko_SetStyleCoordCalcValue(unit: *mut nsStyleUnit, value: *mut nsStyleUnion, calc: CalcValue); + pub fn Gecko_CopyClipPathValueFrom(dst: *mut StyleClipPath, + src: *const StyleClipPath); + pub fn Gecko_DestroyClipPath(clip: *mut StyleClipPath); + pub fn Gecko_NewBasicShape(type_: StyleBasicShapeType) + -> *mut StyleBasicShape; pub fn Gecko_AddRefCalcArbitraryThread(aPtr: *mut Calc); pub fn Gecko_ReleaseCalcArbitraryThread(aPtr: *mut Calc); pub fn Servo_StylesheetFromUTF8Bytes(bytes: *const u8, length: u32, diff --git a/servo/ports/geckolib/gecko_bindings/structs_debug.rs b/servo/ports/geckolib/gecko_bindings/structs_debug.rs index 432fc55e86d1..d86c90fb298c 100644 --- a/servo/ports/geckolib/gecko_bindings/structs_debug.rs +++ b/servo/ports/geckolib/gecko_bindings/structs_debug.rs @@ -5452,7 +5452,7 @@ fn bindgen_test_layout_nsStyleSides() { assert_eq!(::std::mem::align_of::() , 8usize); } /** - * Class that represents a set of top-left/top-right/bottom-left/bottom-right + * Class that represents a set of top-left/top-right/bottom-right/bottom-left * nsStyleCoord pairs. This is used to hold the dimensions of the * corners of a box (for, e.g., border-radius and outline-radius). */ @@ -6375,8 +6375,7 @@ fn bindgen_test_layout_StyleAnimation() { #[repr(C)] #[derive(Debug)] pub struct StyleBasicShape { - pub mRefCnt: nsAutoRefCnt, - pub _mOwningThread: nsAutoOwningThread, + pub mRefCnt: ThreadSafeAutoRefCnt, pub mType: StyleBasicShapeType, pub mFillRule: StyleFillRule, pub mCoordinates: nsTArray, @@ -6385,7 +6384,7 @@ pub struct StyleBasicShape { } #[test] fn bindgen_test_layout_StyleBasicShape() { - assert_eq!(::std::mem::size_of::() , 128usize); + assert_eq!(::std::mem::size_of::() , 120usize); assert_eq!(::std::mem::align_of::() , 8usize); } #[repr(C)] diff --git a/servo/ports/geckolib/gecko_bindings/structs_release.rs b/servo/ports/geckolib/gecko_bindings/structs_release.rs index 30ae855567d7..755b0a02452f 100644 --- a/servo/ports/geckolib/gecko_bindings/structs_release.rs +++ b/servo/ports/geckolib/gecko_bindings/structs_release.rs @@ -5431,7 +5431,7 @@ fn bindgen_test_layout_nsStyleSides() { assert_eq!(::std::mem::align_of::() , 8usize); } /** - * Class that represents a set of top-left/top-right/bottom-left/bottom-right + * Class that represents a set of top-left/top-right/bottom-right/bottom-left * nsStyleCoord pairs. This is used to hold the dimensions of the * corners of a box (for, e.g., border-radius and outline-radius). */ @@ -6353,8 +6353,7 @@ fn bindgen_test_layout_StyleAnimation() { #[repr(C)] #[derive(Debug)] pub struct StyleBasicShape { - pub mRefCnt: nsAutoRefCnt, - pub _mOwningThread: nsAutoOwningThread, + pub mRefCnt: ThreadSafeAutoRefCnt, pub mType: StyleBasicShapeType, pub mFillRule: StyleFillRule, pub mCoordinates: nsTArray, @@ -6363,7 +6362,7 @@ pub struct StyleBasicShape { } #[test] fn bindgen_test_layout_StyleBasicShape() { - assert_eq!(::std::mem::size_of::() , 128usize); + assert_eq!(::std::mem::size_of::() , 120usize); assert_eq!(::std::mem::align_of::() , 8usize); } #[repr(C)] diff --git a/servo/ports/geckolib/gecko_bindings/sugar/ns_style_coord.rs b/servo/ports/geckolib/gecko_bindings/sugar/ns_style_coord.rs index a13f08ea37b8..74d20ff3a587 100644 --- a/servo/ports/geckolib/gecko_bindings/sugar/ns_style_coord.rs +++ b/servo/ports/geckolib/gecko_bindings/sugar/ns_style_coord.rs @@ -211,6 +211,17 @@ pub trait CoordDataMut : CoordData { *union = other.union(); } + /// Useful for initializing uninits + /// (set_value may segfault on uninits) + fn leaky_set_null(&mut self) { + use structs::nsStyleUnit::*; + unsafe { + let (unit, union) = self.values_mut(); + *unit = eStyleUnit_Null; + *union.mInt.as_mut() = 0; + } + } + #[inline(always)] fn set_value(&mut self, value: CoordDataValue) { use self::CoordDataValue::*; diff --git a/servo/ports/geckolib/string_cache/atom_macro.rs b/servo/ports/geckolib/string_cache/atom_macro.rs index 18a493963729..e7ccfca12ad5 100644 --- a/servo/ports/geckolib/string_cache/atom_macro.rs +++ b/servo/ports/geckolib/string_cache/atom_macro.rs @@ -1172,6 +1172,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_minheight: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms13minimum_scaleE"] pub static nsGkAtoms_minimum_scale: *mut nsIAtom; + #[link_name = "_ZN9nsGkAtoms9minlengthE"] + pub static nsGkAtoms_minlength: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms6minposE"] pub static nsGkAtoms_minpos: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms9minusSignE"] @@ -3838,6 +3840,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_onmozinterruptbegin: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms17onmozinterruptendE"] pub static nsGkAtoms_onmozinterruptend: *mut nsIAtom; + #[link_name = "_ZN9nsGkAtoms14ondevicechangeE"] + pub static nsGkAtoms_ondevicechange: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms12cdataTagNameE"] pub static nsGkAtoms_cdataTagName: *mut nsIAtom; #[link_name = "_ZN9nsGkAtoms14commentTagNameE"] @@ -6083,6 +6087,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_minheight: *mut nsIAtom; #[link_name = "?minimum_scale@nsGkAtoms@@2PEAVnsIAtom@@EA"] pub static nsGkAtoms_minimum_scale: *mut nsIAtom; + #[link_name = "?minlength@nsGkAtoms@@2PEAVnsIAtom@@EA"] + pub static nsGkAtoms_minlength: *mut nsIAtom; #[link_name = "?minpos@nsGkAtoms@@2PEAVnsIAtom@@EA"] pub static nsGkAtoms_minpos: *mut nsIAtom; #[link_name = "?minusSign@nsGkAtoms@@2PEAVnsIAtom@@EA"] @@ -8749,6 +8755,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_onmozinterruptbegin: *mut nsIAtom; #[link_name = "?onmozinterruptend@nsGkAtoms@@2PEAVnsIAtom@@EA"] pub static nsGkAtoms_onmozinterruptend: *mut nsIAtom; + #[link_name = "?ondevicechange@nsGkAtoms@@2PEAVnsIAtom@@EA"] + pub static nsGkAtoms_ondevicechange: *mut nsIAtom; #[link_name = "?cdataTagName@nsGkAtoms@@2PEAVnsIAtom@@EA"] pub static nsGkAtoms_cdataTagName: *mut nsIAtom; #[link_name = "?commentTagName@nsGkAtoms@@2PEAVnsIAtom@@EA"] @@ -10994,6 +11002,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_minheight: *mut nsIAtom; #[link_name = "?minimum_scale@nsGkAtoms@@2PAVnsIAtom@@A"] pub static nsGkAtoms_minimum_scale: *mut nsIAtom; + #[link_name = "?minlength@nsGkAtoms@@2PAVnsIAtom@@A"] + pub static nsGkAtoms_minlength: *mut nsIAtom; #[link_name = "?minpos@nsGkAtoms@@2PAVnsIAtom@@A"] pub static nsGkAtoms_minpos: *mut nsIAtom; #[link_name = "?minusSign@nsGkAtoms@@2PAVnsIAtom@@A"] @@ -13660,6 +13670,8 @@ pub enum nsICSSAnonBoxPseudo {} pub static nsGkAtoms_onmozinterruptbegin: *mut nsIAtom; #[link_name = "?onmozinterruptend@nsGkAtoms@@2PAVnsIAtom@@A"] pub static nsGkAtoms_onmozinterruptend: *mut nsIAtom; + #[link_name = "?ondevicechange@nsGkAtoms@@2PAVnsIAtom@@A"] + pub static nsGkAtoms_ondevicechange: *mut nsIAtom; #[link_name = "?cdataTagName@nsGkAtoms@@2PAVnsIAtom@@A"] pub static nsGkAtoms_cdataTagName: *mut nsIAtom; #[link_name = "?commentTagName@nsGkAtoms@@2PAVnsIAtom@@A"] @@ -15332,6 +15344,7 @@ macro_rules! atom { ("min") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_min as *mut _) }; ("minheight") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minheight as *mut _) }; ("minimum-scale") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minimum_scale as *mut _) }; +("minlength") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minlength as *mut _) }; ("minpos") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minpos as *mut _) }; ("minus-sign") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minusSign as *mut _) }; ("minwidth") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_minwidth as *mut _) }; @@ -16665,6 +16678,7 @@ macro_rules! atom { ("ondevicelight") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_ondevicelight as *mut _) }; ("onmozinterruptbegin") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_onmozinterruptbegin as *mut _) }; ("onmozinterruptend") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_onmozinterruptend as *mut _) }; +("ondevicechange") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_ondevicechange as *mut _) }; ("#cdata-section") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_cdataTagName as *mut _) }; ("#comment") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_commentTagName as *mut _) }; ("#document") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::nsGkAtoms_documentNodeName as *mut _) }; diff --git a/servo/tests/unit/style/parsing/basic_shape.rs b/servo/tests/unit/style/parsing/basic_shape.rs index b3702dfc8cd8..62755582b377 100644 --- a/servo/tests/unit/style/parsing/basic_shape.rs +++ b/servo/tests/unit/style/parsing/basic_shape.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use parsing::parse; +use style::parser::Parse; use style::values::specified::basic_shape::*; // Ensure that basic-shape sub-functions parse as both basic shapes