gecko-dev/servo/components/style/gecko_values.rs
Manish Goregaokar 5eb45ceefb servo: Merge #12521 - Introduce safer layer of sugar for nsStyleUnion (from Manishearth:safer-coord); r=bholley
This routes (almost) all access to nsStyleUnion through a largely safe interface, CoordData.

It also introduces a corresponding Rust enum, CoordDataValue, which can be used when interacting with CoordData

LLVM should optimize the costs away in release mode. eddyb tested this a bit, and LLVM has no trouble threading matches together with inlining -- so all of the matches using enums here will have the same generated code as the old matches on the units.

Some unresolved questions:

Should I provide convenience methods like `set_coord`, `set_auto`, etc on CoordData? `.set_enum(CoordDataValue::Something)` should optimize down to the same thing, but the convenience methods look cleaner and won't load the optimizer as much.

Also, we're converting immutable references to mutable ones, which can be used to cause unsafety using some acrobatics. Perhaps a trait-based approach is better?
The issue is that in some places we only have a `&CoordData` (eg copy_from), but CoordData internally is `*mut`. It would be nice if CoordData could parametrize over its mutability, but Rust doesn't let us do that.

The alternate approach is to make CoordData a trait (also CoordDataMut). The trait requires you to implement `get_union()` and `get_unit()`, and gives you the rest of the functions for free. `nsStyleCoord` would directly implement both traits. `nsStyleSides` would have `data_at(idx)` and `data_at_mut(idx)` methods which return a struct `SidesData` containing a reference to the Sides and the index, which itself implements the `CoordData` and `CoordDataMut` trait (we need two traits here because there will have to be two `SidesData` structs).

I decided not to implement the traits approach first since it's pretty trivial to change this code to use traits, and the current design is more straightforward.

Thoughts?

r? @bholley

cc @emilio @heycam

Source-Repo: https://github.com/servo/servo
Source-Revision: 4a77cbdbb2fc1a4f163171356a7f5340e1237317
2016-07-21 03:05:56 -05:00

153 lines
5.7 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unsafe_code)]
use app_units::Au;
use cssparser::RGBA;
use gecko_bindings::structs::nsStyleCoord;
use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
use std::cmp::max;
use values::computed::Angle;
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
pub trait StyleCoordHelpers {
fn set<T: GeckoStyleCoordConvertible>(&mut self, val: T);
}
impl StyleCoordHelpers for nsStyleCoord {
#[inline]
fn set<T: GeckoStyleCoordConvertible>(&mut self, val: T) {
val.to_gecko_style_coord(self);
}
}
pub trait GeckoStyleCoordConvertible : Sized {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T);
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self>;
}
impl GeckoStyleCoordConvertible for LengthOrPercentage {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentage::Length(au) => CoordDataValue::Coord(au.0),
LengthOrPercentage::Percentage(p) => CoordDataValue::Percent(p),
LengthOrPercentage::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
coord.set_value(value);
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentage::Length(Au(coord))),
CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(p)),
CoordDataValue::Calc(calc) => Some(LengthOrPercentage::Calc(calc.into())),
_ => None,
}
}
}
impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentageOrAuto::Length(au) => CoordDataValue::Coord(au.0),
LengthOrPercentageOrAuto::Percentage(p) => CoordDataValue::Percent(p),
LengthOrPercentageOrAuto::Auto => CoordDataValue::Auto,
LengthOrPercentageOrAuto::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
coord.set_value(value);
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrAuto::Length(Au(coord))),
CoordDataValue::Percent(p) => Some(LengthOrPercentageOrAuto::Percentage(p)),
CoordDataValue::Auto => Some(LengthOrPercentageOrAuto::Auto),
CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrAuto::Calc(calc.into())),
_ => None,
}
}
}
impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentageOrNone::Length(au) => CoordDataValue::Coord(au.0),
LengthOrPercentageOrNone::Percentage(p) => CoordDataValue::Percent(p),
LengthOrPercentageOrNone::None => CoordDataValue::None,
LengthOrPercentageOrNone::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
coord.set_value(value);
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrNone::Length(Au(coord))),
CoordDataValue::Percent(p) => Some(LengthOrPercentageOrNone::Percentage(p)),
CoordDataValue::None => Some(LengthOrPercentageOrNone::None),
CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrNone::Calc(calc.into())),
_ => None,
}
}
}
impl<T: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for Option<T> {
fn to_gecko_style_coord<U: CoordDataMut>(&self, coord: &mut U) {
if let Some(ref me) = *self {
me.to_gecko_style_coord(coord);
} else {
coord.set_value(CoordDataValue::None);
}
}
fn from_gecko_style_coord<U: CoordData>(coord: &U) -> Option<Self> {
Some(T::from_gecko_style_coord(coord))
}
}
impl GeckoStyleCoordConvertible for Angle {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
coord.set_value(CoordDataValue::Radian(self.radians()))
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
if let CoordDataValue::Radian(r) = coord.as_value() {
Some(Angle::from_radians(r))
// XXXManishearth should this handle Degree too?
} else {
None
}
}
}
pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
(((rgba.alpha * 255.0).round() as u32) << 24) |
(((rgba.blue * 255.0).round() as u32) << 16) |
(((rgba.green * 255.0).round() as u32) << 8) |
((rgba.red * 255.0).round() as u32)
}
pub fn convert_nscolor_to_rgba(color: u32) -> RGBA {
RGBA {
red: ((color & 0xff) as f32) / 255.0,
green: (((color >> 8) & 0xff) as f32) / 255.0,
blue: (((color >> 16) & 0xff) as f32) / 255.0,
alpha: (((color >> 24) & 0xff) as f32) / 255.0,
}
}
#[inline]
pub fn round_border_to_device_pixels(width: Au, au_per_device_px: Au) -> Au {
// Round width down to the nearest device pixel, but any non-zero value that
// would round down to zero is clamped to 1 device pixel. Used for storing
// computed values of border-*-width and outline-width.
if width == Au(0) {
Au(0)
} else {
max(au_per_device_px, Au(width.0 / au_per_device_px.0 * au_per_device_px.0))
}
}