Refactor image code and implement gecko glue for border-image-source

This commit is contained in:
Nazım Can Altınova 2016-11-02 22:05:01 +03:00
parent da27b61352
commit 132b36835b
2 changed files with 214 additions and 193 deletions

View file

@ -53,6 +53,7 @@ use std::ptr;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::cmp; use std::cmp;
use values::computed::{Image, Gradient};
pub mod style_structs { pub mod style_structs {
% for style_struct in data.style_structs: % for style_struct in data.style_structs:
@ -639,7 +640,7 @@ fn static_assert() {
["border-{0}-radius".format(x.ident.replace("_", "-")) ["border-{0}-radius".format(x.ident.replace("_", "-"))
for x in CORNERS]) %> for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Border" <%self:impl_trait style_struct_name="Border"
skip_longhands="${skip_border_longhands}" skip_longhands="${skip_border_longhands} border-image-source"
skip_additionals="*"> skip_additionals="*">
% for side in SIDES: % for side in SIDES:
@ -663,6 +664,26 @@ fn static_assert() {
corner.y_index, corner.y_index,
need_clone=True) %> need_clone=True) %>
% endfor % 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);
}
}
</%self:impl_trait> </%self:impl_trait>
<% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %> <% 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, pub fn set_${shorthand}_image(&mut self,
images: longhands::${shorthand}_image::computed_value::T, images: longhands::${shorthand}_image::computed_value::T,
cacheable: &mut bool) { cacheable: &mut bool) {
use gecko_bindings::structs::nsStyleImage;
use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; 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 { unsafe {
// Prevent leaking of the last elements we did set // Prevent leaking of the last elements we did set
@ -1421,36 +1277,12 @@ fn static_assert() {
.mLayers.iter_mut()) { .mLayers.iter_mut()) {
% if shorthand == "background": % if shorthand == "background":
if let Some(image) = image.0 { if let Some(image) = image.0 {
match image { set_image(image, &mut geckoimage.mImage, true, cacheable)
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;
}
}
} }
% else: % else:
use properties::longhands::mask_image::single_value::computed_value::T; use properties::longhands::mask_image::single_value::computed_value::T;
match image { match image {
T::Image(image) => match image { T::Image(image) => set_image(image, &mut geckoimage.mImage, false, cacheable),
Image::Gradient(gradient) => {
set_gradient(gradient, &mut geckoimage.mImage)
}
_ => () // we need to support image values
},
_ => () // we need to support url valeus _ => () // we need to support url valeus
} }
% endif % endif
@ -1485,6 +1317,195 @@ fn static_assert() {
} }
</%def> </%def>
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 // 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 // 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. // add support for parsing these lists in servo and pushing to nsTArray's.

View file

@ -66,7 +66,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
animatable=False)} animatable=False)}
// https://drafts.csswg.org/css-backgrounds-3/#border-image-source // 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 cssparser::ToCss;
use std::fmt; use std::fmt;
use values::LocalToCss; use values::LocalToCss;