Auto merge of #23353 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

See each individual commit for details.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23353)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-05-10 21:20:50 -04:00 committed by GitHub
commit 887f43b65b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 698 additions and 334 deletions

View file

@ -21,7 +21,7 @@
//! //!
//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883 //! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
// The semantics of `Arc` are alread documented in the Rust docs, so we don't // The semantics of `Arc` are already documented in the Rust docs, so we don't
// duplicate those here. // duplicate those here.
#![allow(missing_docs)] #![allow(missing_docs)]
@ -86,10 +86,14 @@ const STATIC_REFCOUNT: usize = usize::MAX;
/// See the documentation for [`Arc`] in the standard library. Unlike the /// See the documentation for [`Arc`] in the standard library. Unlike the
/// standard library `Arc`, this `Arc` does not support weak reference counting. /// standard library `Arc`, this `Arc` does not support weak reference counting.
/// ///
/// See the discussion in https://github.com/rust-lang/rust/pull/60594 for the
/// usage of PhantomData.
///
/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html /// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
#[repr(C)] #[repr(C)]
pub struct Arc<T: ?Sized> { pub struct Arc<T: ?Sized> {
p: ptr::NonNull<ArcInner<T>>, p: ptr::NonNull<ArcInner<T>>,
phantom: PhantomData<T>,
} }
/// An `Arc` that is known to be uniquely owned /// An `Arc` that is known to be uniquely owned
@ -164,11 +168,12 @@ impl<T> Arc<T> {
pub fn new(data: T) -> Self { pub fn new(data: T) -> Self {
let x = Box::new(ArcInner { let x = Box::new(ArcInner {
count: atomic::AtomicUsize::new(1), count: atomic::AtomicUsize::new(1),
data: data, data,
}); });
unsafe { unsafe {
Arc { Arc {
p: ptr::NonNull::new_unchecked(Box::into_raw(x)), p: ptr::NonNull::new_unchecked(Box::into_raw(x)),
phantom: PhantomData,
} }
} }
} }
@ -198,6 +203,7 @@ impl<T> Arc<T> {
let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data)); let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
Arc { Arc {
p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>), p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),
phantom: PhantomData,
} }
} }
@ -224,6 +230,7 @@ impl<T> Arc<T> {
Arc { Arc {
p: ptr::NonNull::new_unchecked(ptr), p: ptr::NonNull::new_unchecked(ptr),
phantom: PhantomData,
} }
} }
@ -301,6 +308,9 @@ impl<T: ?Sized> Arc<T> {
impl<T: ?Sized> Clone for Arc<T> { impl<T: ?Sized> Clone for Arc<T> {
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
// NOTE(emilio): If you change anything here, make sure that the
// implementation in layout/style/ServoStyleConstsInlines.h matches!
//
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values. // `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) != STATIC_REFCOUNT { if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {
@ -334,6 +344,7 @@ impl<T: ?Sized> Clone for Arc<T> {
unsafe { unsafe {
Arc { Arc {
p: ptr::NonNull::new_unchecked(self.ptr()), p: ptr::NonNull::new_unchecked(self.ptr()),
phantom: PhantomData,
} }
} }
} }
@ -408,6 +419,9 @@ impl<T: ?Sized> Arc<T> {
impl<T: ?Sized> Drop for Arc<T> { impl<T: ?Sized> Drop for Arc<T> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
// NOTE(emilio): If you change anything here, make sure that the
// implementation in layout/style/ServoStyleConstsInlines.h matches!
//
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values. // `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) == STATIC_REFCOUNT { if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
@ -563,6 +577,7 @@ impl<T: Serialize> Serialize for Arc<T> {
/// Structure to allow Arc-managing some fixed-sized data and a variably-sized /// Structure to allow Arc-managing some fixed-sized data and a variably-sized
/// slice in a single allocation. /// slice in a single allocation.
#[derive(Debug, Eq, PartialEq, PartialOrd)] #[derive(Debug, Eq, PartialEq, PartialOrd)]
#[repr(C)]
pub struct HeaderSlice<H, T: ?Sized> { pub struct HeaderSlice<H, T: ?Sized> {
/// The fixed-sized data. /// The fixed-sized data.
pub header: H, pub header: H,
@ -594,7 +609,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
F: FnOnce(Layout) -> *mut u8, F: FnOnce(Layout) -> *mut u8,
I: Iterator<Item = T> + ExactSizeIterator, I: Iterator<Item = T> + ExactSizeIterator,
{ {
use std::mem::size_of; use std::mem::{align_of, size_of};
assert_ne!(size_of::<T>(), 0, "Need to think about ZST"); assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
// Compute the required size for the allocation. // Compute the required size for the allocation.
@ -602,7 +617,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
let size = { let size = {
// First, determine the alignment of a hypothetical pointer to a // First, determine the alignment of a hypothetical pointer to a
// HeaderSlice. // HeaderSlice.
let fake_slice_ptr_align: usize = mem::align_of::<ArcInner<HeaderSlice<H, [T; 1]>>>(); let fake_slice_ptr_align: usize = mem::align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
// Next, synthesize a totally garbage (but properly aligned) pointer // Next, synthesize a totally garbage (but properly aligned) pointer
// to a sequence of T. // to a sequence of T.
@ -659,6 +674,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
}; };
ptr::write(&mut ((*ptr).count), count); ptr::write(&mut ((*ptr).count), count);
ptr::write(&mut ((*ptr).data.header), header); ptr::write(&mut ((*ptr).data.header), header);
if num_items != 0 {
let mut current: *mut T = &mut (*ptr).data.slice[0]; let mut current: *mut T = &mut (*ptr).data.slice[0];
for _ in 0..num_items { for _ in 0..num_items {
ptr::write( ptr::write(
@ -669,13 +685,17 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
); );
current = current.offset(1); current = current.offset(1);
} }
// We should have consumed the buffer exactly, maybe accounting
// for some padding from the alignment.
debug_assert!(
(buffer.offset(size as isize) as usize - current as *mut u8 as usize) <
align_of::<Self>()
);
}
assert!( assert!(
items.next().is_none(), items.next().is_none(),
"ExactSizeIterator under-reported length" "ExactSizeIterator under-reported length"
); );
// We should have consumed the buffer exactly.
debug_assert_eq!(current as *mut u8, buffer.offset(size as isize));
} }
// Return the fat Arc. // Return the fat Arc.
@ -687,6 +707,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
unsafe { unsafe {
Arc { Arc {
p: ptr::NonNull::new_unchecked(ptr), p: ptr::NonNull::new_unchecked(ptr),
phantom: PhantomData,
} }
} }
} }
@ -732,6 +753,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
/// Header data with an inline length. Consumers that use HeaderWithLength as the /// Header data with an inline length. Consumers that use HeaderWithLength as the
/// Header type in HeaderSlice can take advantage of ThinArc. /// Header type in HeaderSlice can take advantage of ThinArc.
#[derive(Debug, Eq, PartialEq, PartialOrd)] #[derive(Debug, Eq, PartialEq, PartialOrd)]
#[repr(C)]
pub struct HeaderWithLength<H> { pub struct HeaderWithLength<H> {
/// The fixed-sized data. /// The fixed-sized data.
pub header: H, pub header: H,
@ -763,11 +785,20 @@ type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;
/// have a thin pointer instead, perhaps for FFI compatibility /// have a thin pointer instead, perhaps for FFI compatibility
/// or space efficiency. /// or space efficiency.
/// ///
/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
///
/// `ThinArc` solves this by storing the length in the allocation itself, /// `ThinArc` solves this by storing the length in the allocation itself,
/// via `HeaderSliceWithLength`. /// via `HeaderSliceWithLength`.
#[repr(C)] #[repr(C)]
pub struct ThinArc<H, T> { pub struct ThinArc<H, T> {
ptr: *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>, ptr: ptr::NonNull<ArcInner<HeaderSliceWithLength<H, [T; 0]>>>,
phantom: PhantomData<(H, T)>,
}
impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArc<H, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.deref(), f)
}
} }
unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {} unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
@ -777,7 +808,7 @@ unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
// //
// See the comment around the analogous operation in from_header_and_iter. // See the comment around the analogous operation in from_header_and_iter.
fn thin_to_thick<H, T>( fn thin_to_thick<H, T>(
thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>, thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
) -> *mut ArcInner<HeaderSliceWithLength<H, [T]>> { ) -> *mut ArcInner<HeaderSliceWithLength<H, [T]>> {
let len = unsafe { (*thin).data.header.length }; let len = unsafe { (*thin).data.header.length };
let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) }; let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) };
@ -796,7 +827,8 @@ impl<H, T> ThinArc<H, T> {
// Synthesize transient Arc, which never touches the refcount of the ArcInner. // Synthesize transient Arc, which never touches the refcount of the ArcInner.
let transient = unsafe { let transient = unsafe {
NoDrop::new(Arc { NoDrop::new(Arc {
p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr)), p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())),
phantom: PhantomData,
}) })
}; };
@ -841,8 +873,12 @@ impl<H, T> ThinArc<H, T> {
} }
/// Returns the address on the heap of the ThinArc itself -- not the T /// Returns the address on the heap of the ThinArc itself -- not the T
/// within it -- for memory reporting. /// within it -- for memory reporting, and bindings.
/// #[inline]
pub fn ptr(&self) -> *const c_void {
self.ptr.as_ptr() as *const ArcInner<T> as *const c_void
}
/// If this is a static ThinArc, this returns null. /// If this is a static ThinArc, this returns null.
#[inline] #[inline]
pub fn heap_ptr(&self) -> *const c_void { pub fn heap_ptr(&self) -> *const c_void {
@ -851,7 +887,7 @@ impl<H, T> ThinArc<H, T> {
if is_static { if is_static {
ptr::null() ptr::null()
} else { } else {
self.ptr as *const ArcInner<T> as *const c_void self.ptr()
} }
} }
} }
@ -861,7 +897,7 @@ impl<H, T> Deref for ThinArc<H, T> {
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
unsafe { &(*thin_to_thick(self.ptr)).data } unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data }
} }
} }
@ -875,7 +911,10 @@ impl<H, T> Clone for ThinArc<H, T> {
impl<H, T> Drop for ThinArc<H, T> { impl<H, T> Drop for ThinArc<H, T> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
let _ = Arc::from_thin(ThinArc { ptr: self.ptr }); let _ = Arc::from_thin(ThinArc {
ptr: self.ptr,
phantom: PhantomData,
});
} }
} }
@ -893,7 +932,12 @@ impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
mem::forget(a); mem::forget(a);
let thin_ptr = fat_ptr as *mut [usize] as *mut usize; let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
ThinArc { ThinArc {
ptr: thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>, ptr: unsafe {
ptr::NonNull::new_unchecked(
thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
)
},
phantom: PhantomData,
} }
} }
@ -901,11 +945,12 @@ impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
/// is not modified. /// is not modified.
#[inline] #[inline]
pub fn from_thin(a: ThinArc<H, T>) -> Self { pub fn from_thin(a: ThinArc<H, T>) -> Self {
let ptr = thin_to_thick(a.ptr); let ptr = thin_to_thick(a.ptr.as_ptr());
mem::forget(a); mem::forget(a);
unsafe { unsafe {
Arc { Arc {
p: ptr::NonNull::new_unchecked(ptr), p: ptr::NonNull::new_unchecked(ptr),
phantom: PhantomData,
} }
} }
} }
@ -1305,6 +1350,34 @@ mod tests {
} }
} }
#[test]
fn empty_thin() {
let header = HeaderWithLength::new(100u32, 0);
let x = Arc::from_header_and_iter(header, std::iter::empty::<i32>());
let y = Arc::into_thin(x.clone());
assert_eq!(y.header.header, 100);
assert!(y.slice.is_empty());
assert_eq!(x.header.header, 100);
assert!(x.slice.is_empty());
}
#[test]
fn thin_assert_padding() {
#[derive(Clone, Default)]
#[repr(C)]
struct Padded {
i: u16,
}
// The header will have more alignment than `Padded`
let header = HeaderWithLength::new(0i32, 2);
let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];
let a = ThinArc::from_header_and_iter(header, items.into_iter());
assert_eq!(a.slice.len(), 2);
assert_eq!(a.slice[0].i, 0xdead);
assert_eq!(a.slice[1].i, 0xbeef);
}
#[test] #[test]
fn slices_and_thin() { fn slices_and_thin() {
let mut canary = atomic::AtomicUsize::new(0); let mut canary = atomic::AtomicUsize::new(0);

View file

@ -532,28 +532,15 @@ impl nsStyleImage {
pub mod basic_shape { pub mod basic_shape {
//! Conversions from and to CSS shape representations. //! Conversions from and to CSS shape representations.
use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::gecko_bindings::structs::nsStyleCoord;
use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType};
use crate::gecko_bindings::structs::{ use crate::gecko_bindings::structs::{
StyleGeometryBox, StyleShapeSource, StyleShapeSourceType, StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
}; };
use crate::gecko_bindings::sugar::refptr::RefPtr; use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::values::computed::basic_shape::{ use crate::values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape};
BasicShape, ClippingShape, FloatAreaShape, ShapeRadius,
};
use crate::values::computed::length::LengthPercentage;
use crate::values::computed::motion::OffsetPath; use crate::values::computed::motion::OffsetPath;
use crate::values::computed::url::ComputedUrl; use crate::values::computed::url::ComputedUrl;
use crate::values::generics::basic_shape::{ use crate::values::generics::basic_shape::{GeometryBox, Path, ShapeBox, ShapeSource};
BasicShape as GenericBasicShape, InsetRect, Polygon,
};
use crate::values::generics::basic_shape::{Circle, Ellipse, Path, PolygonCoord};
use crate::values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
use crate::values::generics::rect::Rect;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use std::borrow::Borrow;
impl StyleShapeSource { impl StyleShapeSource {
/// Convert StyleShapeSource to ShapeSource except URL and Image /// Convert StyleShapeSource to ShapeSource except URL and Image
@ -569,7 +556,7 @@ pub mod basic_shape {
StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())), StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())),
StyleShapeSourceType::Shape => { StyleShapeSourceType::Shape => {
let other_shape = unsafe { &*self.__bindgen_anon_1.mBasicShape.as_ref().mPtr }; let other_shape = unsafe { &*self.__bindgen_anon_1.mBasicShape.as_ref().mPtr };
let shape = other_shape.into(); let shape = Box::new(other_shape.clone());
let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox { let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox {
None None
} else { } else {
@ -588,12 +575,10 @@ pub mod basic_shape {
/// Generate a SVGPathData from StyleShapeSource if possible. /// Generate a SVGPathData from StyleShapeSource if possible.
fn to_svg_path(&self) -> Option<SVGPathData> { fn to_svg_path(&self) -> Option<SVGPathData> {
use crate::values::specified::svg_path::PathCommand;
match self.mType { match self.mType {
StyleShapeSourceType::Path => { StyleShapeSourceType::Path => {
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
let result: Vec<PathCommand> = gecko_path.mPath.iter().cloned().collect(); Some(SVGPathData(gecko_path.mPath.clone()))
Some(SVGPathData::new(result.into_boxed_slice()))
}, },
_ => None, _ => None,
} }
@ -653,67 +638,6 @@ pub mod basic_shape {
} }
} }
impl<'a> From<&'a StyleBasicShape> for BasicShape {
fn from(other: &'a StyleBasicShape) -> Self {
match other.mType {
StyleBasicShapeType::Inset => {
let t = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[0]);
let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
let round = other.mRadius;
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, round })
},
StyleBasicShapeType::Circle => GenericBasicShape::Circle(Circle {
radius: (&other.mCoordinates[0]).into(),
position: other.mPosition,
}),
StyleBasicShapeType::Ellipse => GenericBasicShape::Ellipse(Ellipse {
semiaxis_x: (&other.mCoordinates[0]).into(),
semiaxis_y: (&other.mCoordinates[1]).into(),
position: other.mPosition,
}),
StyleBasicShapeType::Polygon => {
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(PolygonCoord(
LengthPercentage::from_gecko_style_coord(&other.mCoordinates[x])
.expect(
"polygon() coordinate should be a length, percentage, \
or calc value",
),
LengthPercentage::from_gecko_style_coord(&other.mCoordinates[y])
.expect(
"polygon() coordinate should be a length, percentage, \
or calc value",
),
))
}
GenericBasicShape::Polygon(Polygon {
fill: other.mFillRule,
coordinates: coords,
})
},
}
}
}
impl<'a> From<&'a nsStyleCoord> for ShapeRadius {
fn from(other: &'a nsStyleCoord) -> Self {
let other = other.borrow();
ShapeRadius::from_gecko_style_coord(other)
.expect("<shape-radius> should be a length, percentage, calc, or keyword value")
}
}
impl From<ShapeBox> for StyleGeometryBox { impl From<ShapeBox> for StyleGeometryBox {
fn from(reference: ShapeBox) -> Self { fn from(reference: ShapeBox) -> Self {
use crate::gecko_bindings::structs::StyleGeometryBox::*; use crate::gecko_bindings::structs::StyleGeometryBox::*;

View file

@ -7,13 +7,11 @@
//! Different kind of helpers to interact with Gecko values. //! Different kind of helpers to interact with Gecko values.
use crate::counter_style::{Symbol, Symbols}; use crate::counter_style::{Symbol, Symbols};
use crate::gecko_bindings::structs::StyleGridTrackBreadth;
use crate::gecko_bindings::structs::{nsStyleCoord, CounterStylePtr}; use crate::gecko_bindings::structs::{nsStyleCoord, CounterStylePtr};
use crate::gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius};
use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue}; use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
use crate::values::computed::basic_shape::ShapeRadius as ComputedShapeRadius;
use crate::values::computed::{Angle, Length, LengthPercentage}; use crate::values::computed::{Angle, Length, LengthPercentage};
use crate::values::computed::{Number, NumberOrPercentage, Percentage}; use crate::values::computed::{Number, NumberOrPercentage, Percentage};
use crate::values::generics::basic_shape::ShapeRadius;
use crate::values::generics::gecko::ScrollSnapPoint; use crate::values::generics::gecko::ScrollSnapPoint;
use crate::values::generics::grid::{TrackBreadth, TrackKeyword}; use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
use crate::values::generics::length::LengthPercentageOrAuto; use crate::values::generics::length::LengthPercentageOrAuto;
@ -192,35 +190,6 @@ impl<L: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for TrackBreadth<
} }
} }
impl GeckoStyleCoordConvertible for ComputedShapeRadius {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
match *self {
ShapeRadius::ClosestSide => coord.set_value(CoordDataValue::Enumerated(
StyleShapeRadius::ClosestSide as u32,
)),
ShapeRadius::FarthestSide => coord.set_value(CoordDataValue::Enumerated(
StyleShapeRadius::FarthestSide as u32,
)),
ShapeRadius::Length(lp) => lp.to_gecko_style_coord(coord),
}
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Enumerated(v) => {
if v == StyleShapeRadius::ClosestSide as u32 {
Some(ShapeRadius::ClosestSide)
} else if v == StyleShapeRadius::FarthestSide as u32 {
Some(ShapeRadius::FarthestSide)
} else {
None
}
},
_ => GeckoStyleCoordConvertible::from_gecko_style_coord(coord).map(ShapeRadius::Length),
}
}
}
impl<T: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for Option<T> { impl<T: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for Option<T> {
fn to_gecko_style_coord<U: CoordDataMut>(&self, coord: &mut U) { fn to_gecko_style_coord<U: CoordDataMut>(&self, coord: &mut U) {
if let Some(ref me) = *self { if let Some(ref me) = *self {

View file

@ -188,6 +188,9 @@ pub use html5ever::Prefix;
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub use servo_atoms::Atom; pub use servo_atoms::Atom;
pub use style_traits::arc_slice::ArcSlice;
pub use style_traits::owned_slice::OwnedSlice;
/// The CSS properties supported by the style system. /// The CSS properties supported by the style system.
/// Generated from the properties.mako.rs template by build.rs /// Generated from the properties.mako.rs template by build.rs
#[macro_use] #[macro_use]

View file

@ -136,6 +136,12 @@ impl<'a> ParserContext<'a> {
.expect("Rule type expected, but none was found.") .expect("Rule type expected, but none was found.")
} }
/// Returns whether CSS error reporting is enabled.
#[inline]
pub fn error_reporting_enabled(&self) -> bool {
self.error_reporter.is_some()
}
/// Record a CSS parse error with this contexts error reporting. /// Record a CSS parse error with this contexts error reporting.
pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) { pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
let error_reporter = match self.error_reporter { let error_reporter = match self.error_reporter {

View file

@ -337,6 +337,7 @@ class Longhand(object):
"OverflowWrap", "OverflowWrap",
"OverscrollBehavior", "OverscrollBehavior",
"Percentage", "Percentage",
"PositiveIntegerOrNone",
"Resize", "Resize",
"SVGOpacity", "SVGOpacity",
"SVGPaintOrder", "SVGPaintOrder",

View file

@ -1240,19 +1240,28 @@ pub fn parse_one_declaration_into(
None, None,
); );
let property_id_for_error_reporting = if context.error_reporting_enabled() {
Some(id.clone())
} else {
None
};
let mut input = ParserInput::new(input); let mut input = ParserInput::new(input);
let mut parser = Parser::new(&mut input); let mut parser = Parser::new(&mut input);
let start_position = parser.position(); let start_position = parser.position();
parser.parse_entirely(|parser| { parser.parse_entirely(|parser| {
PropertyDeclaration::parse_into(declarations, id, &context, parser) PropertyDeclaration::parse_into(declarations, id, &context, parser)
}).map_err(|err| { }).map_err(|err| {
let location = err.location; if context.error_reporting_enabled() {
let error = ContextualParseError::UnsupportedPropertyDeclaration( report_one_css_error(
parser.slice_from(start_position), &context,
None,
None,
err, err,
None parser.slice_from(start_position),
); property_id_for_error_reporting,
context.log_css_error(location, error); )
}
}) })
} }
@ -1260,6 +1269,8 @@ pub fn parse_one_declaration_into(
struct PropertyDeclarationParser<'a, 'b: 'a> { struct PropertyDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>, context: &'a ParserContext<'b>,
declarations: &'a mut SourcePropertyDeclaration, declarations: &'a mut SourcePropertyDeclaration,
/// The last parsed property id if any.
last_parsed_property_id: Option<PropertyId>,
} }
@ -1289,6 +1300,7 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
let id = match PropertyId::parse(&name, self.context) { let id = match PropertyId::parse(&name, self.context) {
Ok(id) => id, Ok(id) => id,
Err(..) => { Err(..) => {
self.last_parsed_property_id = None;
return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) { return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) {
StyleParseErrorKind::UnknownVendorProperty StyleParseErrorKind::UnknownVendorProperty
} else { } else {
@ -1296,6 +1308,9 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
})); }));
} }
}; };
if self.context.error_reporting_enabled() {
self.last_parsed_property_id = Some(id.clone());
}
input.parse_until_before(Delimiter::Bang, |input| { input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(self.declarations, id, self.context, input) PropertyDeclaration::parse_into(self.declarations, id, self.context, input)
})?; })?;
@ -1309,6 +1324,68 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
} }
} }
type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>;
#[cold]
fn report_one_css_error<'i>(
context: &ParserContext,
block: Option<&PropertyDeclarationBlock>,
selectors: Option<&SelectorList<SelectorImpl>>,
mut error: ParseError<'i>,
slice: &str,
property: Option<PropertyId>,
) {
debug_assert!(context.error_reporting_enabled());
fn all_properties_in_block(block: &PropertyDeclarationBlock, property: &PropertyId) -> bool {
match *property {
PropertyId::LonghandAlias(id, _) |
PropertyId::Longhand(id) => block.contains(id),
PropertyId::ShorthandAlias(id, _) |
PropertyId::Shorthand(id) => {
id.longhands().all(|longhand| block.contains(longhand))
},
// NOTE(emilio): We could do this, but it seems of limited utility,
// and it's linear on the size of the declaration block, so let's
// not.
PropertyId::Custom(..) => false,
}
}
// If the unrecognized property looks like a vendor-specific property,
// silently ignore it instead of polluting the error output.
if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownVendorProperty) = error.kind {
return;
}
if let Some(ref property) = property {
if let Some(block) = block {
if all_properties_in_block(block, property) {
return;
}
}
error = match *property {
PropertyId::Custom(ref c) => StyleParseErrorKind::new_invalid(format!("--{}", c), error),
_ => StyleParseErrorKind::new_invalid(property.non_custom_id().unwrap().name(), error),
};
}
let location = error.location;
let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);
context.log_css_error(location, error);
}
#[cold]
fn report_css_errors(
context: &ParserContext,
block: &PropertyDeclarationBlock,
selectors: Option<&SelectorList<SelectorImpl>>,
errors: &mut SmallParseErrorVec,
) {
for (error, slice, property) in errors.drain() {
report_one_css_error(context, Some(block), selectors, error, slice, property)
}
}
/// Parse a list of property declarations and return a property declaration /// Parse a list of property declarations and return a property declaration
/// block. /// block.
@ -1320,10 +1397,12 @@ pub fn parse_property_declaration_list(
let mut declarations = SourcePropertyDeclaration::new(); let mut declarations = SourcePropertyDeclaration::new();
let mut block = PropertyDeclarationBlock::new(); let mut block = PropertyDeclarationBlock::new();
let parser = PropertyDeclarationParser { let parser = PropertyDeclarationParser {
context: context, context,
last_parsed_property_id: None,
declarations: &mut declarations, declarations: &mut declarations,
}; };
let mut iter = DeclarationListParser::new(input, parser); let mut iter = DeclarationListParser::new(input, parser);
let mut errors = SmallParseErrorVec::new();
while let Some(declaration) = iter.next() { while let Some(declaration) = iter.next() {
match declaration { match declaration {
Ok(importance) => { Ok(importance) => {
@ -1335,17 +1414,17 @@ pub fn parse_property_declaration_list(
Err((error, slice)) => { Err((error, slice)) => {
iter.parser.declarations.clear(); iter.parser.declarations.clear();
// If the unrecognized property looks like a vendor-specific property, if context.error_reporting_enabled() {
// silently ignore it instead of polluting the error output. let property = iter.parser.last_parsed_property_id.take();
if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownVendorProperty) = error.kind { errors.push((error, slice, property));
continue; }
}
}
}
if !errors.is_empty() {
report_css_errors(context, &block, selectors, &mut errors)
} }
let location = error.location;
let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);
context.log_css_error(location, error);
}
}
}
block block
} }

View file

@ -2515,7 +2515,7 @@ fn static_assert() {
rotate scroll-snap-points-x scroll-snap-points-y rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-coordinate -moz-binding will-change scroll-snap-coordinate -moz-binding will-change
offset-path shape-outside offset-path shape-outside
translate scale""" %> translate scale -webkit-line-clamp""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
#[inline] #[inline]
pub fn generate_combined_transform(&mut self) { pub fn generate_combined_transform(&mut self) {
@ -2901,7 +2901,7 @@ fn static_assert() {
match v { match v {
OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None, OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None,
OffsetPath::Path(p) => { OffsetPath::Path(p) => {
set_style_svg_path(&mut motion.mOffsetPath, &p, FillRule::Nonzero) set_style_svg_path(&mut motion.mOffsetPath, p, FillRule::Nonzero)
}, },
} }
unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) }; unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) };
@ -2924,6 +2924,27 @@ fn static_assert() {
self.copy_offset_path_from(other); self.copy_offset_path_from(other);
} }
#[allow(non_snake_case)]
pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) {
self.gecko.mLineClamp = match v {
Either::First(n) => n.0 as u32,
Either::Second(None_) => 0,
};
}
${impl_simple_copy('_webkit_line_clamp', 'mLineClamp')}
#[allow(non_snake_case)]
pub fn clone__webkit_line_clamp(&self) -> longhands::_webkit_line_clamp::computed_value::T {
match self.gecko.mLineClamp {
0 => Either::Second(None_),
n => {
debug_assert!(n <= std::i32::MAX as u32);
Either::First((n as i32).into())
}
}
}
</%self:impl_trait> </%self:impl_trait>
<%def name="simple_image_array_property(name, shorthand, field_name)"> <%def name="simple_image_array_property(name, shorthand, field_name)">
@ -4002,39 +4023,30 @@ fn static_assert() {
// Set SVGPathData to StyleShapeSource. // Set SVGPathData to StyleShapeSource.
fn set_style_svg_path( fn set_style_svg_path(
shape_source: &mut structs::mozilla::StyleShapeSource, shape_source: &mut structs::mozilla::StyleShapeSource,
servo_path: &values::specified::svg_path::SVGPathData, servo_path: values::specified::svg_path::SVGPathData,
fill: values::generics::basic_shape::FillRule, fill: values::generics::basic_shape::FillRule,
) { ) {
use crate::gecko_bindings::bindings::Gecko_NewStyleSVGPath;
use crate::gecko_bindings::structs::StyleShapeSourceType;
// Setup type.
shape_source.mType = StyleShapeSourceType::Path;
// Setup path. // Setup path.
let gecko_path = unsafe { unsafe {
Gecko_NewStyleSVGPath(shape_source); bindings::Gecko_SetToSVGPath(
&mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap() shape_source,
}; servo_path.0.forget(),
fill,
gecko_path.mPath.assign_from_iter_pod(servo_path.commands().iter().cloned()); );
}
// Setup fill-rule.
gecko_path.mFillRule = fill;
} }
<%def name="impl_shape_source(ident, gecko_ffi_name)"> <%def name="impl_shape_source(ident, gecko_ffi_name)">
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
use crate::gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; use crate::values::generics::basic_shape::ShapeSource;
use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; use crate::gecko_bindings::structs::StyleShapeSourceType;
use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource}; use crate::gecko_bindings::structs::StyleGeometryBox;
use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::values::generics::basic_shape::{BasicShape, ShapeSource};
let ref mut ${ident} = self.gecko.${gecko_ffi_name}; let ref mut ${ident} = self.gecko.${gecko_ffi_name};
// clean up existing struct // clean up existing struct.
unsafe { Gecko_DestroyShapeSource(${ident}) }; unsafe { bindings::Gecko_DestroyShapeSource(${ident}) };
${ident}.mType = StyleShapeSourceType::None; ${ident}.mType = StyleShapeSourceType::None;
match v { match v {
@ -4061,74 +4073,14 @@ fn set_style_svg_path(
${ident}.mReferenceBox = reference.into(); ${ident}.mReferenceBox = reference.into();
${ident}.mType = StyleShapeSourceType::Box; ${ident}.mType = StyleShapeSourceType::Box;
} }
ShapeSource::Path(p) => set_style_svg_path(${ident}, &p.path, p.fill), ShapeSource::Path(p) => set_style_svg_path(${ident}, p.path, p.fill),
ShapeSource::Shape(servo_shape, maybe_box) => { ShapeSource::Shape(servo_shape, maybe_box) => {
fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType)
-> &mut StyleBasicShape {
unsafe { unsafe {
// Create StyleBasicShape in StyleShapeSource. mReferenceBox and mType ${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr =
// will be set manually later. Box::into_raw(servo_shape);
Gecko_NewBasicShape(${ident}, basic_shape_type);
&mut *${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr
} }
} ${ident}.mReferenceBox =
match servo_shape { maybe_box.map(Into::into).unwrap_or(StyleGeometryBox::NoBox);
BasicShape::Inset(inset) => {
let shape = init_shape(${ident}, 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();
inset.rect.0.to_gecko_style_coord(&mut shape.mCoordinates[0]);
shape.mCoordinates[1].leaky_set_null();
inset.rect.1.to_gecko_style_coord(&mut shape.mCoordinates[1]);
shape.mCoordinates[2].leaky_set_null();
inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]);
shape.mCoordinates[3].leaky_set_null();
inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
shape.mRadius = inset.round;
}
BasicShape::Circle(circ) => {
let shape = init_shape(${ident}, 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 shape = init_shape(${ident}, 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 shape = init_shape(${ident}, 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 = poly.fill;
}
}
${ident}.mReferenceBox = maybe_box.map(Into::into)
.unwrap_or(StyleGeometryBox::NoBox);
${ident}.mType = StyleShapeSourceType::Shape; ${ident}.mType = StyleShapeSourceType::Shape;
} }
} }

View file

@ -670,3 +670,16 @@ ${helpers.predefined_type(
animation_value_type="discrete", animation_value_type="discrete",
spec="https://compat.spec.whatwg.org/#touch-action", spec="https://compat.spec.whatwg.org/#touch-action",
)} )}
// Note that we only implement -webkit-line-clamp as a single, longhand
// property for now, but the spec defines line-clamp as a shorthand for separate
// max-lines, block-ellipsis, and continue properties.
${helpers.predefined_type(
"-webkit-line-clamp",
"PositiveIntegerOrNone",
"Either::Second(None_)",
gecko_pref="layout.css.webkit-line-clamp.enabled",
animation_value_type="Integer",
products="gecko",
spec="https://drafts.csswg.org/css-overflow-3/#line-clamp",
)}

View file

@ -87,7 +87,6 @@ ${helpers.predefined_type(
"basic_shape::ClippingShape", "basic_shape::ClippingShape",
"generics::basic_shape::ShapeSource::None", "generics::basic_shape::ShapeSource::None",
products="gecko", products="gecko",
boxed=True,
animation_value_type="basic_shape::ClippingShape", animation_value_type="basic_shape::ClippingShape",
flags="CREATES_STACKING_CONTEXT", flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",

View file

@ -2209,13 +2209,9 @@ impl PropertyDeclaration {
// This probably affects some test results. // This probably affects some test results.
let value = match input.try(CSSWideKeyword::parse) { let value = match input.try(CSSWideKeyword::parse) {
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword), Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
Err(()) => match crate::custom_properties::SpecifiedValue::parse(input) { Err(()) => CustomDeclarationValue::Value(
Ok(value) => CustomDeclarationValue::Value(value), crate::custom_properties::SpecifiedValue::parse(input)?
Err(e) => return Err(StyleParseErrorKind::new_invalid( ),
format!("--{}", property_name),
e,
)),
}
}; };
declarations.push(PropertyDeclaration::Custom(CustomDeclaration { declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
name: property_name, name: property_name,
@ -2236,19 +2232,11 @@ impl PropertyDeclaration {
.or_else(|err| { .or_else(|err| {
while let Ok(_) = input.next() {} // Look for var() after the error. while let Ok(_) = input.next() {} // Look for var() after the error.
if !input.seen_var_or_env_functions() { if !input.seen_var_or_env_functions() {
return Err(StyleParseErrorKind::new_invalid( return Err(err);
non_custom_id.unwrap().name(),
err,
));
} }
input.reset(&start); input.reset(&start);
let (first_token_type, css) = let (first_token_type, css) =
crate::custom_properties::parse_non_custom_with_var(input).map_err(|e| { crate::custom_properties::parse_non_custom_with_var(input)?;
StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
e,
)
})?;
Ok(PropertyDeclaration::WithVariables(VariableDeclaration { Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
id, id,
value: Arc::new(UnparsedValue { value: Arc::new(UnparsedValue {
@ -2287,20 +2275,12 @@ impl PropertyDeclaration {
id.parse_into(declarations, context, input).or_else(|err| { id.parse_into(declarations, context, input).or_else(|err| {
while let Ok(_) = input.next() {} // Look for var() after the error. while let Ok(_) = input.next() {} // Look for var() after the error.
if !input.seen_var_or_env_functions() { if !input.seen_var_or_env_functions() {
return Err(StyleParseErrorKind::new_invalid( return Err(err);
non_custom_id.unwrap().name(),
err,
));
} }
input.reset(&start); input.reset(&start);
let (first_token_type, css) = let (first_token_type, css) =
crate::custom_properties::parse_non_custom_with_var(input).map_err(|e| { crate::custom_properties::parse_non_custom_with_var(input)?;
StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
e,
)
})?;
let unparsed = Arc::new(UnparsedValue { let unparsed = Arc::new(UnparsedValue {
css: css.into_owned(), css: css.into_owned(),
first_token_type: first_token_type, first_token_type: first_token_type,

View file

@ -16,7 +16,6 @@ use crate::values::computed::Image;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use crate::values::CSSFloat; use crate::values::CSSFloat;
use app_units::Au; use app_units::Au;
use euclid::Point2D;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cmp; use std::cmp;
@ -241,16 +240,10 @@ impl Animate for Au {
} }
} }
impl<T> Animate for Point2D<T> impl<T: Animate> Animate for Box<T> {
where
T: Animate,
{
#[inline] #[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Point2D::new( Ok(Box::new((**self).animate(&other, procedure)?))
self.x.animate(&other.x, procedure)?,
self.y.animate(&other.y, procedure)?,
))
} }
} }
@ -288,6 +281,66 @@ where
} }
} }
impl<T> ToAnimatedValue for Box<T>
where
T: ToAnimatedValue,
{
type AnimatedValue = Box<<T as ToAnimatedValue>::AnimatedValue>;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
Box::new((*self).to_animated_value())
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Box::new(T::from_animated_value(*animated))
}
}
impl<T> ToAnimatedValue for Box<[T]>
where
T: ToAnimatedValue,
{
type AnimatedValue = Box<[<T as ToAnimatedValue>::AnimatedValue]>;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.into_vec()
.into_iter()
.map(T::to_animated_value)
.collect::<Vec<_>>()
.into_boxed_slice()
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
animated
.into_vec()
.into_iter()
.map(T::from_animated_value)
.collect::<Vec<_>>()
.into_boxed_slice()
}
}
impl<T> ToAnimatedValue for crate::OwnedSlice<T>
where
T: ToAnimatedValue,
{
type AnimatedValue = crate::OwnedSlice<<T as ToAnimatedValue>::AnimatedValue>;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.into_box().to_animated_value().into()
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Self::from(Box::from_animated_value(animated.into_box()))
}
}
impl<T> ToAnimatedValue for SmallVec<[T; 1]> impl<T> ToAnimatedValue for SmallVec<[T; 1]>
where where
T: ToAnimatedValue, T: ToAnimatedValue,
@ -407,3 +460,17 @@ where
Ok(v.into_boxed_slice()) Ok(v.into_boxed_slice())
} }
} }
impl<T> ToAnimatedZero for crate::ArcSlice<T>
where
T: ToAnimatedZero,
{
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
let v = self
.iter()
.map(|v| v.to_animated_zero())
.collect::<Result<Vec<_>, _>>()?;
Ok(crate::ArcSlice::from_iter(v.into_iter()))
}
}

View file

@ -23,7 +23,7 @@ pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>;
pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>; pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>;
/// A computed basic shape. /// A computed basic shape.
pub type BasicShape = generic::BasicShape< pub type BasicShape = generic::GenericBasicShape<
LengthPercentage, LengthPercentage,
LengthPercentage, LengthPercentage,
LengthPercentage, LengthPercentage,
@ -41,7 +41,7 @@ pub type Ellipse =
generic::Ellipse<LengthPercentage, LengthPercentage, NonNegativeLengthPercentage>; generic::Ellipse<LengthPercentage, LengthPercentage, NonNegativeLengthPercentage>;
/// The computed value of `ShapeRadius` /// The computed value of `ShapeRadius`
pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>; pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
impl ToCss for Circle { impl ToCss for Circle {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result

View file

@ -444,6 +444,30 @@ where
} }
} }
impl<T> ToComputedValue for crate::OwnedSlice<T>
where
T: ToComputedValue,
{
type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
self.iter()
.map(|item| item.to_computed_value(context))
.collect::<Vec<_>>()
.into()
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
computed
.iter()
.map(T::from_computed_value)
.collect::<Vec<_>>()
.into()
}
}
trivial_to_computed_value!(()); trivial_to_computed_value!(());
trivial_to_computed_value!(bool); trivial_to_computed_value!(bool);
trivial_to_computed_value!(f32); trivial_to_computed_value!(f32);
@ -640,6 +664,9 @@ impl From<CSSInteger> for PositiveInteger {
} }
} }
/// A computed positive `<integer>` value or `none`.
pub type PositiveIntegerOrNone = Either<PositiveInteger, None_>;
/// rect(...) /// rect(...)
pub type ClipRect = generics::ClipRect<LengthOrAuto>; pub type ClipRect = generics::ClipRect<LengthOrAuto>;

View file

@ -7,8 +7,8 @@
use crate::values::animated::{Animate, Procedure, ToAnimatedZero}; use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::border::BorderRadius; use crate::values::generics::border::GenericBorderRadius;
use crate::values::generics::position::Position; use crate::values::generics::position::GenericPosition;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use crate::Zero; use crate::Zero;
@ -89,7 +89,7 @@ pub enum ShapeBox {
pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> { pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
#[animation(error)] #[animation(error)]
ImageOrUrl(ImageOrUrl), ImageOrUrl(ImageOrUrl),
Shape(BasicShape, Option<ReferenceBox>), Shape(Box<BasicShape>, Option<ReferenceBox>),
#[animation(error)] #[animation(error)]
Box(ReferenceBox), Box(ReferenceBox),
#[css(function)] #[css(function)]
@ -113,7 +113,8 @@ pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub enum BasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> { #[repr(C, u8)]
pub enum GenericBasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
Inset( Inset(
#[css(field_bound)] #[css(field_bound)]
#[shmem(field_bound)] #[shmem(field_bound)]
@ -129,9 +130,11 @@ pub enum BasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
#[shmem(field_bound)] #[shmem(field_bound)]
Ellipse<H, V, NonNegativeLengthPercentage>, Ellipse<H, V, NonNegativeLengthPercentage>,
), ),
Polygon(Polygon<LengthPercentage>), Polygon(GenericPolygon<LengthPercentage>),
} }
pub use self::GenericBasicShape as BasicShape;
/// <https://drafts.csswg.org/css-shapes/#funcdef-inset> /// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
#[allow(missing_docs)] #[allow(missing_docs)]
#[css(function = "inset")] #[css(function = "inset")]
@ -148,10 +151,11 @@ pub enum BasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)]
pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> { pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
pub rect: Rect<LengthPercentage>, pub rect: Rect<LengthPercentage>,
#[shmem(field_bound)] #[shmem(field_bound)]
pub round: BorderRadius<NonNegativeLengthPercentage>, pub round: GenericBorderRadius<NonNegativeLengthPercentage>,
} }
/// <https://drafts.csswg.org/css-shapes/#funcdef-circle> /// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
@ -171,9 +175,10 @@ pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)]
pub struct Circle<H, V, NonNegativeLengthPercentage> { pub struct Circle<H, V, NonNegativeLengthPercentage> {
pub position: Position<H, V>, pub position: GenericPosition<H, V>,
pub radius: ShapeRadius<NonNegativeLengthPercentage>, pub radius: GenericShapeRadius<NonNegativeLengthPercentage>,
} }
/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse> /// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
@ -193,10 +198,11 @@ pub struct Circle<H, V, NonNegativeLengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)]
pub struct Ellipse<H, V, NonNegativeLengthPercentage> { pub struct Ellipse<H, V, NonNegativeLengthPercentage> {
pub position: Position<H, V>, pub position: GenericPosition<H, V>,
pub semiaxis_x: ShapeRadius<NonNegativeLengthPercentage>, pub semiaxis_x: GenericShapeRadius<NonNegativeLengthPercentage>,
pub semiaxis_y: ShapeRadius<NonNegativeLengthPercentage>, pub semiaxis_y: GenericShapeRadius<NonNegativeLengthPercentage>,
} }
/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius> /// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
@ -216,7 +222,8 @@ pub struct Ellipse<H, V, NonNegativeLengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub enum ShapeRadius<NonNegativeLengthPercentage> { #[repr(C, u8)]
pub enum GenericShapeRadius<NonNegativeLengthPercentage> {
Length(NonNegativeLengthPercentage), Length(NonNegativeLengthPercentage),
#[animation(error)] #[animation(error)]
ClosestSide, ClosestSide,
@ -224,10 +231,12 @@ pub enum ShapeRadius<NonNegativeLengthPercentage> {
FarthestSide, FarthestSide,
} }
pub use self::GenericShapeRadius as ShapeRadius;
/// A generic type for representing the `polygon()` function /// A generic type for representing the `polygon()` function
/// ///
/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon> /// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
#[css(comma, function)] #[css(comma, function = "polygon")]
#[derive( #[derive(
Clone, Clone,
Debug, Debug,
@ -240,15 +249,18 @@ pub enum ShapeRadius<NonNegativeLengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub struct Polygon<LengthPercentage> { #[repr(C)]
pub struct GenericPolygon<LengthPercentage> {
/// The filling rule for a polygon. /// The filling rule for a polygon.
#[css(skip_if = "fill_is_default")] #[css(skip_if = "fill_is_default")]
pub fill: FillRule, pub fill: FillRule,
/// A collection of (x, y) coordinates to draw the polygon. /// A collection of (x, y) coordinates to draw the polygon.
#[css(iterable)] #[css(iterable)]
pub coordinates: Vec<PolygonCoord<LengthPercentage>>, pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,
} }
pub use self::GenericPolygon as Polygon;
/// Coordinates for Polygon. /// Coordinates for Polygon.
#[derive( #[derive(
Clone, Clone,
@ -262,6 +274,7 @@ pub struct Polygon<LengthPercentage> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)]
pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage); pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule // https://drafts.csswg.org/css-shapes/#typedef-fill-rule
@ -393,7 +406,8 @@ where
this.1.animate(&other.1, procedure)?, this.1.animate(&other.1, procedure)?,
)) ))
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?
.into();
Ok(Polygon { Ok(Polygon {
fill: self.fill, fill: self.fill,
coordinates, coordinates,

View file

@ -193,3 +193,20 @@ where
Vec::from_resolved_value(Vec::from(resolved)).into_boxed_slice() Vec::from_resolved_value(Vec::from(resolved)).into_boxed_slice()
} }
} }
impl<T> ToResolvedValue for crate::OwnedSlice<T>
where
T: ToResolvedValue,
{
type ResolvedValue = crate::OwnedSlice<<T as ToResolvedValue>::ResolvedValue>;
#[inline]
fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
self.into_box().to_resolved_value(context).into()
}
#[inline]
fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
Self::from(Box::from_resolved_value(resolved.into_box()))
}
}

View file

@ -55,7 +55,7 @@ pub type Ellipse =
pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>; pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
/// The specified value of `Polygon` /// The specified value of `Polygon`
pub type Polygon = generic::Polygon<LengthPercentage>; pub type Polygon = generic::GenericPolygon<LengthPercentage>;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn is_clip_path_path_enabled(context: &ParserContext) -> bool { fn is_clip_path_path_enabled(context: &ParserContext) -> bool {
@ -138,11 +138,11 @@ where
} }
if let Some(shp) = shape { if let Some(shp) = shape {
return Ok(ShapeSource::Shape(shp, ref_box)); return Ok(ShapeSource::Shape(Box::new(shp), ref_box));
} }
ref_box ref_box
.map(|v| ShapeSource::Box(v)) .map(ShapeSource::Box)
.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) .ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
} }
@ -152,7 +152,7 @@ impl Parse for GeometryBox {
_context: &ParserContext, _context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
if let Ok(shape_box) = input.try(|i| ShapeBox::parse(i)) { if let Ok(shape_box) = input.try(ShapeBox::parse) {
return Ok(GeometryBox::ShapeBox(shape_box)); return Ok(GeometryBox::ShapeBox(shape_box));
} }
@ -352,17 +352,16 @@ impl Polygon {
}) })
.unwrap_or_default(); .unwrap_or_default();
let buf = input.parse_comma_separated(|i| { let coordinates = input
.parse_comma_separated(|i| {
Ok(PolygonCoord( Ok(PolygonCoord(
LengthPercentage::parse(context, i)?, LengthPercentage::parse(context, i)?,
LengthPercentage::parse(context, i)?, LengthPercentage::parse(context, i)?,
)) ))
})?; })?
.into();
Ok(Polygon { Ok(Polygon { fill, coordinates })
fill: fill,
coordinates: buf,
})
} }
} }

View file

@ -12,7 +12,7 @@ use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as Generic
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo; use super::generics::transform::IsParallelTo;
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative}; use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::{Auto, CSSFloat, CSSInteger, Either}; use super::{Auto, CSSFloat, CSSInteger, Either, None_};
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::serialize_atom_identifier; use crate::values::serialize_atom_identifier;
@ -593,6 +593,9 @@ impl Parse for PositiveInteger {
} }
} }
/// A specified positive `<integer>` value or `none`.
pub type PositiveIntegerOrNone = Either<PositiveInteger, None_>;
/// The specified value of a grid `<track-breadth>` /// The specified value of a grid `<track-breadth>`
pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>; pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;

View file

@ -29,16 +29,14 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub struct SVGPathData(Box<[PathCommand]>); #[repr(C)]
pub struct SVGPathData(
// TODO(emilio): Should probably measure this somehow only from the
// specified values.
#[ignore_malloc_size_of = "Arc"] pub crate::ArcSlice<PathCommand>,
);
impl SVGPathData { impl SVGPathData {
/// Return SVGPathData by a slice of PathCommand.
#[inline]
pub fn new(cmd: Box<[PathCommand]>) -> Self {
debug_assert!(!cmd.is_empty());
SVGPathData(cmd)
}
/// Get the array of PathCommand. /// Get the array of PathCommand.
#[inline] #[inline]
pub fn commands(&self) -> &[PathCommand] { pub fn commands(&self) -> &[PathCommand] {
@ -46,9 +44,9 @@ impl SVGPathData {
&self.0 &self.0
} }
/// Create a normalized copy of this path by converting each relative command to an absolute /// Create a normalized copy of this path by converting each relative
/// command. /// command to an absolute command.
fn normalize(&self) -> Self { fn normalize(&self) -> Box<[PathCommand]> {
let mut state = PathTraversalState { let mut state = PathTraversalState {
subpath_start: CoordPair::new(0.0, 0.0), subpath_start: CoordPair::new(0.0, 0.0),
pos: CoordPair::new(0.0, 0.0), pos: CoordPair::new(0.0, 0.0),
@ -58,7 +56,7 @@ impl SVGPathData {
.iter() .iter()
.map(|seg| seg.normalize(&mut state)) .map(|seg| seg.normalize(&mut state))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
SVGPathData(result.into_boxed_slice()) result.into_boxed_slice()
} }
} }
@ -71,7 +69,7 @@ impl ToCss for SVGPathData {
dest.write_char('"')?; dest.write_char('"')?;
{ {
let mut writer = SequenceWriter::new(dest, " "); let mut writer = SequenceWriter::new(dest, " ");
for command in self.0.iter() { for command in self.commands() {
writer.item(command)?; writer.item(command)?;
} }
} }
@ -104,7 +102,9 @@ impl Parse for SVGPathData {
} }
} }
Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) Ok(SVGPathData(crate::ArcSlice::from_iter(
path_parser.path.into_iter(),
)))
} }
} }
@ -114,14 +114,17 @@ impl Animate for SVGPathData {
return Err(()); return Err(());
} }
// FIXME(emilio): This allocates three copies of the path, that's not
// great! Specially, once we're normalized once, we don't need to
// re-normalize again.
let result = self let result = self
.normalize() .normalize()
.0
.iter() .iter()
.zip(other.normalize().0.iter()) .zip(other.normalize().iter())
.map(|(a, b)| a.animate(&b, procedure)) .map(|(a, b)| a.animate(&b, procedure))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
Ok(SVGPathData::new(result.into_boxed_slice()))
Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
} }
} }
@ -131,9 +134,8 @@ impl ComputeSquaredDistance for SVGPathData {
return Err(()); return Err(());
} }
self.normalize() self.normalize()
.0
.iter() .iter()
.zip(other.normalize().0.iter()) .zip(other.normalize().iter())
.map(|(this, other)| this.compute_squared_distance(&other)) .map(|(this, other)| this.compute_squared_distance(&other))
.sum() .sum()
} }

View file

@ -0,0 +1,66 @@
/* 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 https://mozilla.org/MPL/2.0/. */
//! A thin atomically-reference-counted slice.
use servo_arc::ThinArc;
use std::mem;
use std::ops::Deref;
use std::ptr::NonNull;
/// A canary that we stash in ArcSlices.
///
/// Given we cannot use a zero-sized-type for the header, since well, C++
/// doesn't have zsts, and we want to use cbindgen for this type, we may as well
/// assert some sanity at runtime.
const ARC_SLICE_CANARY: u32 = 0xf3f3f3f3;
/// A wrapper type for a refcounted slice using ThinArc.
///
/// cbindgen:derive-eq=false
/// cbindgen:derive-neq=false
#[repr(C)]
#[derive(Clone, Debug, Eq, PartialEq, ToShmem)]
pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u32, T>);
impl<T> Deref for ArcSlice<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
debug_assert_eq!(self.0.header.header, ARC_SLICE_CANARY);
&self.0.slice
}
}
/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
/// The type of the pointer is a bit of a lie, we just want to preserve the type
/// but these pointers cannot be constructed outside of this crate, so we're
/// good.
#[repr(C)]
pub struct ForgottenArcSlicePtr<T>(NonNull<T>);
impl<T> ArcSlice<T> {
/// Creates an Arc for a slice using the given iterator to generate the
/// slice.
#[inline]
pub fn from_iter<I>(items: I) -> Self
where
I: Iterator<Item = T> + ExactSizeIterator,
{
ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
}
/// Creates a value that can be passed via FFI, and forgets this value
/// altogether.
#[inline]
#[allow(unsafe_code)]
pub fn forget(self) -> ForgottenArcSlicePtr<T> {
let ret = unsafe {
ForgottenArcSlicePtr(NonNull::new_unchecked(self.0.ptr() as *const _ as *mut _))
};
mem::forget(self);
ret
}
}

View file

@ -84,11 +84,13 @@ pub enum CSSPixel {}
// / hidpi_ratio => DeviceIndependentPixel // / hidpi_ratio => DeviceIndependentPixel
// / desktop_zoom => CSSPixel // / desktop_zoom => CSSPixel
pub mod arc_slice;
pub mod specified_value_info; pub mod specified_value_info;
#[macro_use] #[macro_use]
pub mod values; pub mod values;
#[macro_use] #[macro_use]
pub mod viewport; pub mod viewport;
pub mod owned_slice;
pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo}; pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
pub use crate::values::{ pub use crate::values::{

View file

@ -0,0 +1,166 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#![allow(unsafe_code)]
//! A replacement for `Box<[T]>` that cbindgen can understand.
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use std::{fmt, mem, slice};
use to_shmem::{SharedMemoryBuilder, ToShmem};
/// A struct that basically replaces a `Box<[T]>`, but which cbindgen can
/// understand.
///
/// We could rely on the struct layout of `Box<[T]>` per:
///
/// https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/pointers.md
///
/// But handling fat pointers with cbindgen both in structs and argument
/// positions more generally is a bit tricky.
///
/// cbindgen:derive-eq=false
/// cbindgen:derive-neq=false
#[repr(C)]
pub struct OwnedSlice<T: Sized> {
ptr: NonNull<T>,
len: usize,
_phantom: PhantomData<T>,
}
impl<T: Sized> Default for OwnedSlice<T> {
#[inline]
fn default() -> Self {
Self {
len: 0,
ptr: NonNull::dangling(),
_phantom: PhantomData,
}
}
}
impl<T: Sized> Drop for OwnedSlice<T> {
#[inline]
fn drop(&mut self) {
if self.len != 0 {
let _ = mem::replace(self, Self::default()).into_vec();
}
}
}
unsafe impl<T: Sized + Send> Send for OwnedSlice<T> {}
unsafe impl<T: Sized + Sync> Sync for OwnedSlice<T> {}
impl<T: Clone> Clone for OwnedSlice<T> {
#[inline]
fn clone(&self) -> Self {
Self::from_slice(&**self)
}
}
impl<T: fmt::Debug> fmt::Debug for OwnedSlice<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.deref().fmt(formatter)
}
}
impl<T: PartialEq> PartialEq for OwnedSlice<T> {
fn eq(&self, other: &Self) -> bool {
self.deref().eq(other.deref())
}
}
impl<T: Eq> Eq for OwnedSlice<T> {}
impl<T: Sized> OwnedSlice<T> {
/// Convert the OwnedSlice into a boxed slice.
#[inline]
pub fn into_box(self) -> Box<[T]> {
self.into_vec().into_boxed_slice()
}
/// Convert the OwnedSlice into a Vec.
#[inline]
pub fn into_vec(self) -> Vec<T> {
let ret = unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), self.len, self.len) };
mem::forget(self);
ret
}
/// Iterate over all the elements in the slice taking ownership of them.
#[inline]
pub fn into_iter(self) -> impl Iterator<Item = T> {
self.into_vec().into_iter()
}
/// Convert the regular slice into an owned slice.
#[inline]
pub fn from_slice(s: &[T]) -> Self
where
T: Clone,
{
Self::from(s.to_vec())
}
}
impl<T> Deref for OwnedSlice<T> {
type Target = [T];
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
impl<T> DerefMut for OwnedSlice<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
impl<T> From<Box<[T]>> for OwnedSlice<T> {
#[inline]
fn from(mut b: Box<[T]>) -> Self {
let len = b.len();
let ptr = unsafe { NonNull::new_unchecked(b.as_mut_ptr()) };
mem::forget(b);
Self {
len,
ptr,
_phantom: PhantomData,
}
}
}
impl<T> From<Vec<T>> for OwnedSlice<T> {
#[inline]
fn from(b: Vec<T>) -> Self {
Self::from(b.into_boxed_slice())
}
}
impl<T: Sized> MallocShallowSizeOf for OwnedSlice<T> {
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
unsafe { ops.malloc_size_of(self.ptr.as_ptr()) }
}
}
impl<T: MallocSizeOf + Sized> MallocSizeOf for OwnedSlice<T> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.shallow_size_of(ops) + (**self).size_of(ops)
}
}
impl<T: ToShmem + Sized> ToShmem for OwnedSlice<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> mem::ManuallyDrop<Self> {
unsafe {
let dest = to_shmem::to_shmem_slice(self.iter(), builder);
mem::ManuallyDrop::new(Self::from(Box::from_raw(dest)))
}
}
}

View file

@ -4,6 +4,7 @@
//! Value information for devtools. //! Value information for devtools.
use crate::arc_slice::ArcSlice;
use servo_arc::Arc; use servo_arc::Arc;
use std::ops::Range; use std::ops::Range;
use std::sync::Arc as StdArc; use std::sync::Arc as StdArc;
@ -116,6 +117,7 @@ impl_generic_specified_value_info!(Option<T>);
impl_generic_specified_value_info!(Vec<T>); impl_generic_specified_value_info!(Vec<T>);
impl_generic_specified_value_info!(Arc<T>); impl_generic_specified_value_info!(Arc<T>);
impl_generic_specified_value_info!(StdArc<T>); impl_generic_specified_value_info!(StdArc<T>);
impl_generic_specified_value_info!(ArcSlice<T>);
impl_generic_specified_value_info!(Range<Idx>); impl_generic_specified_value_info!(Range<Idx>);
impl<T1, T2> SpecifiedValueInfo for (T1, T2) impl<T1, T2> SpecifiedValueInfo for (T1, T2)

View file

@ -311,7 +311,7 @@ where
/// Writes all the items in `src` into a slice in the shared memory buffer and /// Writes all the items in `src` into a slice in the shared memory buffer and
/// returns a pointer to the slice. /// returns a pointer to the slice.
unsafe fn to_shmem_slice<'a, T, I>(src: I, builder: &mut SharedMemoryBuilder) -> *mut [T] pub unsafe fn to_shmem_slice<'a, T, I>(src: I, builder: &mut SharedMemoryBuilder) -> *mut [T]
where where
T: 'a + ToShmem, T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>, I: ExactSizeIterator<Item = &'a T>,