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
// 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.
#![allow(missing_docs)]
@ -86,10 +86,14 @@ const STATIC_REFCOUNT: usize = usize::MAX;
/// See the documentation for [`Arc`] in the standard library. Unlike the
/// 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
#[repr(C)]
pub struct Arc<T: ?Sized> {
p: ptr::NonNull<ArcInner<T>>,
phantom: PhantomData<T>,
}
/// An `Arc` that is known to be uniquely owned
@ -164,11 +168,12 @@ impl<T> Arc<T> {
pub fn new(data: T) -> Self {
let x = Box::new(ArcInner {
count: atomic::AtomicUsize::new(1),
data: data,
data,
});
unsafe {
Arc {
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));
Arc {
p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),
phantom: PhantomData,
}
}
@ -224,6 +230,7 @@ impl<T> Arc<T> {
Arc {
p: ptr::NonNull::new_unchecked(ptr),
phantom: PhantomData,
}
}
@ -301,6 +308,9 @@ impl<T: ?Sized> Arc<T> {
impl<T: ?Sized> Clone for Arc<T> {
#[inline]
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
// `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {
@ -334,6 +344,7 @@ impl<T: ?Sized> Clone for Arc<T> {
unsafe {
Arc {
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> {
#[inline]
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
// `count` never changes between STATIC_REFCOUNT and other values.
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
/// slice in a single allocation.
#[derive(Debug, Eq, PartialEq, PartialOrd)]
#[repr(C)]
pub struct HeaderSlice<H, T: ?Sized> {
/// The fixed-sized data.
pub header: H,
@ -594,7 +609,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
F: FnOnce(Layout) -> *mut u8,
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");
// Compute the required size for the allocation.
@ -602,7 +617,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
let size = {
// First, determine the alignment of a hypothetical pointer to a
// 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
// to a sequence of T.
@ -659,23 +674,28 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
};
ptr::write(&mut ((*ptr).count), count);
ptr::write(&mut ((*ptr).data.header), header);
let mut current: *mut T = &mut (*ptr).data.slice[0];
for _ in 0..num_items {
ptr::write(
current,
items
.next()
.expect("ExactSizeIterator over-reported length"),
if num_items != 0 {
let mut current: *mut T = &mut (*ptr).data.slice[0];
for _ in 0..num_items {
ptr::write(
current,
items
.next()
.expect("ExactSizeIterator over-reported length"),
);
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>()
);
current = current.offset(1);
}
assert!(
items.next().is_none(),
"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.
@ -687,6 +707,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
unsafe {
Arc {
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 type in HeaderSlice can take advantage of ThinArc.
#[derive(Debug, Eq, PartialEq, PartialOrd)]
#[repr(C)]
pub struct HeaderWithLength<H> {
/// The fixed-sized data.
pub header: H,
@ -763,11 +785,20 @@ type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;
/// have a thin pointer instead, perhaps for FFI compatibility
/// 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,
/// via `HeaderSliceWithLength`.
#[repr(C)]
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> {}
@ -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.
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]>> {
let len = unsafe { (*thin).data.header.length };
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.
let transient = unsafe {
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
/// 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.
#[inline]
pub fn heap_ptr(&self) -> *const c_void {
@ -851,7 +887,7 @@ impl<H, T> ThinArc<H, T> {
if is_static {
ptr::null()
} 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]
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> {
#[inline]
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);
let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
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.
#[inline]
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);
unsafe {
Arc {
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]
fn slices_and_thin() {
let mut canary = atomic::AtomicUsize::new(0);

View file

@ -532,28 +532,15 @@ impl nsStyleImage {
pub mod basic_shape {
//! 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::{
StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
};
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::values::computed::basic_shape::{
BasicShape, ClippingShape, FloatAreaShape, ShapeRadius,
};
use crate::values::computed::length::LengthPercentage;
use crate::values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape};
use crate::values::computed::motion::OffsetPath;
use crate::values::computed::url::ComputedUrl;
use crate::values::generics::basic_shape::{
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::generics::basic_shape::{GeometryBox, Path, ShapeBox, ShapeSource};
use crate::values::specified::SVGPathData;
use std::borrow::Borrow;
impl StyleShapeSource {
/// 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::Shape => {
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 {
None
} else {
@ -588,12 +575,10 @@ pub mod basic_shape {
/// Generate a SVGPathData from StyleShapeSource if possible.
fn to_svg_path(&self) -> Option<SVGPathData> {
use crate::values::specified::svg_path::PathCommand;
match self.mType {
StyleShapeSourceType::Path => {
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
let result: Vec<PathCommand> = gecko_path.mPath.iter().cloned().collect();
Some(SVGPathData::new(result.into_boxed_slice()))
Some(SVGPathData(gecko_path.mPath.clone()))
},
_ => 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 {
fn from(reference: ShapeBox) -> Self {
use crate::gecko_bindings::structs::StyleGeometryBox::*;

View file

@ -7,13 +7,11 @@
//! Different kind of helpers to interact with Gecko values.
use crate::counter_style::{Symbol, Symbols};
use crate::gecko_bindings::structs::StyleGridTrackBreadth;
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::values::computed::basic_shape::ShapeRadius as ComputedShapeRadius;
use crate::values::computed::{Angle, Length, LengthPercentage};
use crate::values::computed::{Number, NumberOrPercentage, Percentage};
use crate::values::generics::basic_shape::ShapeRadius;
use crate::values::generics::gecko::ScrollSnapPoint;
use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
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> {
fn to_gecko_style_coord<U: CoordDataMut>(&self, coord: &mut U) {
if let Some(ref me) = *self {

View file

@ -188,6 +188,9 @@ pub use html5ever::Prefix;
#[cfg(feature = "servo")]
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.
/// Generated from the properties.mako.rs template by build.rs
#[macro_use]

View file

@ -136,6 +136,12 @@ impl<'a> ParserContext<'a> {
.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.
pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
let error_reporter = match self.error_reporter {

View file

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

View file

@ -1240,19 +1240,28 @@ pub fn parse_one_declaration_into(
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 parser = Parser::new(&mut input);
let start_position = parser.position();
parser.parse_entirely(|parser| {
PropertyDeclaration::parse_into(declarations, id, &context, parser)
}).map_err(|err| {
let location = err.location;
let error = ContextualParseError::UnsupportedPropertyDeclaration(
parser.slice_from(start_position),
err,
None
);
context.log_css_error(location, error);
if context.error_reporting_enabled() {
report_one_css_error(
&context,
None,
None,
err,
parser.slice_from(start_position),
property_id_for_error_reporting,
)
}
})
}
@ -1260,6 +1269,8 @@ pub fn parse_one_declaration_into(
struct PropertyDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
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) {
Ok(id) => id,
Err(..) => {
self.last_parsed_property_id = None;
return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) {
StyleParseErrorKind::UnknownVendorProperty
} 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| {
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
/// block.
@ -1320,10 +1397,12 @@ pub fn parse_property_declaration_list(
let mut declarations = SourcePropertyDeclaration::new();
let mut block = PropertyDeclarationBlock::new();
let parser = PropertyDeclarationParser {
context: context,
context,
last_parsed_property_id: None,
declarations: &mut declarations,
};
let mut iter = DeclarationListParser::new(input, parser);
let mut errors = SmallParseErrorVec::new();
while let Some(declaration) = iter.next() {
match declaration {
Ok(importance) => {
@ -1335,17 +1414,17 @@ pub fn parse_property_declaration_list(
Err((error, slice)) => {
iter.parser.declarations.clear();
// 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 {
continue;
if context.error_reporting_enabled() {
let property = iter.parser.last_parsed_property_id.take();
errors.push((error, slice, property));
}
let location = error.location;
let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);
context.log_css_error(location, error);
}
}
}
if !errors.is_empty() {
report_css_errors(context, &block, selectors, &mut errors)
}
block
}

View file

@ -2515,7 +2515,7 @@ fn static_assert() {
rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-coordinate -moz-binding will-change
offset-path shape-outside
translate scale""" %>
translate scale -webkit-line-clamp""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
#[inline]
pub fn generate_combined_transform(&mut self) {
@ -2901,7 +2901,7 @@ fn static_assert() {
match v {
OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None,
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) };
@ -2924,6 +2924,27 @@ fn static_assert() {
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>
<%def name="simple_image_array_property(name, shorthand, field_name)">
@ -4002,39 +4023,30 @@ fn static_assert() {
// Set SVGPathData to StyleShapeSource.
fn set_style_svg_path(
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,
) {
use crate::gecko_bindings::bindings::Gecko_NewStyleSVGPath;
use crate::gecko_bindings::structs::StyleShapeSourceType;
// Setup type.
shape_source.mType = StyleShapeSourceType::Path;
// Setup path.
let gecko_path = unsafe {
Gecko_NewStyleSVGPath(shape_source);
&mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap()
};
gecko_path.mPath.assign_from_iter_pod(servo_path.commands().iter().cloned());
// Setup fill-rule.
gecko_path.mFillRule = fill;
unsafe {
bindings::Gecko_SetToSVGPath(
shape_source,
servo_path.0.forget(),
fill,
);
}
}
<%def name="impl_shape_source(ident, gecko_ffi_name)">
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
use crate::gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource};
use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType};
use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource};
use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::values::generics::basic_shape::{BasicShape, ShapeSource};
use crate::values::generics::basic_shape::ShapeSource;
use crate::gecko_bindings::structs::StyleShapeSourceType;
use crate::gecko_bindings::structs::StyleGeometryBox;
let ref mut ${ident} = self.gecko.${gecko_ffi_name};
// clean up existing struct
unsafe { Gecko_DestroyShapeSource(${ident}) };
// clean up existing struct.
unsafe { bindings::Gecko_DestroyShapeSource(${ident}) };
${ident}.mType = StyleShapeSourceType::None;
match v {
@ -4061,74 +4073,14 @@ fn set_style_svg_path(
${ident}.mReferenceBox = reference.into();
${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) => {
fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType)
-> &mut StyleBasicShape {
unsafe {
// Create StyleBasicShape in StyleShapeSource. mReferenceBox and mType
// will be set manually later.
Gecko_NewBasicShape(${ident}, basic_shape_type);
&mut *${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr
}
unsafe {
${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr =
Box::into_raw(servo_shape);
}
match servo_shape {
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}.mReferenceBox =
maybe_box.map(Into::into).unwrap_or(StyleGeometryBox::NoBox);
${ident}.mType = StyleShapeSourceType::Shape;
}
}

View file

@ -670,3 +670,16 @@ ${helpers.predefined_type(
animation_value_type="discrete",
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",
"generics::basic_shape::ShapeSource::None",
products="gecko",
boxed=True,
animation_value_type="basic_shape::ClippingShape",
flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",

View file

@ -2209,13 +2209,9 @@ impl PropertyDeclaration {
// This probably affects some test results.
let value = match input.try(CSSWideKeyword::parse) {
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
Err(()) => match crate::custom_properties::SpecifiedValue::parse(input) {
Ok(value) => CustomDeclarationValue::Value(value),
Err(e) => return Err(StyleParseErrorKind::new_invalid(
format!("--{}", property_name),
e,
)),
}
Err(()) => CustomDeclarationValue::Value(
crate::custom_properties::SpecifiedValue::parse(input)?
),
};
declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
name: property_name,
@ -2236,19 +2232,11 @@ impl PropertyDeclaration {
.or_else(|err| {
while let Ok(_) = input.next() {} // Look for var() after the error.
if !input.seen_var_or_env_functions() {
return Err(StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
err,
));
return Err(err);
}
input.reset(&start);
let (first_token_type, css) =
crate::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
e,
)
})?;
crate::custom_properties::parse_non_custom_with_var(input)?;
Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
id,
value: Arc::new(UnparsedValue {
@ -2287,20 +2275,12 @@ impl PropertyDeclaration {
id.parse_into(declarations, context, input).or_else(|err| {
while let Ok(_) = input.next() {} // Look for var() after the error.
if !input.seen_var_or_env_functions() {
return Err(StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
err,
));
return Err(err);
}
input.reset(&start);
let (first_token_type, css) =
crate::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
StyleParseErrorKind::new_invalid(
non_custom_id.unwrap().name(),
e,
)
})?;
crate::custom_properties::parse_non_custom_with_var(input)?;
let unparsed = Arc::new(UnparsedValue {
css: css.into_owned(),
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::CSSFloat;
use app_units::Au;
use euclid::Point2D;
use smallvec::SmallVec;
use std::cmp;
@ -241,16 +240,10 @@ impl Animate for Au {
}
}
impl<T> Animate for Point2D<T>
where
T: Animate,
{
impl<T: Animate> Animate for Box<T> {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Point2D::new(
self.x.animate(&other.x, procedure)?,
self.y.animate(&other.y, procedure)?,
))
Ok(Box::new((**self).animate(&other, 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]>
where
T: ToAnimatedValue,
@ -407,3 +460,17 @@ where
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>;
/// A computed basic shape.
pub type BasicShape = generic::BasicShape<
pub type BasicShape = generic::GenericBasicShape<
LengthPercentage,
LengthPercentage,
LengthPercentage,
@ -41,7 +41,7 @@ pub type Ellipse =
generic::Ellipse<LengthPercentage, LengthPercentage, NonNegativeLengthPercentage>;
/// The computed value of `ShapeRadius`
pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
impl ToCss for Circle {
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!(bool);
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(...)
pub type ClipRect = generics::ClipRect<LengthOrAuto>;

View file

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

View file

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

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::transform::IsParallelTo;
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::parser::{Parse, ParserContext};
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>`
pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;

View file

@ -29,16 +29,14 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
ToResolvedValue,
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 {
/// 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.
#[inline]
pub fn commands(&self) -> &[PathCommand] {
@ -46,9 +44,9 @@ impl SVGPathData {
&self.0
}
/// Create a normalized copy of this path by converting each relative command to an absolute
/// command.
fn normalize(&self) -> Self {
/// Create a normalized copy of this path by converting each relative
/// command to an absolute command.
fn normalize(&self) -> Box<[PathCommand]> {
let mut state = PathTraversalState {
subpath_start: CoordPair::new(0.0, 0.0),
pos: CoordPair::new(0.0, 0.0),
@ -58,7 +56,7 @@ impl SVGPathData {
.iter()
.map(|seg| seg.normalize(&mut state))
.collect::<Vec<_>>();
SVGPathData(result.into_boxed_slice())
result.into_boxed_slice()
}
}
@ -71,7 +69,7 @@ impl ToCss for SVGPathData {
dest.write_char('"')?;
{
let mut writer = SequenceWriter::new(dest, " ");
for command in self.0.iter() {
for command in self.commands() {
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(());
}
// 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
.normalize()
.0
.iter()
.zip(other.normalize().0.iter())
.zip(other.normalize().iter())
.map(|(a, b)| a.animate(&b, procedure))
.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(());
}
self.normalize()
.0
.iter()
.zip(other.normalize().0.iter())
.zip(other.normalize().iter())
.map(|(this, other)| this.compute_squared_distance(&other))
.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
// / desktop_zoom => CSSPixel
pub mod arc_slice;
pub mod specified_value_info;
#[macro_use]
pub mod values;
#[macro_use]
pub mod viewport;
pub mod owned_slice;
pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
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.
use crate::arc_slice::ArcSlice;
use servo_arc::Arc;
use std::ops::Range;
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!(Arc<T>);
impl_generic_specified_value_info!(StdArc<T>);
impl_generic_specified_value_info!(ArcSlice<T>);
impl_generic_specified_value_info!(Range<Idx>);
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
/// 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
T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>,