From 132b36835bf60e617232f42e9066411156c6e2be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 2 Nov 2016 22:05:01 +0300 Subject: [PATCH] Refactor image code and implement gecko glue for border-image-source --- components/style/properties/gecko.mako.rs | 405 +++++++++--------- .../style/properties/longhand/border.mako.rs | 2 +- 2 files changed, 214 insertions(+), 193 deletions(-) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 7c2aa42b264..351c8790eb3 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -53,6 +53,7 @@ use std::ptr; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::sync::Arc; use std::cmp; +use values::computed::{Image, Gradient}; pub mod style_structs { % for style_struct in data.style_structs: @@ -639,7 +640,7 @@ fn static_assert() { ["border-{0}-radius".format(x.ident.replace("_", "-")) for x in CORNERS]) %> <%self:impl_trait style_struct_name="Border" - skip_longhands="${skip_border_longhands}" + skip_longhands="${skip_border_longhands} border-image-source" skip_additionals="*"> % for side in SIDES: @@ -663,6 +664,26 @@ fn static_assert() { corner.y_index, need_clone=True) %> % endfor + + pub fn set_border_image_source(&mut self, v: longhands::border_image_source::computed_value::T) { + unsafe { + // Prevent leaking of the last elements we did set + Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); + } + + if let Some(image) = v.0 { + // TODO: We need to make border-image-source match with background-image + // until then we are setting with_url to false + set_image(image, &mut self.gecko.mBorderImageSource, false, &mut false) + } + } + + pub fn copy_border_image_source_from(&mut self, other: &Self) { + unsafe { + Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource, + &other.gecko.mBorderImageSource); + } + } <% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %> @@ -1238,172 +1259,7 @@ fn static_assert() { pub fn set_${shorthand}_image(&mut self, images: longhands::${shorthand}_image::computed_value::T, cacheable: &mut bool) { - use gecko_bindings::structs::nsStyleImage; use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; - use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SHAPE_CIRCULAR}; - use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER}; - use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER}; - use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; - use gecko_bindings::structs::nsStyleCoord; - use values::computed::{Image, Gradient, GradientKind, GradientShape, LengthOrKeyword}; - use values::computed::LengthOrPercentageOrKeyword; - use values::specified::AngleOrCorner; - use values::specified::{HorizontalDirection, SizeKeyword, VerticalDirection}; - use cssparser::Color as CSSColor; - - fn set_gradient(gradient: Gradient, geckoimage: &mut nsStyleImage) { - let stop_count = gradient.stops.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.gradient_kind { - GradientKind::Linear(angle_or_corner) => { - let gecko_gradient = unsafe { - Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, - NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, - gradient.repeating, - /* legacy_syntax = */ false, - stop_count as u32) - }; - - match angle_or_corner { - AngleOrCorner::Angle(angle) => { - unsafe { - (*gecko_gradient).mAngle.set(angle); - (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); - (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); - } - }, - AngleOrCorner::Corner(horiz, vert) => { - let percent_x = match horiz { - HorizontalDirection::Left => 0.0, - HorizontalDirection::Right => 1.0, - }; - let percent_y = match vert { - VerticalDirection::Top => 0.0, - VerticalDirection::Bottom => 1.0, - }; - - unsafe { - (*gecko_gradient).mAngle.set_value(CoordDataValue::None); - (*gecko_gradient).mBgPosX - .set_value(CoordDataValue::Percent(percent_x)); - (*gecko_gradient).mBgPosY - .set_value(CoordDataValue::Percent(percent_y)); - } - } - } - gecko_gradient - }, - GradientKind::Radial(shape, position) => { - let (gecko_shape, gecko_size) = match shape { - GradientShape::Circle(ref length) => { - let size = match *length { - LengthOrKeyword::Keyword(keyword) => { - match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - } - }, - _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, - }; - (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) - }, - GradientShape::Ellipse(ref length) => { - let size = match *length { - LengthOrPercentageOrKeyword::Keyword(keyword) => { - match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - } - }, - _ => 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, - /* legacy_syntax = */ false, - stop_count as u32) - }; - - // Clear mAngle and mBgPos fields - unsafe { - (*gecko_gradient).mAngle.set_value(CoordDataValue::None); - (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); - (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); - } - - // Setting radius values depending shape - match shape { - GradientShape::Circle(length) => { - if let LengthOrKeyword::Length(len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(len.0)); - (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(len.0)); - } - } - }, - GradientShape::Ellipse(length) => { - if let LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set(first_len); - (*gecko_gradient).mRadiusY.set(second_len); - } - } - }, - } - unsafe { - (*gecko_gradient).mBgPosX.set(position.horizontal); - (*gecko_gradient).mBgPosY.set(position.vertical); - } - - gecko_gradient - }, - }; - - let mut coord: nsStyleCoord = nsStyleCoord::null(); - for (index, stop) in gradient.stops.iter().enumerate() { - // NB: stops are guaranteed to be none in the gecko side by - // default. - coord.set(stop.position); - let color = match stop.color { - CSSColor::CurrentColor => { - // TODO(emilio): gecko just stores an nscolor, - // and it doesn't seem to support currentColor - // as value in a gradient. - // - // Double-check it and either remove - // currentColor for servo or see how gecko - // handles this. - 0 - }, - CSSColor::RGBA(ref rgba) => convert_rgba_to_nscolor(rgba), - }; - - let mut stop = unsafe { - &mut (*gecko_gradient).mStops[index] - }; - - stop.mColor = color; - stop.mIsInterpolationHint = false; - stop.mLocation.copy_from(&coord); - } - - unsafe { - Gecko_SetGradientImageValue(geckoimage, gecko_gradient); - } - } unsafe { // Prevent leaking of the last elements we did set @@ -1421,36 +1277,12 @@ fn static_assert() { .mLayers.iter_mut()) { % if shorthand == "background": if let Some(image) = image.0 { - match image { - Image::Gradient(gradient) => { - set_gradient(gradient, &mut geckoimage.mImage) - }, - Image::Url(ref url, ref extra_data) => { - unsafe { - Gecko_SetUrlImageValue(&mut geckoimage.mImage, - url.as_str().as_ptr(), - url.as_str().len() as u32, - extra_data.base.get(), - extra_data.referrer.get(), - extra_data.principal.get()); - } - // We unfortunately must make any url() value uncacheable, since - // the applicable declarations cache is not per document, but - // global, and the imgRequestProxy objects we store in the style - // structs don't like to be tracked by more than one document. - *cacheable = false; - } - } + set_image(image, &mut geckoimage.mImage, true, cacheable) } % else: use properties::longhands::mask_image::single_value::computed_value::T; match image { - T::Image(image) => match image { - Image::Gradient(gradient) => { - set_gradient(gradient, &mut geckoimage.mImage) - } - _ => () // we need to support image values - }, + T::Image(image) => set_image(image, &mut geckoimage.mImage, false, cacheable), _ => () // we need to support url valeus } % endif @@ -1485,6 +1317,195 @@ fn static_assert() { } +fn set_image(image: Image, mut geckoimage: &mut structs::nsStyleImage, with_url: bool, cacheable: &mut bool) { + match image { + Image::Gradient(gradient) => { + set_gradient(gradient, &mut geckoimage) + }, + Image::Url(ref url, ref extra_data) if with_url => { + unsafe { + Gecko_SetUrlImageValue(geckoimage, + url.as_str().as_ptr(), + url.as_str().len() as u32, + extra_data.base.get(), + extra_data.referrer.get(), + extra_data.principal.get()); + } + // We unfortunately must make any url() value uncacheable, since + // the applicable declarations cache is not per document, but + // global, and the imgRequestProxy objects we store in the style + // structs don't like to be tracked by more than one document. + *cacheable = false; + }, + _ => (), + } +} + +fn set_gradient(gradient: Gradient, geckoimage: &mut structs::nsStyleImage) { + use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SHAPE_CIRCULAR}; + use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER}; + use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER}; + use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; + use gecko_bindings::structs::nsStyleCoord; + use values::computed::{GradientKind, GradientShape, LengthOrKeyword}; + use values::computed::LengthOrPercentageOrKeyword; + use values::specified::AngleOrCorner; + use values::specified::{HorizontalDirection, SizeKeyword, VerticalDirection}; + use cssparser::Color as CSSColor; + + let stop_count = gradient.stops.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.gradient_kind { + GradientKind::Linear(angle_or_corner) => { + let gecko_gradient = unsafe { + Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, + NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, + gradient.repeating, + /* legacy_syntax = */ false, + stop_count as u32) + }; + + match angle_or_corner { + AngleOrCorner::Angle(angle) => { + unsafe { + (*gecko_gradient).mAngle.set(angle); + (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); + (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); + } + }, + AngleOrCorner::Corner(horiz, vert) => { + let percent_x = match horiz { + HorizontalDirection::Left => 0.0, + HorizontalDirection::Right => 1.0, + }; + let percent_y = match vert { + VerticalDirection::Top => 0.0, + VerticalDirection::Bottom => 1.0, + }; + + unsafe { + (*gecko_gradient).mAngle.set_value(CoordDataValue::None); + (*gecko_gradient).mBgPosX + .set_value(CoordDataValue::Percent(percent_x)); + (*gecko_gradient).mBgPosY + .set_value(CoordDataValue::Percent(percent_y)); + } + } + } + gecko_gradient + }, + GradientKind::Radial(shape, position) => { + let (gecko_shape, gecko_size) = match shape { + GradientShape::Circle(ref length) => { + let size = match *length { + LengthOrKeyword::Keyword(keyword) => { + match keyword { + SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, + SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, + SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + } + }, + _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, + }; + (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) + }, + GradientShape::Ellipse(ref length) => { + let size = match *length { + LengthOrPercentageOrKeyword::Keyword(keyword) => { + match keyword { + SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, + SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, + SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + } + }, + _ => 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, + /* legacy_syntax = */ false, + stop_count as u32) + }; + + // Clear mAngle and mBgPos fields + unsafe { + (*gecko_gradient).mAngle.set_value(CoordDataValue::None); + (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); + (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); + } + + // Setting radius values depending shape + match shape { + GradientShape::Circle(length) => { + if let LengthOrKeyword::Length(len) = length { + unsafe { + (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(len.0)); + (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(len.0)); + } + } + }, + GradientShape::Ellipse(length) => { + if let LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) = length { + unsafe { + (*gecko_gradient).mRadiusX.set(first_len); + (*gecko_gradient).mRadiusY.set(second_len); + } + } + }, + } + unsafe { + (*gecko_gradient).mBgPosX.set(position.horizontal); + (*gecko_gradient).mBgPosY.set(position.vertical); + } + + gecko_gradient + }, + }; + + let mut coord: nsStyleCoord = nsStyleCoord::null(); + for (index, stop) in gradient.stops.iter().enumerate() { + // NB: stops are guaranteed to be none in the gecko side by + // default. + coord.set(stop.position); + let color = match stop.color { + CSSColor::CurrentColor => { + // TODO(emilio): gecko just stores an nscolor, + // and it doesn't seem to support currentColor + // as value in a gradient. + // + // Double-check it and either remove + // currentColor for servo or see how gecko + // handles this. + 0 + }, + CSSColor::RGBA(ref rgba) => convert_rgba_to_nscolor(rgba), + }; + + let mut stop = unsafe { + &mut (*gecko_gradient).mStops[index] + }; + + stop.mColor = color; + stop.mIsInterpolationHint = false; + stop.mLocation.copy_from(&coord); + } + + unsafe { + Gecko_SetGradientImageValue(geckoimage, gecko_gradient); + } +} + // TODO: Gecko accepts lists in most background-related properties. We just use // the first element (which is the common case), but at some point we want to // add support for parsing these lists in servo and pushing to nsTArray's. diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 9c1761ec863..c9f62a87f79 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -66,7 +66,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box", animatable=False)} // https://drafts.csswg.org/css-backgrounds-3/#border-image-source -<%helpers:longhand name="border-image-source" products="none" animatable="False"> +<%helpers:longhand name="border-image-source" products="gecko" animatable="False"> use cssparser::ToCss; use std::fmt; use values::LocalToCss;