diff options
Diffstat (limited to 'components')
124 files changed, 3319 insertions, 3191 deletions
diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 17f946f7187..2c0f6d612e1 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -60,10 +60,10 @@ use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect}; use style::properties::{style_structs, ComputedValues}; use style::servo::restyle_damage::ServoRestyleDamage; use style::values::computed::effects::SimpleShadow; -use style::values::computed::image::{Image, ImageLayer}; +use style::values::computed::image::Image; use style::values::computed::{ClipRectOrAuto, Gradient, LengthOrAuto}; use style::values::generics::background::BackgroundSize; -use style::values::generics::image::{GradientKind, PaintWorklet}; +use style::values::generics::image::PaintWorklet; use style::values::specified::ui::CursorKind; use style::values::RGBA; use style_traits::ToCss; @@ -732,12 +732,8 @@ impl Fragment { // http://www.w3.org/TR/CSS21/colors.html#background let background = style.get_background(); for (i, background_image) in background.background_image.0.iter().enumerate().rev() { - let background_image = match *background_image { - ImageLayer::None => continue, - ImageLayer::Image(ref image) => image, - }; - match *background_image { + Image::None => {}, Image::Gradient(ref gradient) => { self.build_display_list_for_background_gradient( state, @@ -975,15 +971,15 @@ impl Fragment { display_list_section, ); - let display_item = match gradient.kind { - GradientKind::Linear(angle_or_corner) => { - let (gradient, stops) = gradient::linear( - style, - placement.tile_size, - &gradient.items[..], - angle_or_corner, - gradient.repeating, - ); + let display_item = match gradient { + Gradient::Linear { + ref direction, + ref items, + ref repeating, + compat_mode: _, + } => { + let (gradient, stops) = + gradient::linear(style, placement.tile_size, items, *direction, *repeating); let item = webrender_api::GradientDisplayItem { gradient, bounds: placement.bounds.to_f32_px(), @@ -993,14 +989,20 @@ impl Fragment { }; DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops)) }, - GradientKind::Radial(ref shape, ref center) => { + Gradient::Radial { + ref shape, + ref position, + ref items, + ref repeating, + compat_mode: _, + } => { let (gradient, stops) = gradient::radial( style, placement.tile_size, - &gradient.items[..], + items, shape, - center, - gradient.repeating, + position, + *repeating, ); let item = webrender_api::RadialGradientDisplayItem { gradient, @@ -1011,6 +1013,7 @@ impl Fragment { }; DisplayItem::RadialGradient(CommonDisplayItem::with_data(base, item, stops)) }, + Gradient::Conic { .. } => unimplemented!(), }; state.add_display_item(display_item); }); @@ -1122,22 +1125,20 @@ impl Fragment { let border_radius = border::radii(bounds, border_style_struct); let border_widths = border.to_physical(style.writing_mode); - if let ImageLayer::Image(ref image) = border_style_struct.border_image_source { - if self - .build_display_list_for_border_image( - state, - style, - base.clone(), - bounds, - image, - border_widths, - ) - .is_some() - { - return; - } - // Fallback to rendering a solid border. + if self + .build_display_list_for_border_image( + state, + style, + base.clone(), + bounds, + &border_style_struct.border_image_source, + border_widths, + ) + .is_some() + { + return; } + if border_widths == SideOffsets2D::zero() { return; } @@ -1224,30 +1225,37 @@ impl Fragment { height = image.height; NinePatchBorderSource::Image(image.key?) }, - Image::Gradient(ref gradient) => match gradient.kind { - GradientKind::Linear(angle_or_corner) => { - let (wr_gradient, linear_stops) = gradient::linear( - style, - border_image_area, - &gradient.items[..], - angle_or_corner, - gradient.repeating, - ); + Image::Gradient(ref gradient) => match **gradient { + Gradient::Linear { + ref direction, + ref items, + ref repeating, + compat_mode: _, + } => { + let (wr_gradient, linear_stops) = + gradient::linear(style, border_image_area, items, *direction, *repeating); stops = linear_stops; NinePatchBorderSource::Gradient(wr_gradient) }, - GradientKind::Radial(ref shape, ref center) => { + Gradient::Radial { + ref shape, + ref position, + ref items, + ref repeating, + compat_mode: _, + } => { let (wr_gradient, radial_stops) = gradient::radial( style, border_image_area, - &gradient.items[..], + items, shape, - center, - gradient.repeating, + position, + *repeating, ); stops = radial_stops; NinePatchBorderSource::RadialGradient(wr_gradient) }, + Gradient::Conic { .. } => unimplemented!(), }, _ => return None, }; diff --git a/components/layout/display_list/gradient.rs b/components/layout/display_list/gradient.rs index e683bbb963c..b816bca753f 100644 --- a/components/layout/display_list/gradient.rs +++ b/components/layout/display_list/gradient.rs @@ -7,8 +7,8 @@ use app_units::Au; use euclid::default::{Point2D, Size2D, Vector2D}; use style::properties::ComputedValues; use style::values::computed::image::{EndingShape, LineDirection}; -use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position}; -use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent}; +use style::values::computed::{Angle, Color, LengthPercentage, Percentage, Position}; +use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent}; use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient}; /// A helper data structure for gradients. @@ -78,7 +78,7 @@ fn ellipse_size_keyword( fn convert_gradient_stops( style: &ComputedValues, - gradient_items: &[GradientItem], + gradient_items: &[GradientItem<Color, LengthPercentage>], total_length: Au, ) -> GradientBuilder { // Determine the position of each stop per CSS-IMAGES § 3.4. @@ -237,7 +237,7 @@ fn position_to_offset(position: &LengthPercentage, total_length: Au) -> f32 { pub fn linear( style: &ComputedValues, size: Size2D<Au>, - stops: &[GradientItem], + stops: &[GradientItem<Color, LengthPercentage>], direction: LineDirection, repeating: bool, ) -> (Gradient, Vec<GradientStop>) { @@ -303,7 +303,7 @@ pub fn linear( pub fn radial( style: &ComputedValues, size: Size2D<Au>, - stops: &[GradientItem], + stops: &[GradientItem<Color, LengthPercentage>], shape: &EndingShape, center: &Position, repeating: bool, diff --git a/components/layout_2020/display_list/gradient.rs b/components/layout_2020/display_list/gradient.rs index f1e5c1a3c1a..09f5be610c2 100644 --- a/components/layout_2020/display_list/gradient.rs +++ b/components/layout_2020/display_list/gradient.rs @@ -4,9 +4,8 @@ use style::properties::ComputedValues; use style::values::computed::image::{EndingShape, Gradient, LineDirection}; -use style::values::computed::{GradientItem, Length, Position}; -use style::values::generics::image::GenericGradientKind as Kind; -use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent}; +use style::values::computed::{Color, Length, LengthPercentage, Position}; +use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent}; use webrender_api::{self as wr, units}; pub(super) fn build( @@ -15,36 +14,51 @@ pub(super) fn build( layer: &super::background::BackgroundLayer, builder: &mut super::DisplayListBuilder, ) { - let extend_mode = if gradient.repeating { - wr::ExtendMode::Repeat - } else { - wr::ExtendMode::Clamp - }; - match &gradient.kind { - Kind::Linear(line_direction) => build_linear( + match gradient { + Gradient::Linear { + ref items, + ref direction, + ref repeating, + compat_mode: _, + } => build_linear( style, - &gradient.items, - line_direction, - extend_mode, + items, + direction, + if *repeating { + wr::ExtendMode::Repeat + } else { + wr::ExtendMode::Clamp + }, &layer, builder, ), - Kind::Radial(ending_shape, center) => build_radial( + Gradient::Radial { + ref shape, + ref position, + ref items, + ref repeating, + compat_mode: _, + } => build_radial( style, - &gradient.items, - ending_shape, - center, - extend_mode, + items, + shape, + position, + if *repeating { + wr::ExtendMode::Repeat + } else { + wr::ExtendMode::Clamp + }, &layer, builder, ), + Gradient::Conic { .. } => unimplemented!(), } } /// https://drafts.csswg.org/css-images-3/#linear-gradients pub(super) fn build_linear( style: &ComputedValues, - items: &[GradientItem], + items: &[GradientItem<Color, LengthPercentage>], line_direction: &LineDirection, extend_mode: wr::ExtendMode, layer: &super::background::BackgroundLayer, @@ -144,7 +158,7 @@ pub(super) fn build_linear( /// https://drafts.csswg.org/css-images-3/#radial-gradients pub(super) fn build_radial( style: &ComputedValues, - items: &[GradientItem], + items: &[GradientItem<Color, LengthPercentage>], shape: &EndingShape, center: &Position, extend_mode: wr::ExtendMode, @@ -244,7 +258,7 @@ pub(super) fn build_radial( /// https://drafts.csswg.org/css-images-4/#color-stop-fixup fn fixup_stops( style: &ComputedValues, - items: &[GradientItem], + items: &[GradientItem<Color, LengthPercentage>], gradient_line_length: Length, ) -> Vec<wr::GradientStop> { // Remove color transititon hints, which are not supported yet. diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index dccee5ea71a..af0797d30b6 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -333,7 +333,7 @@ impl<'a> BuilderForBoxFragment<'a> { } fn build_background(&mut self, builder: &mut DisplayListBuilder) { - use style::values::computed::image::{Image, ImageLayer}; + use style::values::computed::image::Image; let b = self.fragment.style.get_background(); let background_color = self.fragment.style.resolve_color(b.background_color); if background_color.alpha > 0 { @@ -345,85 +345,80 @@ impl<'a> BuilderForBoxFragment<'a> { builder.wr.push_rect(&common, rgba(background_color)) } // Reverse because the property is top layer first, we want to paint bottom layer first. - for (index, layer) in b.background_image.0.iter().enumerate().rev() { - match layer { - ImageLayer::None => {}, - ImageLayer::Image(image) => match image { - Image::Gradient(gradient) => { - let intrinsic = IntrinsicSizes { - width: None, - height: None, - ratio: None, - }; - if let Some(layer) = - &background::layout_layer(self, builder, index, intrinsic) - { - gradient::build(&self.fragment.style, gradient, layer, builder) - } - }, - Image::Url(image_url) => { - // FIXME: images won’t always have in intrinsic width or height - // when support for SVG is added. - // Or a WebRender `ImageKey`, for that matter. - let (width, height, key) = match image_url.url() { - Some(url) => { - match builder.context.get_webrender_image_for_url( - self.fragment.tag, - url.clone(), - UsePlaceholder::No, - ) { - Some(WebRenderImageInfo { - width, - height, - key: Some(key), - }) => (width, height, key), - _ => continue, - } - }, - None => continue, - }; - - // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution - let dppx = 1.0; - - let intrinsic = IntrinsicSizes { - width: Some(Length::new(width as f32 / dppx)), - height: Some(Length::new(height as f32 / dppx)), - // FIXME https://github.com/w3c/csswg-drafts/issues/4572 - ratio: Some(width as f32 / height as f32), - }; - - if let Some(layer) = - background::layout_layer(self, builder, index, intrinsic) - { - let image_rendering = - image_rendering(self.fragment.style.clone_image_rendering()); - if layer.repeat { - builder.wr.push_repeating_image( - &layer.common, - layer.bounds, - layer.tile_size, - layer.tile_spacing, - image_rendering, - wr::AlphaType::PremultipliedAlpha, - key, - wr::ColorF::WHITE, - ) - } else { - builder.wr.push_image( - &layer.common, - layer.bounds, - image_rendering, - wr::AlphaType::PremultipliedAlpha, - key, - wr::ColorF::WHITE, - ) + for (index, image) in b.background_image.0.iter().enumerate().rev() { + match image { + Image::None => {}, + Image::Gradient(ref gradient) => { + let intrinsic = IntrinsicSizes { + width: None, + height: None, + ratio: None, + }; + if let Some(layer) = &background::layout_layer(self, builder, index, intrinsic) + { + gradient::build(&self.fragment.style, &gradient, layer, builder) + } + }, + Image::Url(ref image_url) => { + // FIXME: images won’t always have in intrinsic width or height + // when support for SVG is added. + // Or a WebRender `ImageKey`, for that matter. + let (width, height, key) = match image_url.url() { + Some(url) => { + match builder.context.get_webrender_image_for_url( + self.fragment.tag, + url.clone(), + UsePlaceholder::No, + ) { + Some(WebRenderImageInfo { + width, + height, + key: Some(key), + }) => (width, height, key), + _ => continue, } + }, + None => continue, + }; + + // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution + let dppx = 1.0; + + let intrinsic = IntrinsicSizes { + width: Some(Length::new(width as f32 / dppx)), + height: Some(Length::new(height as f32 / dppx)), + // FIXME https://github.com/w3c/csswg-drafts/issues/4572 + ratio: Some(width as f32 / height as f32), + }; + + if let Some(layer) = background::layout_layer(self, builder, index, intrinsic) { + let image_rendering = + image_rendering(self.fragment.style.clone_image_rendering()); + if layer.repeat { + builder.wr.push_repeating_image( + &layer.common, + layer.bounds, + layer.tile_size, + layer.tile_spacing, + image_rendering, + wr::AlphaType::PremultipliedAlpha, + key, + wr::ColorF::WHITE, + ) + } else { + builder.wr.push_image( + &layer.common, + layer.bounds, + image_rendering, + wr::AlphaType::PremultipliedAlpha, + key, + wr::ColorF::WHITE, + ) } - }, - // Gecko-only value, represented as a (boxed) empty enum on non-Gecko. - Image::Rect(rect) => match **rect {}, + } }, + // Gecko-only value, represented as a (boxed) empty enum on non-Gecko. + Image::Rect(ref rect) => match **rect {}, } } } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 040b4c3f3cf..c129582a358 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -303,7 +303,11 @@ impl InlineFormattingContext { panic!("display:none does not generate an abspos box") }, }; - let hoisted_box = box_.clone().to_hoisted(initial_start_corner, tree_rank); + let hoisted_box = AbsolutelyPositionedBox::to_hoisted( + box_.clone(), + initial_start_corner, + tree_rank, + ); let hoisted_fragment = hoisted_box.fragment.clone(); ifc.push_hoisted_box_to_positioning_context(hoisted_box); ifc.current_nesting_level.fragments_so_far.push( @@ -786,7 +790,7 @@ impl TextRun { glyphs, text_decoration_line: ifc.current_nesting_level.text_decoration_line, })); - if runs.is_empty() { + if runs.as_slice().is_empty() { break; } else { // New line diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 33bba78de96..eb4a03be62e 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -315,7 +315,8 @@ impl BlockLevelBox { )) }, BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { - let hoisted_box = box_.clone().to_hoisted(Vec2::zero(), tree_rank); + let hoisted_box = + AbsolutelyPositionedBox::to_hoisted(box_.clone(), Vec2::zero(), tree_rank); let hoisted_fragment = hoisted_box.fragment.clone(); positioning_context.push(hoisted_box); Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment { diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 6b7a08ec29b..be5008035a5 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -3,8 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #![deny(unsafe_code)] -#![feature(arbitrary_self_types)] -#![feature(exact_size_is_empty)] #[macro_use] extern crate serde; diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 899b2b39e61..cb71ad521c4 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -100,7 +100,7 @@ impl AbsolutelyPositionedBox { } pub(crate) fn to_hoisted( - self: Arc<Self>, + self_: Arc<Self>, initial_start_corner: Vec2<Length>, tree_rank: usize, ) -> HoistedAbsolutelyPositionedBox { @@ -124,7 +124,7 @@ impl AbsolutelyPositionedBox { } } - let box_offsets = self.contents.style.box_offsets(); + let box_offsets = self_.contents.style.box_offsets(); HoistedAbsolutelyPositionedBox { tree_rank, box_offsets: Vec2 { @@ -140,7 +140,7 @@ impl AbsolutelyPositionedBox { ), }, fragment: ArcRefCell::new(None), - absolutely_positioned_box: self, + absolutely_positioned_box: self_, } } } diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 064ceb5148e..1ec4c92fcf0 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -914,10 +914,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { None } @@ -1441,11 +1437,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - debug!("ServoThreadSafeLayoutElement::exported_part called"); - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { debug!("ServoThreadSafeLayoutElement::imported_part called"); None diff --git a/components/layout_thread_2020/dom_wrapper.rs b/components/layout_thread_2020/dom_wrapper.rs index 92a9f3f005b..7b1da3454a0 100644 --- a/components/layout_thread_2020/dom_wrapper.rs +++ b/components/layout_thread_2020/dom_wrapper.rs @@ -922,10 +922,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { None } @@ -1447,11 +1443,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - debug!("ServoThreadSafeLayoutElement::exported_part called"); - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { debug!("ServoThreadSafeLayoutElement::imported_part called"); None diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 3c9555ba659..021757a4ad1 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -3992,8 +3992,8 @@ class CGMemberJITInfo(CGThing): protoID: PrototypeList::ID::${name} as u16, }, __bindgen_anon_3: JSJitInfo__bindgen_ty_3 { depth: ${depth} }, - _bitfield_1: unsafe { - mem::transmute(new_jsjitinfo_bitfield_1!( + _bitfield_1: __BindgenBitfieldUnit::new( + new_jsjitinfo_bitfield_1!( JSJitInfo_OpType::${opType} as u8, JSJitInfo_AliasSet::${aliasSet} as u8, JSValueType::${returnType} as u8, @@ -4004,8 +4004,8 @@ class CGMemberJITInfo(CGThing): ${isLazilyCachedInSlot}, ${isTypedMethod}, ${slotIndex}, - )) - }, + ).to_ne_bytes() + ), } """, opName=opName, @@ -5988,6 +5988,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::error::throw_type_error', 'js::error::throw_internal_error', 'js::rust::wrappers::Call', + 'js::jsapi::__BindgenBitfieldUnit', 'js::jsapi::CallArgs', 'js::jsapi::CurrentGlobalOrNull', 'js::rust::wrappers::GetPropertyKeys', diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 4f4d376e951..59f9b964564 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -383,7 +383,7 @@ where #[allow(unrooted_must_root)] pub unsafe fn from_box(value: Box<T>) -> Self { Self { - ptr: Box::into_raw_non_null(value), + ptr: Box::leak(value).into(), } } } diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 18a41857923..812434fd180 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -17,7 +17,9 @@ use crate::stylesheet_loader::StylesheetLoader; use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::Locked; -use style::stylesheets::{CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError}; +use style::stylesheets::{ + AllowImportRules, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError, +}; #[allow(unsafe_code)] unsafe_no_jsmanaged_fields!(RulesSource); @@ -116,6 +118,7 @@ impl CSSRuleList { index, nested, Some(&loader), + AllowImportRules::Yes, ) })?; diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 2fd6780cab0..baccc4e65aa 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -682,10 +682,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> { hints.push(from_declaration( shared_lock, PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue( - vec![specified::ImageLayer::Image(specified::Image::for_cascade( - url.into(), - ))] - .into(), + vec![specified::Image::for_cascade(url.into())].into(), )), )); } @@ -3164,10 +3161,6 @@ impl<'a> SelectorsElement for DomRoot<Element> { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { None } diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index b4118e234c3..9e04585168c 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -41,7 +41,6 @@ use js::error::throw_type_error; use js::rust::HandleValue; use profile_traits::ipc; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; -use servo_config::pref; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; const DEFAULT_WIDTH: u32 = 300; @@ -222,7 +221,8 @@ impl HTMLCanvasElement { cx: JSContext, options: HandleValue, ) -> Option<DomRoot<WebGL2RenderingContext>> { - if !pref!(dom.webgl2.enabled) { + if !WebGL2RenderingContext::is_webgl2_enabled(cx, self.global().reflector().get_jsobject()) + { return None; } if let Some(ctx) = self.context() { diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index afa99d8d86b..6d10b0838f7 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -26,7 +26,7 @@ use servo_arc::Arc; use std::cell::Cell; use style::media_queries::MediaList; use style::parser::ParserContext as CssParserContext; -use style::stylesheets::{CssRuleType, Origin, Stylesheet}; +use style::stylesheets::{AllowImportRules, CssRuleType, Origin, Stylesheet}; use style_traits::ParsingMode; #[dom_struct] @@ -119,6 +119,7 @@ impl HTMLStyleElement { css_error_reporter, doc.quirks_mode(), self.line_number as u32, + AllowImportRules::Yes, ); let sheet = Arc::new(sheet); diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 62af18d1002..8e967fbcb86 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -16,6 +16,7 @@ use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::webglactiveinfo::WebGLActiveInfo; @@ -50,12 +51,14 @@ use ipc_channel::ipc; use js::jsapi::{JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value}; use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; -use js::rust::CustomAutoRooterGuard; +use js::rust::{CustomAutoRooterGuard, HandleObject}; use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array}; use script_layout_interface::HTMLCanvasDataSource; +use servo_config::pref; use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; +use url::Host; #[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] @@ -178,8 +181,26 @@ impl WebGL2RenderingContext { WebGL2RenderingContext::new_inherited(window, canvas, size, attrs) .map(|ctx| reflect_dom_object(Box::new(ctx), window)) } + + #[allow(unsafe_code)] + pub fn is_webgl2_enabled(_cx: JSContext, global: HandleObject) -> bool { + if pref!(dom.webgl2.enabled) { + return true; + } + + let global = unsafe { GlobalScope::from_object(global.get()) }; + let origin = global.origin(); + let host = origin.host(); + WEBGL2_ORIGINS + .iter() + .any(|origin| host == Host::parse(origin).ok().as_ref()) + } } +/// List of domains for which WebGL 2 is enabled automatically, regardless +/// of the status of the dom.webgl2.enabled preference. +static WEBGL2_ORIGINS: &[&str] = &["www.servoexperiments.com"]; + impl WebGL2RenderingContext { pub fn recreate(&self, size: Size2D<u32>) { self.base.recreate(size) diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index e7f94cb9a3c..f6278370cce 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -542,7 +542,7 @@ interface mixin WebGL2RenderingContextBase void bindVertexArray(WebGLVertexArrayObject? array); }; -[Exposed=Window, Pref="dom.webgl2.enabled"] +[Exposed=Window, Func="WebGL2RenderingContext::is_webgl2_enabled"] interface WebGL2RenderingContext { }; diff --git a/components/script/dom/xrview.rs b/components/script/dom/xrview.rs index 14309dfc99c..8aa0b0adc58 100644 --- a/components/script/dom/xrview.rs +++ b/components/script/dom/xrview.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods}; +use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::utils::create_typed_array; @@ -24,17 +25,24 @@ pub struct XRView { proj: Heap<*mut JSObject>, #[ignore_malloc_size_of = "mozjs"] view: Heap<*mut JSObject>, + proj_array: Vec<f32>, transform: Dom<XRRigidTransform>, } impl XRView { - fn new_inherited(session: &XRSession, transform: &XRRigidTransform, eye: XREye) -> XRView { + fn new_inherited( + session: &XRSession, + transform: &XRRigidTransform, + eye: XREye, + proj_array: Vec<f32>, + ) -> XRView { XRView { reflector_: Reflector::new(), session: Dom::from_ref(session), eye, proj: Heap::default(), view: Heap::default(), + proj_array, transform: Dom::from_ref(transform), } } @@ -55,13 +63,18 @@ impl XRView { let transform = pose.pre_transform(&offset); let transform = XRRigidTransform::new(global, cast_transform(transform)); + // row_major since euclid uses row vectors + let proj = view.projection.to_row_major_array(); let ret = reflect_dom_object( - Box::new(XRView::new_inherited(session, &transform, eye)), + Box::new(XRView::new_inherited( + session, + &transform, + eye, + (&proj).to_vec(), + )), global, ); - // row_major since euclid uses row vectors - let proj = view.projection.to_row_major_array(); let cx = global.get_cx(); create_typed_array(cx, &proj, &ret.proj); ret @@ -80,6 +93,10 @@ impl XRViewMethods for XRView { /// https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix fn ProjectionMatrix(&self, _cx: JSContext) -> NonNull<JSObject> { + if self.proj.get().is_null() { + let cx = self.global().get_cx(); + create_typed_array(cx, &self.proj_array, &self.proj); + } NonNull::new(self.proj.get()).unwrap() } diff --git a/components/script/lib.rs b/components/script/lib.rs index 07090f4716b..4eeca229e01 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -2,12 +2,9 @@ * 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/. */ -#![feature(box_into_raw_non_null)] #![feature(const_fn)] -#![feature(const_transmute)] #![feature(core_intrinsics)] #![feature(drain_filter)] -#![feature(inner_deref)] #![feature(plugin)] #![feature(register_tool)] #![deny(unsafe_code)] diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs index 651c0815f9f..374c650986d 100644 --- a/components/script_layout_interface/lib.rs +++ b/components/script_layout_interface/lib.rs @@ -7,7 +7,6 @@ //! to depend on script. #![deny(unsafe_code)] -#![feature(box_into_raw_non_null)] #[macro_use] extern crate html5ever; diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 8993a5ea003..e8fd8afebca 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -676,19 +676,22 @@ where None => return false, }; - loop { - let outer_host = host.containing_shadow_host(); - if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host { - break; + let current_host = context.shared.current_host; + if current_host != Some(host.opaque()) { + loop { + let outer_host = host.containing_shadow_host(); + if outer_host.as_ref().map(|h| h.opaque()) == current_host { + break; + } + let outer_host = match outer_host { + Some(h) => h, + None => return false, + }; + // TODO(emilio): if worth it, we could early return if + // host doesn't have the exportparts attribute. + hosts.push(host); + host = outer_host; } - let outer_host = match outer_host { - Some(h) => h, - None => return false, - }; - // TODO(emilio): if worth it, we could early return if - // host doesn't have the exportparts attribute. - hosts.push(host); - host = outer_host; } // Translate the part into the right scope. diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 92a5b039b90..0f7cb9c6055 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -2013,27 +2013,48 @@ where input.skip_whitespace(); let mut empty = true; - if !parse_type_selector(parser, input, builder)? { - if let Some(url) = parser.default_namespace() { - // If there was no explicit type selector, but there is a - // default namespace, there is an implicit "<defaultns>|*" type - // selector. - builder.push_simple_selector(Component::DefaultNamespace(url)) - } - } else { + if parse_type_selector(parser, input, builder)? { empty = false; } let mut state = SelectorParsingState::empty(); loop { - let parse_result = match parse_one_simple_selector(parser, input, state)? { + let result = match parse_one_simple_selector(parser, input, state)? { None => break, Some(result) => result, }; + if empty { + if let Some(url) = parser.default_namespace() { + // If there was no explicit type selector, but there is a + // default namespace, there is an implicit "<defaultns>|*" type + // selector. Except for :host, where we ignore it. + // + // https://drafts.csswg.org/css-scoping/#host-element-in-tree: + // + // When considered within its own shadow trees, the shadow + // host is featureless. Only the :host, :host(), and + // :host-context() pseudo-classes are allowed to match it. + // + // https://drafts.csswg.org/selectors-4/#featureless: + // + // A featureless element does not match any selector at all, + // except those it is explicitly defined to match. If a + // given selector is allowed to match a featureless element, + // it must do so while ignoring the default namespace. + // + if !matches!( + result, + SimpleSelectorParseResult::SimpleSelector(Component::Host(..)) + ) { + builder.push_simple_selector(Component::DefaultNamespace(url)); + } + } + } + empty = false; - match parse_result { + match result { SimpleSelectorParseResult::SimpleSelector(s) => { builder.push_simple_selector(s); }, diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index d6198c5a5f5..ac90fa1f00c 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -117,13 +117,6 @@ pub trait Element: Sized + Clone + Debug { case_sensitivity: CaseSensitivity, ) -> bool; - /// Returns the mapping from the `exportparts` attribute in the regular - /// direction, that is, inner-tree -> outer-tree. - fn exported_part( - &self, - name: &<Self::Impl as SelectorImpl>::PartName, - ) -> Option<<Self::Impl as SelectorImpl>::PartName>; - /// Returns the mapping from the `exportparts` attribute in the reverse /// direction, that is, in an outer-tree -> inner-tree direction. fn imported_part( diff --git a/components/style/animation.rs b/components/style/animation.rs index d734acd6d7b..402c281a388 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -12,14 +12,13 @@ use crate::bezier::Bezier; use crate::context::SharedStyleContext; use crate::dom::{OpaqueNode, TElement}; use crate::font_metrics::FontMetricsProvider; -use crate::properties::animated_properties::AnimatedProperty; +use crate::properties::animated_properties::{AnimatedProperty, TransitionPropertyIteration}; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; use crate::properties::{self, CascadeMode, ComputedValues, LonghandId}; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use crate::stylesheets::Origin; use crate::timer::Timer; -use crate::values::computed::box_::TransitionProperty; use crate::values::computed::Time; use crate::values::computed::TimingFunction; use crate::values::generics::box_::AnimationIterationCount; @@ -271,52 +270,6 @@ impl PropertyAnimation { self.property.name() } - /// Creates a new property animation for the given transition index and old - /// and new styles. Any number of animations may be returned, from zero (if - /// the property did not animate) to one (for a single transition property) - /// to arbitrarily many (for `all`). - pub fn from_transition( - transition_index: usize, - old_style: &ComputedValues, - new_style: &mut ComputedValues, - ) -> Vec<PropertyAnimation> { - let mut result = vec![]; - let box_style = new_style.get_box(); - let transition_property = box_style.transition_property_at(transition_index); - let timing_function = box_style.transition_timing_function_mod(transition_index); - let duration = box_style.transition_duration_mod(transition_index); - - match transition_property { - TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => result, - TransitionProperty::Shorthand(ref shorthand_id) => shorthand_id - .longhands() - .filter_map(|longhand| { - PropertyAnimation::from_longhand( - longhand, - timing_function, - duration, - old_style, - new_style, - ) - }) - .collect(), - TransitionProperty::Longhand(longhand_id) => { - let animation = PropertyAnimation::from_longhand( - longhand_id, - timing_function, - duration, - old_style, - new_style, - ); - - if let Some(animation) = animation { - result.push(animation); - } - result - }, - } - } - fn from_longhand( longhand: LonghandId, timing_function: TimingFunction, @@ -414,56 +367,70 @@ pub fn start_transitions_if_applicable( running_and_expired_transitions: &[PropertyAnimation], ) -> bool { let mut had_animations = false; - for i in 0..new_style.get_box().transition_property_count() { - // Create any property animations, if applicable. - let property_animations = - PropertyAnimation::from_transition(i, old_style, Arc::make_mut(new_style)); - for property_animation in property_animations { - // Set the property to the initial value. - // - // NB: get_mut is guaranteed to succeed since we called make_mut() - // above. - property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0); + let transitions: Vec<TransitionPropertyIteration> = new_style.transition_properties().collect(); + for transition in &transitions { + let property_animation = match PropertyAnimation::from_longhand( + transition.longhand_id, + new_style + .get_box() + .transition_timing_function_mod(transition.index), + new_style + .get_box() + .transition_duration_mod(transition.index), + old_style, + Arc::make_mut(new_style), + ) { + Some(property_animation) => property_animation, + None => continue, + }; - // Per [1], don't trigger a new transition if the end state for that - // transition is the same as that of a transition that's already - // running on the same node. - // - // [1]: https://drafts.csswg.org/css-transitions/#starting + // Set the property to the initial value. + // + // NB: get_mut is guaranteed to succeed since we called make_mut() + // above. + property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0); + + // Per [1], don't trigger a new transition if the end state for that + // transition is the same as that of a transition that's already + // running on the same node. + // + // [1]: https://drafts.csswg.org/css-transitions/#starting + debug!( + "checking {:?} for matching end value", + running_and_expired_transitions + ); + if running_and_expired_transitions + .iter() + .any(|animation| animation.has_the_same_end_value_as(&property_animation)) + { debug!( - "checking {:?} for matching end value", - running_and_expired_transitions + "Not initiating transition for {}, other transition \ + found with the same end value", + property_animation.property_name() ); - if running_and_expired_transitions - .iter() - .any(|animation| animation.has_the_same_end_value_as(&property_animation)) - { - debug!( - "Not initiating transition for {}, other transition \ - found with the same end value", - property_animation.property_name() - ); - continue; - } + continue; + } - // Kick off the animation. - debug!("Kicking off transition of {:?}", property_animation); - let box_style = new_style.get_box(); - let now = timer.seconds(); - let start_time = now + (box_style.transition_delay_mod(i).seconds() as f64); - new_animations_sender - .send(Animation::Transition( - opaque_node, - start_time, - AnimationFrame { - duration: box_style.transition_duration_mod(i).seconds() as f64, - property_animation, - }, - )) - .unwrap(); + // Kick off the animation. + debug!("Kicking off transition of {:?}", property_animation); + let box_style = new_style.get_box(); + let now = timer.seconds(); + let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64); + let duration = box_style + .transition_duration_mod(transition.index) + .seconds() as f64; + new_animations_sender + .send(Animation::Transition( + opaque_node, + start_time, + AnimationFrame { + duration, + property_animation, + }, + )) + .unwrap(); - had_animations = true; - } + had_animations = true; } had_animations diff --git a/components/style/build.rs b/components/style/build.rs index 4477e648ac9..1d5211d690b 100644 --- a/components/style/build.rs +++ b/components/style/build.rs @@ -28,11 +28,11 @@ mod build_gecko { } lazy_static! { - pub static ref PYTHON: String = env::var("PYTHON").ok().unwrap_or_else(|| { + pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python2.7.exe", "python27.exe", "python.exe"] + ["python3.exe"] } else { - ["python2.7", "python2", "python"] + ["python3"] }; for &name in &candidates { if Command::new(name) @@ -45,7 +45,7 @@ lazy_static! { } } panic!( - "Can't find python (tried {})! Try fixing PATH or setting the PYTHON env var", + "Can't find python (tried {})! Try fixing PATH or setting the PYTHON3 env var", candidates.join(", ") ) }); diff --git a/components/style/counter_style/mod.rs b/components/style/counter_style/mod.rs index d131f350393..9c4be49bb91 100644 --- a/components/style/counter_style/mod.rs +++ b/components/style/counter_style/mod.rs @@ -408,7 +408,9 @@ impl ToCss for System { } /// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol> -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)] +#[derive( + Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem, +)] #[repr(u8)] pub enum Symbol { /// <string> @@ -554,7 +556,9 @@ impl Parse for Fallback { } /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols> -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)] +#[derive( + Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem, +)] #[repr(C)] pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>); diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index 76a72276a44..d0af57886db 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -579,7 +579,8 @@ impl<'a> CustomPropertiesBuilder<'a> { match result { Ok(new_value) => Arc::new(new_value), Err(..) => { - map.remove(name); + // Don't touch the map, this has the same effect as + // making it compute to the inherited one. return; }, } @@ -653,16 +654,22 @@ impl<'a> CustomPropertiesBuilder<'a> { None => return self.inherited.cloned(), }; if self.may_have_cycles { - substitute_all(&mut map, self.device); + let inherited = self.inherited.as_ref().map(|m| &***m); + substitute_all(&mut map, inherited, self.device); } Some(Arc::new(map)) } } -/// Resolve all custom properties to either substituted or invalid. +/// Resolve all custom properties to either substituted, invalid, or unset +/// (meaning we should use the inherited value). /// /// It does cycle dependencies removal at the same time as substitution. -fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Device) { +fn substitute_all( + custom_properties_map: &mut CustomPropertiesMap, + inherited: Option<&CustomPropertiesMap>, + device: &Device, +) { // The cycle dependencies removal in this function is a variant // of Tarjan's algorithm. It is mostly based on the pseudo-code // listed in @@ -698,6 +705,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi /// all unfinished strong connected components. stack: SmallVec<[usize; 5]>, map: &'a mut CustomPropertiesMap, + /// The inherited variables. We may need to restore some if we fail + /// substitution. + inherited: Option<&'a CustomPropertiesMap>, /// to resolve the environment to substitute `env()` variables. device: &'a Device, } @@ -831,17 +841,25 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi return None; } - // Now we have shown that this variable is not in a loop, and - // all of its dependencies should have been resolved. We can - // start substitution now. + // Now we have shown that this variable is not in a loop, and all of its + // dependencies should have been resolved. We can start substitution + // now. let result = substitute_references_in_value(&value, &context.map, &context.device); - match result { Ok(computed_value) => { context.map.insert(name, Arc::new(computed_value)); }, Err(..) => { - context.map.remove(&name); + // This is invalid, reset it to the unset (inherited) value. + let inherited = context.inherited.and_then(|m| m.get(&name)).cloned(); + match inherited { + Some(computed_value) => { + context.map.insert(name, computed_value); + }, + None => { + context.map.remove(&name); + }, + }; }, } @@ -859,6 +877,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi stack: SmallVec::new(), var_info: SmallVec::new(), map: custom_properties_map, + inherited, device, }; traverse(name, &mut context); diff --git a/components/style/dom.rs b/components/style/dom.rs index d07cac998ae..c1c1f74ef68 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -522,6 +522,14 @@ pub trait TElement: { } + /// Internal iterator for the part names that this element exports for a + /// given part name. + fn each_exported_part<F>(&self, _name: &Atom, _callback: F) + where + F: FnMut(&Atom), + { + } + /// Whether a given element may generate a pseudo-element. /// /// This is useful to avoid computing, for example, pseudo styles for diff --git a/components/style/driver.rs b/components/style/driver.rs index e8dfe7cf3db..aa39f3482f4 100644 --- a/components/style/driver.rs +++ b/components/style/driver.rs @@ -89,7 +89,7 @@ pub fn traverse_dom<E, D>( // ThreadLocalStyleContext on the main thread. If the main thread // ThreadLocalStyleContext has not released its TLS borrow by that point, // we'll panic on double-borrow. - let mut maybe_tls: Option<ScopedTLS<ThreadLocalStyleContext<E>>> = None; + let mut tls_slots = None; let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context()); let mut context = StyleContext { shared: traversal.shared_context(), @@ -129,7 +129,7 @@ pub fn traverse_dom<E, D>( // depth for all the children. if pool.is_some() && discovered.len() > WORK_UNIT_MAX { let pool = pool.unwrap(); - maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool)); + let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool); let root_opaque = root.as_node().opaque(); let drain = discovered.drain(..); pool.install(|| { @@ -151,10 +151,12 @@ pub fn traverse_dom<E, D>( scope, pool, traversal, - maybe_tls.as_ref().unwrap(), + &tls, ); }); }); + + tls_slots = Some(tls.into_slots()); break; } nodes_remaining_at_current_depth = discovered.len(); @@ -164,9 +166,9 @@ pub fn traverse_dom<E, D>( // Collect statistics from thread-locals if requested. if dump_stats || report_stats { let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default()); - let parallel = maybe_tls.is_some(); - if let Some(tls) = maybe_tls { - for mut slot in tls.into_slots().into_vec() { + let parallel = tls_slots.is_some(); + if let Some(ref mut tls) = tls_slots { + for slot in tls.iter_mut() { if let Some(cx) = slot.get_mut() { aggregate += cx.statistics.clone(); } diff --git a/components/style/element_state.rs b/components/style/element_state.rs index 8165c73ef14..cf943cc3c4e 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -137,6 +137,10 @@ bitflags! { const IN_AUTOFILL_STATE = 1 << 50; /// Non-standard & undocumented. const IN_AUTOFILL_PREVIEW_STATE = 1 << 51; + /// :focus-visible + /// + /// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo + const IN_FOCUS_VISIBLE_STATE = 1 << 52; } } diff --git a/components/style/encoding_support.rs b/components/style/encoding_support.rs index 5544487179f..1ba92953bb9 100644 --- a/components/style/encoding_support.rs +++ b/components/style/encoding_support.rs @@ -10,7 +10,7 @@ use crate::context::QuirksMode; use crate::error_reporting::ParseErrorReporter; use crate::media_queries::MediaList; use crate::shared_lock::SharedRwLock; -use crate::stylesheets::{Origin, Stylesheet, StylesheetLoader, UrlExtraData}; +use crate::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetLoader, UrlExtraData}; use cssparser::{stylesheet_encoding, EncodingSupport}; use servo_arc::Arc; use std::borrow::Cow; @@ -78,6 +78,7 @@ impl Stylesheet { error_reporter, quirks_mode, 0, + AllowImportRules::Yes, ) } @@ -100,6 +101,7 @@ impl Stylesheet { stylesheet_loader, error_reporter, 0, + AllowImportRules::Yes, ) } } diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 3deaeacadc8..ea3700a3235 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -10,244 +10,9 @@ #![allow(unsafe_code)] -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::{self, Matrix4x4Components}; -use crate::gecko_bindings::structs::{nsStyleImage, nsresult}; +use crate::gecko_bindings::structs::{nsresult, Matrix4x4Components}; use crate::stylesheets::RulesMutateError; use crate::values::computed::transform::Matrix3D; -use crate::values::computed::{Gradient, Image, TextAlign}; -use crate::values::generics::image::GenericImage; -use crate::values::generics::rect::Rect; - -impl nsStyleImage { - /// Set a given Servo `Image` value into this `nsStyleImage`. - pub fn set(&mut self, image: Image) { - match image { - GenericImage::Gradient(boxed_gradient) => self.set_gradient(boxed_gradient), - GenericImage::Url(ref url) => unsafe { - bindings::Gecko_SetLayerImageImageValue(self, url); - }, - GenericImage::Rect(ref image_rect) => { - unsafe { - bindings::Gecko_SetLayerImageImageValue(self, &image_rect.url); - bindings::Gecko_InitializeImageCropRect(self); - - // Set CropRect - let ref mut rect = *self.mCropRect.mPtr; - *rect = Rect( - image_rect.top, - image_rect.right, - image_rect.bottom, - image_rect.left, - ); - } - }, - GenericImage::Element(ref element) => unsafe { - bindings::Gecko_SetImageElement(self, element.as_ptr()); - }, - } - } - - fn set_gradient(&mut self, gradient: Box<Gradient>) { - unsafe { - bindings::Gecko_SetGradientImageValue(self, Box::into_raw(gradient)); - } - } - - /// Converts into Image. - pub unsafe fn into_image(self: &nsStyleImage) -> Option<Image> { - use crate::gecko_bindings::structs::nsStyleImageType; - use crate::values::computed::MozImageRect; - - match self.mType { - nsStyleImageType::eStyleImageType_Null => None, - nsStyleImageType::eStyleImageType_Image => { - let url = self.__bindgen_anon_1.mImage.as_ref().clone(); - if self.mCropRect.mPtr.is_null() { - Some(GenericImage::Url(url)) - } else { - let rect = &*self.mCropRect.mPtr; - Some(GenericImage::Rect(Box::new(MozImageRect { - url, - top: rect.0, - right: rect.1, - bottom: rect.2, - left: rect.3, - }))) - } - }, - nsStyleImageType::eStyleImageType_Gradient => { - let gradient: &Gradient = &**self.__bindgen_anon_1.mGradient.as_ref(); - Some(GenericImage::Gradient(Box::new(gradient.clone()))) - }, - nsStyleImageType::eStyleImageType_Element => { - use crate::gecko_string_cache::Atom; - let atom = bindings::Gecko_GetImageElement(self); - Some(GenericImage::Element(Atom::from_raw(atom))) - }, - } - } -} - -pub mod basic_shape { - //! Conversions from and to CSS shape representations. - use crate::gecko_bindings::structs::{ - StyleGeometryBox, StyleShapeSource, StyleShapeSourceType, - }; - use crate::values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape}; - use crate::values::computed::motion::OffsetPath; - use crate::values::generics::basic_shape::{GeometryBox, Path, ShapeBox, ShapeSource}; - use crate::values::specified::SVGPathData; - - impl StyleShapeSource { - /// Convert StyleShapeSource to ShapeSource except URL and Image - /// types. - fn into_shape_source<ReferenceBox, ImageOrUrl>( - &self, - ) -> Option<ShapeSource<BasicShape, ReferenceBox, ImageOrUrl>> - where - ReferenceBox: From<StyleGeometryBox>, - { - match self.mType { - StyleShapeSourceType::None => Some(ShapeSource::None), - StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())), - StyleShapeSourceType::Shape => { - let other_shape = unsafe { &*self.__bindgen_anon_1.mBasicShape.as_ref().mPtr }; - let shape = Box::new(other_shape.clone()); - let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox { - None - } else { - Some(self.mReferenceBox.into()) - }; - Some(ShapeSource::Shape(shape, reference_box)) - }, - StyleShapeSourceType::Image => None, - StyleShapeSourceType::Path => { - let path = self.to_svg_path().expect("expect an SVGPathData"); - let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule; - Some(ShapeSource::Path(Path { fill, path })) - }, - } - } - - /// Generate a SVGPathData from StyleShapeSource if possible. - fn to_svg_path(&self) -> Option<SVGPathData> { - match self.mType { - StyleShapeSourceType::Path => { - let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; - Some(SVGPathData(gecko_path.mPath.clone())) - }, - _ => None, - } - } - } - - impl<'a> From<&'a StyleShapeSource> for ClippingShape { - fn from(other: &'a StyleShapeSource) -> Self { - match other.mType { - StyleShapeSourceType::Image => unsafe { - use crate::values::generics::image::Image as GenericImage; - - let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr; - let image = shape_image.into_image().expect("Cannot convert to Image"); - match image { - GenericImage::Url(url) => ShapeSource::ImageOrUrl(url.0), - _ => panic!("ClippingShape doesn't support non-url images"), - } - }, - _ => other - .into_shape_source() - .expect("Couldn't convert to StyleSource!"), - } - } - } - - impl<'a> From<&'a StyleShapeSource> for FloatAreaShape { - fn from(other: &'a StyleShapeSource) -> Self { - match other.mType { - StyleShapeSourceType::Image => unsafe { - let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr; - let image = shape_image.into_image().expect("Cannot convert to Image"); - ShapeSource::ImageOrUrl(image) - }, - _ => other - .into_shape_source() - .expect("Couldn't convert to StyleSource!"), - } - } - } - - impl<'a> From<&'a StyleShapeSource> for OffsetPath { - fn from(other: &'a StyleShapeSource) -> Self { - use crate::values::generics::motion::GenericOffsetPath; - match other.mType { - StyleShapeSourceType::Path => GenericOffsetPath::Path( - other.to_svg_path().expect("Cannot convert to SVGPathData"), - ), - StyleShapeSourceType::None => OffsetPath::none(), - StyleShapeSourceType::Shape | - StyleShapeSourceType::Box | - StyleShapeSourceType::Image => unreachable!("Unsupported offset-path type"), - } - } - } - - impl From<ShapeBox> for StyleGeometryBox { - fn from(reference: ShapeBox) -> Self { - use crate::gecko_bindings::structs::StyleGeometryBox::*; - match reference { - ShapeBox::ContentBox => ContentBox, - ShapeBox::PaddingBox => PaddingBox, - ShapeBox::BorderBox => BorderBox, - ShapeBox::MarginBox => MarginBox, - } - } - } - - impl From<GeometryBox> for StyleGeometryBox { - fn from(reference: GeometryBox) -> Self { - use crate::gecko_bindings::structs::StyleGeometryBox::*; - match reference { - GeometryBox::ShapeBox(shape_box) => From::from(shape_box), - GeometryBox::FillBox => FillBox, - GeometryBox::StrokeBox => StrokeBox, - GeometryBox::ViewBox => ViewBox, - } - } - } - - // Will panic on NoBox - // Ideally these would be implemented on Option<T>, - // but coherence doesn't like that and TryFrom isn't stable - impl From<StyleGeometryBox> for GeometryBox { - fn from(reference: StyleGeometryBox) -> Self { - use crate::gecko_bindings::structs::StyleGeometryBox::*; - match reference { - ContentBox => GeometryBox::ShapeBox(ShapeBox::ContentBox), - PaddingBox => GeometryBox::ShapeBox(ShapeBox::PaddingBox), - BorderBox => GeometryBox::ShapeBox(ShapeBox::BorderBox), - MarginBox => GeometryBox::ShapeBox(ShapeBox::MarginBox), - FillBox => GeometryBox::FillBox, - StrokeBox => GeometryBox::StrokeBox, - ViewBox => GeometryBox::ViewBox, - _ => panic!("Unexpected StyleGeometryBox while converting to GeometryBox"), - } - } - } - - impl From<StyleGeometryBox> for ShapeBox { - fn from(reference: StyleGeometryBox) -> Self { - use crate::gecko_bindings::structs::StyleGeometryBox::*; - match reference { - ContentBox => ShapeBox::ContentBox, - PaddingBox => ShapeBox::PaddingBox, - BorderBox => ShapeBox::BorderBox, - MarginBox => ShapeBox::MarginBox, - _ => panic!("Unexpected StyleGeometryBox while converting to ShapeBox"), - } - } - } -} impl From<RulesMutateError> for nsresult { fn from(other: RulesMutateError) -> Self { @@ -260,39 +25,6 @@ impl From<RulesMutateError> for nsresult { } } -impl TextAlign { - /// Obtain a specified value from a Gecko keyword value - /// - /// Intended for use with presentation attributes, not style structs - pub fn from_gecko_keyword(kw: u32) -> Self { - match kw { - structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left, - structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right, - structs::NS_STYLE_TEXT_ALIGN_CENTER => TextAlign::Center, - structs::NS_STYLE_TEXT_ALIGN_JUSTIFY => TextAlign::Justify, - structs::NS_STYLE_TEXT_ALIGN_MOZ_LEFT => TextAlign::MozLeft, - structs::NS_STYLE_TEXT_ALIGN_MOZ_RIGHT => TextAlign::MozRight, - structs::NS_STYLE_TEXT_ALIGN_MOZ_CENTER => TextAlign::MozCenter, - structs::NS_STYLE_TEXT_ALIGN_CHAR => TextAlign::Char, - structs::NS_STYLE_TEXT_ALIGN_END => TextAlign::End, - _ => panic!("Found unexpected value in style struct for text-align property"), - } - } -} - -/// Convert to String from given chars pointer. -pub unsafe fn string_from_chars_pointer(p: *const u16) -> String { - use std::slice; - let mut length = 0; - let mut iter = p; - while *iter != 0 { - length += 1; - iter = iter.offset(1); - } - let char_vec = slice::from_raw_parts(p, length as usize); - String::from_utf16_lossy(char_vec) -} - impl<'a> From<&'a Matrix4x4Components> for Matrix3D { fn from(m: &'a Matrix4x4Components) -> Matrix3D { Matrix3D { diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 25ce9f5b3a1..27cb666fc6f 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -310,6 +310,17 @@ impl Device { /// Returns safe area insets pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> { - SideOffsets2D::zero() + let pc = match self.pres_context() { + Some(pc) => pc, + None => return SideOffsets2D::zero(), + }; + let mut top = 0.0; + let mut right = 0.0; + let mut bottom = 0.0; + let mut left = 0.0; + unsafe { + bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left) + }; + SideOffsets2D::new(top, right, bottom, left) } } diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs index 8d9fc3d2d85..ea5db98030a 100644 --- a/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/components/style/gecko/non_ts_pseudo_class_list.rs @@ -30,77 +30,78 @@ macro_rules! apply_non_ts_list { ($apply_macro:ident) => { $apply_macro! { [ - ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("link", Link, link, IN_UNVISITED_STATE, _), - ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _), - ("visited", Visited, visited, IN_VISITED_STATE, _), - ("active", Active, active, IN_ACTIVE_STATE, _), - ("checked", Checked, checked, IN_CHECKED_STATE, _), - ("defined", Defined, defined, IN_DEFINED_STATE, _), - ("disabled", Disabled, disabled, IN_DISABLED_STATE, _), - ("enabled", Enabled, enabled, IN_ENABLED_STATE, _), - ("focus", Focus, focus, IN_FOCUS_STATE, _), - ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _), - ("hover", Hover, hover, IN_HOVER_STATE, _), - ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _), - ("target", Target, target, IN_TARGET_STATE, _), - ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _), - ("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _), + ("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("link", Link, IN_UNVISITED_STATE, _), + ("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _), + ("visited", Visited, IN_VISITED_STATE, _), + ("active", Active, IN_ACTIVE_STATE, _), + ("checked", Checked, IN_CHECKED_STATE, _), + ("defined", Defined, IN_DEFINED_STATE, _), + ("disabled", Disabled, IN_DISABLED_STATE, _), + ("enabled", Enabled, IN_ENABLED_STATE, _), + ("focus", Focus, IN_FOCUS_STATE, _), + ("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _), + ("focus-visible", FocusVisible, IN_FOCUS_VISIBLE_STATE, _), + ("hover", Hover, IN_HOVER_STATE, _), + ("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _), + ("target", Target, IN_TARGET_STATE, _), + ("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _), + ("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _), // TODO(emilio): This is inconsistently named (the capital R). - ("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _), - ("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _), - ("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _), - ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-has-dir-attr", MozHasDirAttr, mozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-ltr", MozDirAttrLTR, mozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-rtl", MozDirAttrRTL, mozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, mozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _), + ("-moz-broken", MozBroken, IN_BROKEN_STATE, _), + ("-moz-loading", MozLoading, IN_LOADING_STATE, _), + ("-moz-suppressed", MozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-has-dir-attr", MozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-dir-attr-ltr", MozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-dir-attr-rtl", MozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-autofill", MozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-clicktoplay", MozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, mozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _), + ("-moz-handler-disabled", MozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-blocked", MozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-handler-crashed", MozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _), - ("required", Required, required, IN_REQUIRED_STATE, _), - ("optional", Optional, optional, IN_OPTIONAL_STATE, _), - ("valid", Valid, valid, IN_VALID_STATE, _), - ("invalid", Invalid, invalid, IN_INVALID_STATE, _), - ("in-range", InRange, inRange, IN_INRANGE_STATE, _), - ("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _), - ("default", Default, defaultPseudo, IN_DEFAULT_STATE, _), - ("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _), - ("-moz-read-only", MozReadOnly, mozReadOnly, IN_MOZ_READONLY_STATE, _), - ("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _), - ("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _), - ("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _), - ("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _), - ("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _), - ("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _), - ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _), + ("required", Required, IN_REQUIRED_STATE, _), + ("optional", Optional, IN_OPTIONAL_STATE, _), + ("valid", Valid, IN_VALID_STATE, _), + ("invalid", Invalid, IN_INVALID_STATE, _), + ("in-range", InRange, IN_INRANGE_STATE, _), + ("out-of-range", OutOfRange, IN_OUTOFRANGE_STATE, _), + ("default", Default, IN_DEFAULT_STATE, _), + ("placeholder-shown", PlaceholderShown, IN_PLACEHOLDER_SHOWN_STATE, _), + ("-moz-read-only", MozReadOnly, IN_MOZ_READONLY_STATE, _), + ("-moz-read-write", MozReadWrite, IN_MOZ_READWRITE_STATE, _), + ("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _), + ("-moz-ui-valid", MozUIValid, IN_MOZ_UI_VALID_STATE, _), + ("-moz-ui-invalid", MozUIInvalid, IN_MOZ_UI_INVALID_STATE, _), + ("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _), + ("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _), + ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _), - ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), + ("-moz-user-disabled", MozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-first-node", MozFirstNode, firstNode, _, _), - ("-moz-last-node", MozLastNode, lastNode, _, _), - ("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _), - ("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, mozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, mozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-is-html", MozIsHTML, mozIsHTML, _, _), - ("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _), - ("-moz-lwtheme", MozLWTheme, mozLWTheme, _, _), - ("-moz-lwtheme-brighttext", MozLWThemeBrightText, mozLWThemeBrightText, _, _), - ("-moz-lwtheme-darktext", MozLWThemeDarkText, mozLWThemeDarkText, _, _), - ("-moz-window-inactive", MozWindowInactive, mozWindowInactive, _, _), + ("-moz-first-node", MozFirstNode, _, _), + ("-moz-last-node", MozLastNode, _, _), + ("-moz-only-whitespace", MozOnlyWhitespace, _, _), + ("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), + ("-moz-is-html", MozIsHTML, _, _), + ("-moz-placeholder", MozPlaceholder, _, _), + ("-moz-lwtheme", MozLWTheme, _, _), + ("-moz-lwtheme-brighttext", MozLWThemeBrightText, _, _), + ("-moz-lwtheme-darktext", MozLWThemeDarkText, _, _), + ("-moz-window-inactive", MozWindowInactive, _, _), ] } } diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 8d05dd9bbb1..d989380c02e 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -134,12 +134,6 @@ impl PseudoElement { *self == PseudoElement::FirstLine } - /// Whether this pseudo-element is ::-moz-fieldset-content. - #[inline] - pub fn is_fieldset_content(&self) -> bool { - *self == PseudoElement::FieldsetContent - } - /// Whether this pseudo-element is the ::-moz-color-swatch pseudo. #[inline] pub fn is_color_swatch(&self) -> bool { diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py index cf7cc77c16f..5c59a5c566c 100755 --- a/components/style/gecko/regen_atoms.py +++ b/components/style/gecko/regen_atoms.py @@ -94,7 +94,7 @@ class FileAvoidWrite(BytesIO): self.name = filename def write(self, buf): - if isinstance(buf, unicode): + if isinstance(buf, str): buf = buf.encode('utf-8') BytesIO.write(self, buf) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 9f5b49c5c13..3c944bbc6eb 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -42,7 +42,7 @@ bitflags! { pub type Lang = Atom; macro_rules! pseudo_class_name { - ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { + ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { /// Our representation of a non tree-structural pseudo-class. #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] pub enum NonTSPseudoClass { @@ -72,7 +72,7 @@ impl ToCss for NonTSPseudoClass { W: fmt::Write, { macro_rules! pseudo_class_serialize { - ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { + ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => concat!(":", $css),)* NonTSPseudoClass::Lang(ref s) => { @@ -134,7 +134,7 @@ impl NonTSPseudoClass { /// in a particular state. pub fn parse_non_functional(name: &str) -> Option<Self> { macro_rules! pseudo_class_parse { - ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { + ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { match_ignore_ascii_case! { &name, $($css => Some(NonTSPseudoClass::$name),)* "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen), @@ -156,7 +156,7 @@ impl NonTSPseudoClass { }; } macro_rules! pseudo_class_check_is_enabled_in { - ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { + ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => check_flag!($flags),)* NonTSPseudoClass::MozLocaleDir(_) | @@ -172,6 +172,9 @@ impl NonTSPseudoClass { /// Returns whether the pseudo-class is enabled in content sheets. #[inline] fn is_enabled_in_content(&self) -> bool { + if matches!(*self, NonTSPseudoClass::FocusVisible) { + return static_prefs::pref!("layout.css.focus-visible.enabled"); + } !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME) } @@ -186,7 +189,7 @@ impl NonTSPseudoClass { }; } macro_rules! pseudo_class_state { - ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { + ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => flag!($state),)* NonTSPseudoClass::Dir(..) | diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index fa9914f6222..b2a66f709e2 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -194,11 +194,6 @@ impl ElementSnapshot for GeckoElementSnapshot { } #[inline] - fn exported_part(&self, name: &Atom) -> Option<Atom> { - snapshot_helpers::exported_part(&*self.mAttrs, name) - } - - #[inline] fn imported_part(&self, name: &Atom) -> Option<Atom> { snapshot_helpers::imported_part(&*self.mAttrs, name) } diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index cb3056e7bd5..bd905870353 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -83,16 +83,26 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> { } #[inline(always)] -pub(super) fn exported_part( +pub(super) fn each_exported_part( attrs: &[structs::AttrArray_InternalAttr], name: &Atom, -) -> Option<Atom> { - let attr = find_attr(attrs, &atom!("exportparts"))?; - let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) }; - if atom.is_null() { - return None; + mut callback: impl FnMut(&Atom), +) { + let attr = match find_attr(attrs, &atom!("exportparts")) { + Some(attr) => attr, + None => return, + }; + let mut length = 0; + let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) }; + if atoms.is_null() { + return; + } + + unsafe { + for atom in std::slice::from_raw_parts(atoms, length) { + Atom::with(*atom, &mut callback) + } } - Some(unsafe { Atom::from_raw(atom) }) } #[inline(always)] diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 2b842e72d9b..a5472fadd66 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -72,7 +72,7 @@ use crate::values::computed::Length; use crate::values::specified::length::FontBaseSize; use crate::CaseSensitivityExt; use app_units::Au; -use atomic_refcell::{AtomicRefCell, AtomicRefMut}; +use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::matching::VisitedHandlingMode; @@ -557,8 +557,9 @@ impl<'le> fmt::Debug for GeckoElement<'le> { } impl<'le> GeckoElement<'le> { + /// Gets the raw `ElementData` refcell for the element. #[inline(always)] - fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> { + pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> { unsafe { self.0.mServoData.get().as_ref() } } @@ -1281,6 +1282,14 @@ impl<'le> TElement for GeckoElement<'le> { snapshot_helpers::each_class_or_part(attr, callback) } + #[inline] + fn each_exported_part<F>(&self, name: &Atom, callback: F) + where + F: FnMut(&Atom), + { + snapshot_helpers::each_exported_part(self.attrs(), name, callback) + } + fn each_part<F>(&self, callback: F) where F: FnMut(&Atom), @@ -1383,21 +1392,6 @@ impl<'le> TElement for GeckoElement<'le> { panic!("Atomic child count not implemented in Gecko"); } - /// Whether there is an ElementData container. - fn has_data(&self) -> bool { - self.get_data().is_some() - } - - /// Immutably borrows the ElementData. - fn borrow_data(&self) -> Option<AtomicRef<ElementData>> { - self.get_data().map(|x| x.borrow()) - } - - /// Mutably borrows the ElementData. - fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> { - self.get_data().map(|x| x.borrow_mut()) - } - unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> { if !self.has_data() { debug!("Creating ElementData for {:?}", self); @@ -1566,9 +1560,7 @@ impl<'le> TElement for GeckoElement<'le> { before_change_style: &ComputedValues, after_change_style: &ComputedValues, ) -> bool { - use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::properties::LonghandIdSet; - use crate::values::computed::TransitionProperty; debug_assert!( self.might_need_transitions_update(Some(before_change_style), after_change_style), @@ -1577,53 +1569,21 @@ impl<'le> TElement for GeckoElement<'le> { ); let after_change_box_style = after_change_style.get_box(); - let transitions_count = after_change_box_style.transition_property_count(); let existing_transitions = self.css_transitions_info(); - - // Check if this property is none, custom or unknown. - let is_none_or_custom_property = |property: nsCSSPropertyID| -> bool { - return property == nsCSSPropertyID::eCSSPropertyExtra_no_properties || - property == nsCSSPropertyID::eCSSPropertyExtra_variable || - property == nsCSSPropertyID::eCSSProperty_UNKNOWN; - }; - let mut transitions_to_keep = LonghandIdSet::new(); - - for i in 0..transitions_count { - let property = after_change_box_style.transition_nscsspropertyid_at(i); - let combined_duration = after_change_box_style.transition_combined_duration_at(i); - - // We don't need to update transition for none/custom properties. - if is_none_or_custom_property(property) { - continue; - } - - let transition_property: TransitionProperty = property.into(); - - let mut property_check_helper = |property: LonghandId| -> bool { - let property = property.to_physical(after_change_style.writing_mode); - transitions_to_keep.insert(property); - self.needs_transitions_update_per_property( - property, - combined_duration, - before_change_style, - after_change_style, - &existing_transitions, - ) - }; - - match transition_property { - TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {}, - TransitionProperty::Shorthand(ref shorthand) => { - if shorthand.longhands().any(property_check_helper) { - return true; - } - }, - TransitionProperty::Longhand(longhand_id) => { - if property_check_helper(longhand_id) { - return true; - } - }, + for transition_property in after_change_style.transition_properties() { + let physical_longhand = transition_property + .longhand_id + .to_physical(after_change_style.writing_mode); + transitions_to_keep.insert(physical_longhand); + if self.needs_transitions_update_per_property( + physical_longhand, + after_change_box_style.transition_combined_duration_at(transition_property.index), + before_change_style, + after_change_style, + &existing_transitions, + ) { + return true; } } @@ -1634,6 +1594,22 @@ impl<'le> TElement for GeckoElement<'le> { .any(|property| !transitions_to_keep.contains(*property)) } + /// Whether there is an ElementData container. + #[inline] + fn has_data(&self) -> bool { + self.get_data().is_some() + } + + /// Immutably borrows the ElementData. + fn borrow_data(&self) -> Option<AtomicRef<ElementData>> { + self.get_data().map(|x| x.borrow()) + } + + /// Mutably borrows the ElementData. + fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> { + self.get_data().map(|x| x.borrow_mut()) + } + #[inline] fn lang_attr(&self) -> Option<AttrValue> { let ptr = unsafe { bindings::Gecko_LangValue(self.0) }; @@ -2064,6 +2040,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::MozReadOnly | NonTSPseudoClass::MozReadWrite | NonTSPseudoClass::FocusWithin | + NonTSPseudoClass::FocusVisible | NonTSPseudoClass::MozDragOver | NonTSPseudoClass::MozDevtoolsHighlighted | NonTSPseudoClass::MozStyleeditorTransitioning | @@ -2226,11 +2203,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } #[inline] - fn exported_part(&self, name: &Atom) -> Option<Atom> { - snapshot_helpers::exported_part(self.attrs(), name) - } - - #[inline] fn imported_part(&self, name: &Atom) -> Option<Atom> { snapshot_helpers::imported_part(self.attrs(), name) } diff --git a/components/style/gecko_string_cache/namespace.rs b/components/style/gecko_string_cache/namespace.rs index 2dba484e002..72de229f1d7 100644 --- a/components/style/gecko_string_cache/namespace.rs +++ b/components/style/gecko_string_cache/namespace.rs @@ -24,7 +24,18 @@ macro_rules! ns { } /// A Gecko namespace is just a wrapped atom. -#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] +#[derive( + Clone, + Debug, + Default, + Eq, + Hash, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] #[repr(transparent)] pub struct Namespace(pub Atom); diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index bc74527bf16..1154c136165 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -62,9 +62,6 @@ pub trait ElementSnapshot: Sized { /// called if `has_attrs()` returns true. fn is_part(&self, name: &Atom) -> bool; - /// See Element::exported_part. - fn exported_part(&self, name: &Atom) -> Option<Atom>; - /// See Element::imported_part. fn imported_part(&self, name: &Atom) -> Option<Atom>; @@ -371,13 +368,6 @@ where } } - fn exported_part(&self, name: &Atom) -> Option<Atom> { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name), - _ => self.element.exported_part(name), - } - } - fn imported_part(&self, name: &Atom) -> Option<Atom> { match self.snapshot() { Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name), diff --git a/components/style/properties/Mako-0.9.1.zip b/components/style/properties/Mako-0.9.1.zip Binary files differdeleted file mode 100644 index b7450e30012..00000000000 --- a/components/style/properties/Mako-0.9.1.zip +++ /dev/null diff --git a/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl b/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl Binary files differnew file mode 100644 index 00000000000..9593025a473 --- /dev/null +++ b/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl diff --git a/components/style/properties/build.py b/components/style/properties/build.py index 45d8fa676ab..92966ce1b2d 100644 --- a/components/style/properties/build.py +++ b/components/style/properties/build.py @@ -8,7 +8,7 @@ import re import sys BASE = os.path.dirname(__file__.replace('\\', '/')) -sys.path.insert(0, os.path.join(BASE, "Mako-0.9.1.zip")) +sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl")) sys.path.insert(0, BASE) # For importing `data.py` from mako import exceptions @@ -130,7 +130,7 @@ def main(): def abort(message): - sys.stderr.write(message + b"\n") + print(message, file=sys.stderr) sys.exit(1) @@ -146,18 +146,18 @@ def render(filename, **context): strict_undefined=True) # Uncomment to debug generated Python code: # write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code) - return template.render(**context).encode("utf8") + return template.render(**context) except Exception: # Uncomment to see a traceback in generated Python code: # raise - abort(exceptions.text_error_template().render().encode("utf8")) + abort(exceptions.text_error_template().render()) def write(directory, filename, content): if not os.path.exists(directory): os.makedirs(directory) full_path = os.path.join(directory, filename) - open(full_path, "wb").write(content) + open(full_path, "w", encoding="utf-8").write(content) python_addr = RE_PYTHON_ADDR.search(content) if python_addr: diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 50029da8479..e9a34d6bf34 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -13,7 +13,7 @@ use crate::media_queries::Device; use crate::properties::{ComputedValues, StyleBuilder}; use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword}; use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; -use crate::properties::CASCADE_PROPERTY; +use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags}; use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; @@ -337,89 +337,80 @@ where context.builder.build() } -/// How should a declaration behave when ignoring document colors? -enum DeclarationApplication { - /// We should apply the declaration. - Apply, - /// We should ignore the declaration. - Ignore, - /// We should apply the following declaration, only if any other declaration - /// hasn't set it before. - ApplyUnlessOverriden(PropertyDeclaration), -} +/// For ignored colors mode, we sometimes want to do something equivalent to +/// "revert-or-initial", where we `revert` for a given origin, but then apply a +/// given initial value if nothing in other origins did override it. +/// +/// This is a bit of a clunky way of achieving this. +type DeclarationsToApplyUnlessOverriden = SmallVec::<[PropertyDeclaration; 2]>; -fn application_when_ignoring_colors( +fn tweak_when_ignoring_colors( builder: &StyleBuilder, longhand_id: LonghandId, origin: Origin, - declaration: &PropertyDeclaration, -) -> DeclarationApplication { + declaration: &mut Cow<PropertyDeclaration>, + declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden, +) { if !longhand_id.ignored_when_document_colors_disabled() { - return DeclarationApplication::Apply; + return; } let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent); if is_ua_or_user_rule { - return DeclarationApplication::Apply; + return; } // Don't override background-color on ::-moz-color-swatch. It is set as an // author style (via the style attribute), but it's pretty important for it // to show up for obvious reasons :) if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor { - return DeclarationApplication::Apply; + return; } - // Treat background-color a bit differently. If the specified color is - // anything other than a fully transparent color, convert it into the - // Device's default background color. - // Also: for now, we treat background-image a bit differently, too. - // background-image is marked as ignored, but really, we only ignore - // it when backplates are disabled (since then text may be unreadable over - // a background image, if we're ignoring document colors). - // Here we check backplate status to decide if ignoring background-image - // is the right decision. - match *declaration { + // A few special-cases ahead. + match **declaration { + // We honor color and background-color: transparent, and + // "revert-or-initial" otherwise. PropertyDeclaration::BackgroundColor(ref color) => { - if color.is_transparent() { - return DeclarationApplication::Apply; + if !color.is_transparent() { + let color = builder.device.default_background_color(); + declarations_to_apply_unless_overriden.push( + PropertyDeclaration::BackgroundColor(color.into()) + ) } - let color = builder.device.default_background_color(); - DeclarationApplication::ApplyUnlessOverriden( - PropertyDeclaration::BackgroundColor(color.into()) - ) } PropertyDeclaration::Color(ref color) => { + // otherwise. if color.0.is_transparent() { - return DeclarationApplication::Apply; - } - if builder.get_parent_inherited_text().clone_color().alpha != 0 { - return DeclarationApplication::Ignore; + return; } let color = builder.device.default_color(); - DeclarationApplication::ApplyUnlessOverriden( + declarations_to_apply_unless_overriden.push( PropertyDeclaration::Color(specified::ColorPropertyValue(color.into())) ) }, - // In the future, if/when we remove the backplate pref, we can remove this - // special case along with the 'ignored_when_colors_disabled=True' mako line - // for the "background-image" property. + // We honor url background-images if backplating. #[cfg(feature = "gecko")] - PropertyDeclaration::BackgroundImage(..) => { + PropertyDeclaration::BackgroundImage(ref bkg) => { + use crate::values::generics::image::Image; if static_prefs::pref!("browser.display.permit_backplate") { - DeclarationApplication::Apply - } else { - DeclarationApplication::Ignore + if bkg.0.iter().all(|image| matches!(*image, Image::Url(..))) { + return; + } } }, - _ => DeclarationApplication::Ignore, + _ => {}, } + + *declaration.to_mut() = PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert); + } struct Cascade<'a, 'b: 'a> { context: &'a mut computed::Context<'b>, cascade_mode: CascadeMode<'a>, seen: LonghandIdSet, + author_specified: LonghandIdSet, reverted: PerOrigin<LonghandIdSet>, } @@ -429,6 +420,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { context, cascade_mode, seen: LonghandIdSet::default(), + author_specified: LonghandIdSet::default(), reverted: Default::default(), } } @@ -491,7 +483,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { let ignore_colors = !self.context.builder.device.use_document_colors(); let mut declarations_to_apply_unless_overriden = - SmallVec::<[PropertyDeclaration; 2]>::new(); + DeclarationsToApplyUnlessOverriden::new(); for (declaration, origin) in declarations { let declaration_id = declaration.id(); @@ -533,26 +525,23 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { continue; } - let declaration = self.substitute_variables_if_needed(declaration); + let mut declaration = self.substitute_variables_if_needed(declaration); // When document colors are disabled, do special handling of // properties that are marked as ignored in that mode. if ignore_colors { - let application = application_when_ignoring_colors( + tweak_when_ignoring_colors( &self.context.builder, longhand_id, origin, - &declaration, + &mut declaration, + &mut declarations_to_apply_unless_overriden, + ); + debug_assert_eq!( + declaration.id(), + PropertyDeclarationId::Longhand(longhand_id), + "Shouldn't change the declaration id!", ); - - match application { - DeclarationApplication::Ignore => continue, - DeclarationApplication::Apply => {}, - DeclarationApplication::ApplyUnlessOverriden(decl) => { - declarations_to_apply_unless_overriden.push(decl); - continue; - } - } } let css_wide_keyword = declaration.get_css_wide_keyword(); @@ -569,6 +558,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { } self.seen.insert(physical_longhand_id); + if origin == Origin::Author { + self.author_specified.insert(physical_longhand_id); + } let unset = css_wide_keyword.map_or(false, |css_wide_keyword| { match css_wide_keyword { @@ -691,6 +683,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { if let Some(svg) = builder.get_svg_if_mutated() { svg.fill_arrays(); } + + } + + if self.author_specified.contains_any(LonghandIdSet::border_background_properties()) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND); + } + if self.author_specified.contains_any(LonghandIdSet::padding_properties()) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING); } #[cfg(feature = "servo")] @@ -711,12 +711,26 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { None => return false, }; - let cached_style = match cache.find(guards, &self.context.builder) { + let builder = &mut self.context.builder; + + let cached_style = match cache.find(guards, &builder) { Some(style) => style, None => return false, }; - self.context.builder.copy_reset_from(cached_style); + builder.copy_reset_from(cached_style); + + // We're using the same reset style as another element, and we'll skip + // applying the relevant properties. So we need to do the relevant + // bookkeeping here to keep these two bits correct. + // + // Note that all the properties involved are non-inherited, so we don't + // need to do anything else other than just copying the bits over. + let reset_props_bits = + ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND | + ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING; + builder.add_flags(cached_style.flags & reset_props_bits); + true } diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 4363f3f36e9..221363ba4b1 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -67,6 +67,23 @@ bitflags! { /// Whether this style is the style of the document element. const IS_ROOT_ELEMENT_STYLE = 1 << 11; + + /// Whether this element is inside an `opacity: 0` subtree. + const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 12; + + /// Whether there are author-specified rules for border-* properties + /// (except border-image-*), background-color, or background-image. + /// + /// TODO(emilio): Maybe do include border-image, see: + /// + /// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845 + const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 13; + + /// Whether there are author-specified rules for padding-* properties. + /// + /// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see + /// https://github.com/w3c/csswg-drafts/issues/4777 + const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 14; } } @@ -74,10 +91,11 @@ impl ComputedValueFlags { /// Flags that are unconditionally propagated to descendants. #[inline] fn inherited_flags() -> Self { - ComputedValueFlags::IS_RELEVANT_LINK_VISITED | - ComputedValueFlags::CAN_BE_FRAGMENTED | - ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE | - ComputedValueFlags::HAS_TEXT_DECORATION_LINES + Self::IS_RELEVANT_LINK_VISITED | + Self::CAN_BE_FRAGMENTED | + Self::IS_IN_PSEUDO_ELEMENT_SUBTREE | + Self::HAS_TEXT_DECORATION_LINES | + Self::IS_IN_OPACITY_ZERO_SUBTREE } /// Flags that may be propagated to descendants. diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 15ab380789b..472d1eb34dd 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -382,6 +382,7 @@ class Longhand(object): "ScrollSnapStrictness", "ScrollSnapType", "TextAlign", + "TextAlignLast", "TextDecorationLine", "TextEmphasisPosition", "TextTransform", @@ -602,7 +603,7 @@ class PropertiesData(object): longhand = Longhand(self.current_style_struct, name, **kwargs) self.add_prefixed_aliases(longhand) - longhand.alias = list(map(lambda xp: Alias(xp[0], longhand, xp[1]), longhand.alias)) + longhand.alias = [Alias(xp[0], longhand, xp[1]) for xp in longhand.alias] self.longhand_aliases += longhand.alias self.current_style_struct.longhands.append(longhand) self.longhands.append(longhand) @@ -620,7 +621,7 @@ class PropertiesData(object): sub_properties = [self.longhands_by_name[s] for s in sub_properties] shorthand = Shorthand(name, sub_properties, *args, **kwargs) self.add_prefixed_aliases(shorthand) - shorthand.alias = list(map(lambda xp: Alias(xp[0], shorthand, xp[1]), shorthand.alias)) + shorthand.alias = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.alias] self.shorthand_aliases += shorthand.alias self.shorthands.append(shorthand) self.shorthands_by_name[name] = shorthand @@ -669,17 +670,17 @@ def _remove_common_first_line_and_first_letter_properties(props, engine): class PropertyRestrictions: @staticmethod def logical_group(data, group): - return map(lambda p: p.name, data.longhands_by_logical_group[group]) + return [p.name for p in data.longhands_by_logical_group[group]] @staticmethod def shorthand(data, shorthand): if shorthand not in data.shorthands_by_name: return [] - return map(lambda p: p.name, data.shorthands_by_name[shorthand].sub_properties) + return [p.name for p in data.shorthands_by_name[shorthand].sub_properties] @staticmethod def spec(data, spec_path): - return map(lambda p: p.name, filter(lambda p: spec_path in p.spec, data.longhands)) + return [p.name for p in data.longhands if spec_path in p.spec] # https://drafts.csswg.org/css-pseudo/#first-letter-styling @staticmethod diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 6cfb4c859b4..f3db987d8f9 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -22,16 +22,14 @@ use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ff use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name}; % endfor use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle; -use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom; use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom; -use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom; use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; -use crate::gecko_bindings::bindings::Gecko_SetNullImageValue; use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::mozilla::PseudoStyleType; +use crate::gecko::data::PerDocumentStyleData; use crate::gecko::values::round_border_to_device_pixels; use crate::logical_geometry::WritingMode; use crate::media_queries::Device; @@ -43,11 +41,9 @@ use std::mem::{forget, MaybeUninit}; use std::{cmp, ops, ptr}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; use crate::values::computed::{Percentage, TransitionProperty}; -use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::BorderStyle; use crate::values::computed::font::FontSize; use crate::values::generics::column::ColumnCount; -use crate::values::generics::image::ImageLayer; pub mod style_structs { @@ -392,112 +388,6 @@ def set_gecko_property(ffi_name, expr): } </%def> -<%def name="impl_svg_length(ident, gecko_ffi_name)"> - // When context-value is used on an SVG length, the corresponding flag is - // set on mContextFlags, and the length field is set to the initial value. - - pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - use crate::values::generics::svg::SVGLength; - use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; - let length = match v { - SVGLength::LengthPercentage(length) => { - self.gecko.mContextFlags &= !CONTEXT_VALUE; - length - } - SVGLength::ContextValue => { - self.gecko.mContextFlags |= CONTEXT_VALUE; - match longhands::${ident}::get_initial_value() { - SVGLength::LengthPercentage(length) => length, - _ => unreachable!("Initial value should not be context-value"), - } - } - }; - self.gecko.${gecko_ffi_name} = length; - } - - pub fn copy_${ident}_from(&mut self, other: &Self) { - use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; - self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone(); - self.gecko.mContextFlags = - (self.gecko.mContextFlags & !CONTEXT_VALUE) | - (other.gecko.mContextFlags & CONTEXT_VALUE); - } - - pub fn reset_${ident}(&mut self, other: &Self) { - self.copy_${ident}_from(other) - } - - pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use crate::values::generics::svg::SVGLength; - use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; - if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 { - return SVGLength::ContextValue; - } - SVGLength::LengthPercentage(self.gecko.${gecko_ffi_name}.clone()) - } -</%def> - -<%def name="impl_svg_opacity(ident, gecko_ffi_name)"> - <% source_prefix = ident.split("_")[0].upper() + "_OPACITY_SOURCE" %> - - pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; - use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; - use crate::gecko_bindings::structs::nsStyleSVGOpacitySource::*; - use crate::values::generics::svg::SVGOpacity; - self.gecko.mContextFlags &= !MASK; - match v { - SVGOpacity::Opacity(opacity) => { - self.gecko.mContextFlags |= - (eStyleSVGOpacitySource_Normal as u8) << SHIFT; - self.gecko.${gecko_ffi_name} = opacity; - } - SVGOpacity::ContextFillOpacity => { - self.gecko.mContextFlags |= - (eStyleSVGOpacitySource_ContextFillOpacity as u8) << SHIFT; - self.gecko.${gecko_ffi_name} = 1.; - } - SVGOpacity::ContextStrokeOpacity => { - self.gecko.mContextFlags |= - (eStyleSVGOpacitySource_ContextStrokeOpacity as u8) << SHIFT; - self.gecko.${gecko_ffi_name} = 1.; - } - } - } - - pub fn copy_${ident}_from(&mut self, other: &Self) { - use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; - self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; - self.gecko.mContextFlags = - (self.gecko.mContextFlags & !MASK) | - (other.gecko.mContextFlags & MASK); - } - - pub fn reset_${ident}(&mut self, other: &Self) { - self.copy_${ident}_from(other) - } - - pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; - use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; - use crate::gecko_bindings::structs::nsStyleSVGOpacitySource::*; - use crate::values::generics::svg::SVGOpacity; - - let source = (self.gecko.mContextFlags & MASK) >> SHIFT; - if source == eStyleSVGOpacitySource_Normal as u8 { - return SVGOpacity::Opacity(self.gecko.${gecko_ffi_name}); - } else { - debug_assert_eq!(self.gecko.${gecko_ffi_name}, 1.0); - if source == eStyleSVGOpacitySource_ContextFillOpacity as u8 { - SVGOpacity::ContextFillOpacity - } else { - debug_assert_eq!(source, eStyleSVGOpacitySource_ContextStrokeOpacity as u8); - SVGOpacity::ContextStrokeOpacity - } - } - } -</%def> - <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None, round_to_pixels=False)"> #[allow(non_snake_case)] @@ -647,12 +537,7 @@ impl Clone for ${style_struct.gecko_struct_name} { </%def> -<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name=None)"> - <% - if gecko_ffi_name is None: - gecko_ffi_name = "m" + to_camel_case(ident) - %> - +<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { self.gecko.${gecko_ffi_name} = From::from(v) @@ -711,9 +596,6 @@ impl Clone for ${style_struct.gecko_struct_name} { # Types used with predefined_type()-defined properties that we can auto-generate. predefined_types = { "MozScriptMinSize": impl_absolute_length, - "SVGLength": impl_svg_length, - "SVGOpacity": impl_svg_opacity, - "SVGWidth": impl_svg_length, } def longhand_method(longhand): @@ -778,8 +660,7 @@ fn static_assert() { for x in CORNERS]) %> <%self:impl_trait style_struct_name="Border" - skip_longhands="${skip_border_longhands} border-image-source - border-image-repeat"> + skip_longhands="${skip_border_longhands} border-image-repeat"> % for side in SIDES: pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) { self.gecko.mBorderStyle[${side.index}] = v; @@ -848,35 +729,6 @@ fn static_assert() { corner) %> % endfor - pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) { - unsafe { - // Prevent leaking of the last elements we did set - Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); - } - - if let ImageLayer::Image(image) = image { - self.gecko.mBorderImageSource.set(image); - } - } - - pub fn copy_border_image_source_from(&mut self, other: &Self) { - unsafe { - Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource, - &other.gecko.mBorderImageSource); - } - } - - pub fn reset_border_image_source(&mut self, other: &Self) { - self.copy_border_image_source_from(other) - } - - pub fn clone_border_image_source(&self) -> longhands::border_image_source::computed_value::T { - match unsafe { self.gecko.mBorderImageSource.into_image() } { - Some(image) => ImageLayer::Image(image), - None => ImageLayer::None, - } - } - <% border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"] %> @@ -950,11 +802,10 @@ fn static_assert() { <% skip_position_longhands = " ".join(x.ident for x in SIDES) %> <%self:impl_trait style_struct_name="Position" - skip_longhands="${skip_position_longhands} grid-auto-flow"> + skip_longhands="${skip_position_longhands}"> % for side in SIDES: <% impl_split_style_coord(side.ident, "mOffset", side.index) %> % endfor - ${impl_simple_type_with_conversion("grid_auto_flow")} pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) { debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY); self.gecko.mJustifyItems.computed = v; @@ -1005,15 +856,13 @@ fn static_assert() { } </%self:impl_trait> -<% - skip_font_longhands = """font-family font-size font-size-adjust font-weight - font-style font-stretch -moz-script-level - font-synthesis -x-lang font-variant-alternates - font-variant-east-asian font-variant-ligatures - font-variant-numeric font-language-override - font-feature-settings font-variation-settings - -moz-min-font-size-ratio -x-text-zoom""" -%> +<% skip_font_longhands = """font-family font-size font-size-adjust font-weight + font-style font-stretch font-synthesis -x-lang + font-variant-alternates font-variant-east-asian + font-variant-ligatures font-variant-numeric + font-language-override font-feature-settings + font-variation-settings -moz-min-font-size-ratio + -x-text-zoom""" %> <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}"> @@ -1275,9 +1124,7 @@ fn static_assert() { longhands::_x_text_zoom::computed_value::T(self.gecko.mAllowZoom) } - ${impl_simple("_moz_script_level", "mScriptLevel")} <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %> - ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")} ${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")} ${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")} @@ -1401,9 +1248,6 @@ fn static_assert() { ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)} </%def> -<%def name="impl_transition_timing_function()"> - ${impl_animation_or_transition_timing_function('transition')} -</%def> <%def name="impl_animation_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)} @@ -1413,10 +1257,6 @@ fn static_assert() { ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)} </%def> -<%def name="impl_animation_timing_function()"> - ${impl_animation_or_transition_timing_function('animation')} -</%def> - <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pub fn set_animation_${ident}<I>(&mut self, v: I) @@ -1466,7 +1306,7 @@ fn static_assert() { animation-iteration-count animation-timing-function clear transition-duration transition-delay transition-timing-function transition-property - shape-outside -webkit-line-clamp""" %> + -webkit-line-clamp""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> #[inline] pub fn set_display(&mut self, v: longhands::display::computed_value::T) { @@ -1509,7 +1349,7 @@ fn static_assert() { ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} - ${impl_transition_timing_function()} + ${impl_animation_or_transition_timing_function('transition')} pub fn transition_combined_duration_at(&self, index: usize) -> f32 { // https://drafts.csswg.org/css-transitions/#transition-combined-duration @@ -1723,10 +1563,7 @@ fn static_assert() { ${impl_animation_count('iteration_count', 'IterationCount')} ${impl_copy_animation_value('iteration_count', 'IterationCount')} - - ${impl_animation_timing_function()} - - <% impl_shape_source("shape_outside", "mShapeOutside") %> + ${impl_animation_or_transition_timing_function('animation')} #[allow(non_snake_case)] pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) { @@ -2009,7 +1846,7 @@ fn static_assert() { for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(count as usize) { - Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage); + layer.mImage = other.mImage.clone(); } self.gecko.${image_layers_field}.mImageCount = count; } @@ -2029,22 +1866,17 @@ fn static_assert() { let images = images.into_iter(); unsafe { - // Prevent leaking of the last elements we did set - for image in &mut self.gecko.${image_layers_field}.mLayers { - Gecko_SetNullImageValue(&mut image.mImage) - } - // XXXManishearth clear mSourceURI for masks - Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, images.len(), - LayerType::${shorthand.title()}); + Gecko_EnsureImageLayersLength( + &mut self.gecko.${image_layers_field}, + images.len(), + LayerType::${shorthand.title()}, + ); } self.gecko.${image_layers_field}.mImageCount = images.len() as u32; - for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { - if let ImageLayer::Image(image) = image { - geckoimage.mImage.set(image) - } + geckoimage.mImage = image; } } @@ -2052,12 +1884,8 @@ fn static_assert() { longhands::${shorthand}_image::computed_value::List( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mImageCount as usize) - .map(|ref layer| { - match unsafe { layer.mImage.into_image() } { - Some(image) => ImageLayer::Image(image), - None => ImageLayer::None, - } - }).collect() + .map(|layer| layer.mImage.clone()) + .collect() ) } @@ -2189,18 +2017,9 @@ fn static_assert() { <%self:impl_trait style_struct_name="InheritedText" - skip_longhands="text-align -webkit-text-stroke-width text-emphasis-position"> - - <% text_align_keyword = Keyword("text-align", - "start end left right center justify -moz-center -moz-left -moz-right char", - gecko_strip_moz_prefix=False) %> - ${impl_keyword('text_align', 'mTextAlign', text_align_keyword)} - - ${impl_simple_type_with_conversion("text_emphasis_position")} - + skip_longhands="-webkit-text-stroke-width"> ${impl_non_negative_length('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth')} - </%self:impl_trait> <%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter"> @@ -2244,204 +2063,21 @@ fn static_assert() { } </%self:impl_trait> -// Set SVGPathData to StyleShapeSource. -fn set_style_svg_path( - shape_source: &mut structs::mozilla::StyleShapeSource, - servo_path: values::specified::svg_path::SVGPathData, - fill: values::generics::basic_shape::FillRule, -) { - // Setup path. - 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::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 { bindings::Gecko_DestroyShapeSource(${ident}) }; - - ${ident}.mType = StyleShapeSourceType::None; - - match v { - ShapeSource::None => {} // don't change the type - ShapeSource::ImageOrUrl(image) => { - % if ident == "clip_path": - use crate::values::generics::image::Image; - - let image = Image::Url(ComputedImageUrl(image)); - % endif - unsafe { - bindings::Gecko_NewShapeImage(${ident}); - let style_image = &mut *${ident}.__bindgen_anon_1.mShapeImage.as_mut().mPtr; - style_image.set(image); - } - } - ShapeSource::Box(reference) => { - ${ident}.mReferenceBox = reference.into(); - ${ident}.mType = StyleShapeSourceType::Box; - } - ShapeSource::Path(p) => set_style_svg_path(${ident}, p.path, p.fill), - ShapeSource::Shape(servo_shape, maybe_box) => { - unsafe { - ${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr = - Box::into_raw(servo_shape); - } - ${ident}.mReferenceBox = - maybe_box.map(Into::into).unwrap_or(StyleGeometryBox::NoBox); - ${ident}.mType = StyleShapeSourceType::Shape; - } - } - - } - - pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - (&self.gecko.${gecko_ffi_name}).into() - } - - pub fn copy_${ident}_from(&mut self, other: &Self) { - use crate::gecko_bindings::bindings::Gecko_CopyShapeSourceFrom; - unsafe { - Gecko_CopyShapeSourceFrom(&mut self.gecko.${gecko_ffi_name}, &other.gecko.${gecko_ffi_name}); - } - } - - pub fn reset_${ident}(&mut self, other: &Self) { - self.copy_${ident}_from(other) - } -</%def> - <% skip_svg_longhands = """ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image -clip-path """ %> <%self:impl_trait style_struct_name="SVG" skip_longhands="${skip_svg_longhands}"> - <% impl_common_image_layer_properties("mask") %> <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %> <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %> - <% impl_shape_source("clip_path", "mClipPath") %> </%self:impl_trait> -<%self:impl_trait style_struct_name="InheritedSVG" - skip_longhands="stroke-dasharray"> - pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { - use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; - use crate::values::generics::svg::SVGStrokeDashArray; - - match v { - SVGStrokeDashArray::Values(v) => { - let v = v.into_iter(); - self.gecko.mContextFlags &= !CONTEXT_VALUE; - unsafe { - bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut *self.gecko, v.len() as u32); - } - for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) { - *gecko = servo; - } - } - SVGStrokeDashArray::ContextValue => { - self.gecko.mContextFlags |= CONTEXT_VALUE; - unsafe { - bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut *self.gecko, 0); - } - } - } - } - - pub fn copy_stroke_dasharray_from(&mut self, other: &Self) { - use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; - unsafe { - bindings::Gecko_nsStyleSVG_CopyDashArray(&mut *self.gecko, &*other.gecko); - } - self.gecko.mContextFlags = - (self.gecko.mContextFlags & !CONTEXT_VALUE) | - (other.gecko.mContextFlags & CONTEXT_VALUE); - } - - pub fn reset_stroke_dasharray(&mut self, other: &Self) { - self.copy_stroke_dasharray_from(other) - } - - pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T { - use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; - use crate::values::generics::svg::SVGStrokeDashArray; - - if self.gecko.mContextFlags & CONTEXT_VALUE != 0 { - debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0); - return SVGStrokeDashArray::ContextValue; - } - SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect()) - } +<%self:impl_trait style_struct_name="InheritedSVG"> </%self:impl_trait> -<%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor"> - pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) { - self.gecko.mCursor = v.keyword; - unsafe { - bindings::Gecko_SetCursorArrayCapacity(&mut *self.gecko, v.images.len()); - } - for i in 0..v.images.len() { - unsafe { - bindings::Gecko_AppendCursorImage(&mut *self.gecko, &v.images[i].url); - } - - match v.images[i].hotspot { - Some((x, y)) => { - self.gecko.mCursorImages[i].mHaveHotspot = true; - self.gecko.mCursorImages[i].mHotspotX = x; - self.gecko.mCursorImages[i].mHotspotY = y; - }, - _ => { - self.gecko.mCursorImages[i].mHaveHotspot = false; - } - } - } - } - - pub fn copy_cursor_from(&mut self, other: &Self) { - self.gecko.mCursor = other.gecko.mCursor; - unsafe { - Gecko_CopyCursorArrayFrom(&mut *self.gecko, &*other.gecko); - } - } - - pub fn reset_cursor(&mut self, other: &Self) { - self.copy_cursor_from(other) - } - - pub fn clone_cursor(&self) -> longhands::cursor::computed_value::T { - use crate::values::computed::ui::CursorImage; - - let keyword = self.gecko.mCursor; - - let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| { - let url = gecko_cursor_image.mImage.clone(); - - let hotspot = - if gecko_cursor_image.mHaveHotspot { - Some((gecko_cursor_image.mHotspotX, gecko_cursor_image.mHotspotY)) - } else { - None - }; - - CursorImage { url, hotspot } - }).collect::<Vec<_>>().into_boxed_slice(); - - longhands::cursor::computed_value::T { images, keyword } - } +<%self:impl_trait style_struct_name="InheritedUI"> </%self:impl_trait> <%self:impl_trait style_struct_name="Column" @@ -2483,8 +2119,7 @@ clip-path } </%self:impl_trait> -<%self:impl_trait style_struct_name="UI" skip_longhands="-moz-force-broken-image-icon"> - ${impl_simple_type_with_conversion("_moz_force_broken_image_icon", "mForceBrokenImageIcon")} +<%self:impl_trait style_struct_name="UI"> </%self:impl_trait> <%self:impl_trait style_struct_name="XUL"> @@ -2494,3 +2129,40 @@ clip-path ${declare_style_struct(style_struct)} ${impl_style_struct(style_struct)} % endfor + +/// Assert that the initial values set in Gecko style struct constructors +/// match the values returned by `get_initial_value()` for each longhand. +#[cfg(feature = "gecko")] +#[inline] +pub fn assert_initial_values_match(data: &PerDocumentStyleData) { + if cfg!(debug_assertions) { + let data = data.borrow(); + let cv = data.stylist.device().default_computed_values(); + <% + # Skip properties with initial values that change at computed value time. + SKIPPED = [ + "border-top-width", + "border-bottom-width", + "border-left-width", + "border-right-width", + "font-family", + "font-size", + "outline-width", + ] + TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED] + %> + % for property in TO_TEST: + assert_eq!( + cv.clone_${property.ident}(), + longhands::${property.ident}::get_initial_value(), + concat!( + "initial value in Gecko style struct for ", + stringify!(${property.ident}), + " must match longhands::", + stringify!(${property.ident}), + "::get_initial_value()" + ) + ); + % endfor + } +} diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 7a25b2569ea..9baab22cb45 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -680,7 +680,7 @@ % endfor let mut bits = ${type}::empty(); - % for servo_bit, gecko_bit in bit_map.iteritems(): + % for servo_bit, gecko_bit in bit_map.items(): if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 { bits |= ${servo_bit}; } @@ -696,7 +696,7 @@ let mut bits: ${kw_type} = 0; // FIXME: if we ensure that the Servo bitflags storage is the same // as Gecko's one, we can just copy it. - % for servo_bit, gecko_bit in bit_map.iteritems(): + % for servo_bit, gecko_bit in bit_map.items(): if self.contains(${servo_bit}) { bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type}; } @@ -707,7 +707,8 @@ </%def> <%def name="single_keyword(name, values, vector=False, - extra_specified=None, needs_conversion=False, **kwargs)"> + extra_specified=None, needs_conversion=False, + gecko_pref_controlled_initial_value=None, **kwargs)"> <% keyword_kwargs = {a: kwargs.pop(a, None) for a in [ 'gecko_constant_prefix', @@ -724,18 +725,19 @@ ]} %> - <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)"> + <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False, + gecko_pref_controlled_initial_value=None)"> <%def name="variants(variants, include_aliases)"> % for variant in variants: % if include_aliases: <% aliases = [] - for alias, v in keyword.aliases_for(engine).iteritems(): + for alias, v in keyword.aliases_for(engine).items(): if variant == v: aliases.append(alias) %> % if aliases: - #[parse(aliases = "${','.join(aliases)}")] + #[parse(aliases = "${','.join(sorted(aliases))}")] % endif % endif ${to_camel_case(variant)}, @@ -773,10 +775,20 @@ } #[inline] pub fn get_initial_value() -> computed_value::T { + % if engine == "gecko" and gecko_pref_controlled_initial_value: + if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") { + return computed_value::T::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])}; + } + % endif computed_value::T::${to_camel_case(values.split()[0])} } #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { + % if engine == "gecko" and gecko_pref_controlled_initial_value: + if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") { + return SpecifiedValue::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])}; + } + % endif SpecifiedValue::${to_camel_case(values.split()[0])} } #[inline] @@ -805,7 +817,8 @@ % else: <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> ${inner_body(Keyword(name, values, **keyword_kwargs), - extra_specified=extra_specified, needs_conversion=needs_conversion)} + extra_specified=extra_specified, needs_conversion=needs_conversion, + gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)} % if caller: ${caller.body()} % endif diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 84f843ec728..2e9a53fc677 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -11,7 +11,7 @@ #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nsCSSPropertyID; use itertools::{EitherOrBoth, Itertools}; -use crate::properties::{CSSWideKeyword, PropertyDeclaration}; +use crate::properties::{CSSWideKeyword, PropertyDeclaration, NonCustomPropertyIterator}; use crate::properties::longhands; use crate::properties::longhands::visibility::computed_value::T as Visibility; use crate::properties::LonghandId; @@ -883,3 +883,66 @@ impl ToAnimatedZero for AnimatedFilter { } } } + +/// An iterator over all the properties that transition on a given style. +pub struct TransitionPropertyIterator<'a> { + style: &'a ComputedValues, + index_range: core::ops::Range<usize>, + longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>, +} + +impl<'a> TransitionPropertyIterator<'a> { + /// Create a `TransitionPropertyIterator` for the given style. + pub fn from_style(style: &'a ComputedValues) -> Self { + Self { + style, + index_range: 0..style.get_box().transition_property_count(), + longhand_iterator: None, + } + } +} + +/// A single iteration of the TransitionPropertyIterator. +pub struct TransitionPropertyIteration { + /// The id of the longhand for this property. + pub longhand_id: LonghandId, + + /// The index of this property in the list of transition properties for this + /// iterator's style. + pub index: usize, +} + +impl<'a> Iterator for TransitionPropertyIterator<'a> { + type Item = TransitionPropertyIteration; + + fn next(&mut self) -> Option<Self::Item> { + use crate::values::computed::TransitionProperty; + loop { + if let Some(ref mut longhand_iterator) = self.longhand_iterator { + if let Some(longhand_id) = longhand_iterator.next() { + return Some(TransitionPropertyIteration { + longhand_id, + index: self.index_range.start, + }); + } + self.longhand_iterator = None; + } + + let index = self.index_range.next()?; + match self.style.get_box().transition_property_at(index) { + TransitionProperty::Longhand(longhand_id) => { + return Some(TransitionPropertyIteration { + longhand_id, + index, + }) + } + // In the other cases, we set up our state so that we are ready to + // compute the next value of the iterator and then loop (equivalent + // to calling self.next()). + TransitionProperty::Shorthand(ref shorthand_id) => + self.longhand_iterator = Some(shorthand_id.longhands()), + TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {} + } + } + } +} diff --git a/components/style/properties/longhands/background.mako.rs b/components/style/properties/longhands/background.mako.rs index e0ef021f32d..1cbf601ad5d 100644 --- a/components/style/properties/longhands/background.mako.rs +++ b/components/style/properties/longhands/background.mako.rs @@ -21,10 +21,10 @@ ${helpers.predefined_type( ${helpers.predefined_type( "background-image", - "ImageLayer", + "Image", engines="gecko servo-2013 servo-2020", - initial_value="computed::ImageLayer::none()", - initial_specified_value="specified::ImageLayer::none()", + initial_value="computed::Image::None", + initial_specified_value="specified::Image::None", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector="True", animation_value_type="discrete", @@ -35,8 +35,8 @@ ${helpers.predefined_type( ${helpers.predefined_type( "background-position-" + axis, "position::" + direction + "Position", + "computed::LengthPercentage::zero_percent()", engines="gecko servo-2013 servo-2020", - initial_value="computed::LengthPercentage::zero()", initial_specified_value="SpecifiedValue::initial_specified_value()", spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis, animation_value_type="ComputedValue", @@ -107,7 +107,7 @@ ${helpers.single_keyword( """normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity""", - gecko_constant_prefix="NS_STYLE_BLEND", + gecko_enum_prefix="StyleBlend", vector=True, engines="gecko", animation_value_type="discrete", diff --git a/components/style/properties/longhands/border.mako.rs b/components/style/properties/longhands/border.mako.rs index f281fa1a30d..1c4eae16939 100644 --- a/components/style/properties/longhands/border.mako.rs +++ b/components/style/properties/longhands/border.mako.rs @@ -106,10 +106,10 @@ ${helpers.single_keyword( ${helpers.predefined_type( "border-image-source", - "ImageLayer", + "Image", engines="gecko servo-2013 servo-2020", - initial_value="computed::ImageLayer::none()", - initial_specified_value="specified::ImageLayer::none()", + initial_value="computed::Image::None", + initial_specified_value="specified::Image::None", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector=False, animation_value_type="discrete", diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 8946290918e..a4f2cc670f5 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -573,6 +573,7 @@ ${helpers.single_keyword( "backface-visibility", "visible hidden", engines="gecko servo-2013 servo-2020", + gecko_enum_prefix="StyleBackfaceVisibility", spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property", extra_prefixes=transform_extra_prefixes, animation_value_type="discrete", @@ -633,7 +634,7 @@ ${helpers.predefined_type( "Appearance", "computed::Appearance::None", engines="gecko", - alias="-webkit-appearance:layout.css.webkit-appearance.enabled", + alias="-webkit-appearance", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)", animation_value_type="discrete", gecko_ffi_name="mAppearance", @@ -679,10 +680,10 @@ ${helpers.predefined_type( ${helpers.predefined_type( "shape-outside", - "basic_shape::FloatAreaShape", - "generics::basic_shape::ShapeSource::None", + "basic_shape::ShapeOutside", + "generics::basic_shape::ShapeOutside::None", engines="gecko", - animation_value_type="basic_shape::FloatAreaShape", + animation_value_type="basic_shape::ShapeOutside", spec="https://drafts.csswg.org/css-shapes/#shape-outside-property", )} diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs index d34f13a4a76..f9cef1a34e2 100644 --- a/components/style/properties/longhands/effects.mako.rs +++ b/components/style/properties/longhands/effects.mako.rs @@ -83,7 +83,7 @@ ${helpers.single_keyword( color-burn hard-light soft-light difference exclusion hue saturation color luminosity""", engines="gecko servo-2013 servo-2020", - gecko_constant_prefix="NS_STYLE_BLEND", + gecko_enum_prefix="StyleBlend", animation_value_type="discrete", flags="CREATES_STACKING_CONTEXT", spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode", diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index 7e0bff6fbbe..98ed7fb7f9e 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -230,7 +230,7 @@ ${helpers.predefined_type( ${helpers.predefined_type( "-moz-script-level", "MozScriptLevel", - 0, + "0", engines="gecko", animation_value_type="none", enabled_in="ua", diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs index e8f3a0a45d9..fe2cc5ed024 100644 --- a/components/style/properties/longhands/inherited_box.mako.rs +++ b/components/style/properties/longhands/inherited_box.mako.rs @@ -86,6 +86,7 @@ ${helpers.single_keyword( ${helpers.single_keyword( "image-orientation", "none from-image", + gecko_pref_controlled_initial_value="layout.css.image-orientation.initial-from-image=from-image", engines="gecko", gecko_enum_prefix="StyleImageOrientation", animation_value_type="discrete", diff --git a/components/style/properties/longhands/inherited_svg.mako.rs b/components/style/properties/longhands/inherited_svg.mako.rs index 1839f90d6e6..73db38e0042 100644 --- a/components/style/properties/longhands/inherited_svg.mako.rs +++ b/components/style/properties/longhands/inherited_svg.mako.rs @@ -36,15 +36,16 @@ ${helpers.single_keyword( engines="gecko", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty", + gecko_enum_prefix="StyleColorInterpolation", )} ${helpers.single_keyword( "color-interpolation-filters", "linearrgb auto srgb", engines="gecko", - gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty", + gecko_enum_prefix="StyleColorInterpolation", )} ${helpers.predefined_type( diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index f01bedb177e..41614a04d92 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -141,11 +141,12 @@ ${helpers.predefined_type( % endif </%helpers:single_keyword> -${helpers.single_keyword( +${helpers.predefined_type( "text-align-last", - "auto start end left right center justify", + "TextAlignLast", + "computed::text::TextAlignLast::Auto", + needs_context=False, engines="gecko", - gecko_constant_prefix="NS_STYLE_TEXT_ALIGN", animation_value_type="discrete", spec="https://drafts.csswg.org/css-text/#propdef-text-align-last", )} @@ -244,7 +245,7 @@ ${helpers.predefined_type( ${helpers.predefined_type( "text-emphasis-style", "TextEmphasisStyle", - None, + "computed::TextEmphasisStyle::None", engines="gecko", initial_specified_value="SpecifiedValue::None", animation_value_type="discrete", @@ -367,13 +368,12 @@ ${helpers.single_keyword( servo_restyle_damage="rebuild_and_reflow", )} -// FIXME Firefox expects the initial value of this property to change depending -// on the value of the layout.css.control-characters.visible pref. ${helpers.single_keyword( "-moz-control-character-visibility", "hidden visible", engines="gecko", - gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY", + gecko_enum_prefix="StyleControlCharacterVisibility", + gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible", animation_value_type="none", gecko_ffi_name="mControlCharacterVisibility", spec="Nonstandard", diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index 660da1a5a37..d9403be5864 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -333,7 +333,7 @@ ${helpers.single_keyword( ${helpers.predefined_type( "object-position", "Position", - "computed::Position::zero()", + "computed::Position::center()", engines="gecko", boxed=True, spec="https://drafts.csswg.org/css-images-3/#the-object-position", @@ -375,7 +375,7 @@ ${helpers.predefined_type( ${helpers.predefined_type( "grid-auto-flow", "GridAutoFlow", - "computed::GridAutoFlow::row()", + "computed::GridAutoFlow::ROW", engines="gecko", animation_value_type="discrete", spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow", diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs index 724c38b2d1b..a09f3e7b656 100644 --- a/components/style/properties/longhands/svg.mako.rs +++ b/components/style/properties/longhands/svg.mako.rs @@ -10,6 +10,7 @@ ${helpers.single_keyword( "vector-effect", "none non-scaling-stroke", engines="gecko", + gecko_enum_prefix="StyleVectorEffect", animation_value_type="discrete", spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty", )} @@ -76,10 +77,10 @@ ${helpers.single_keyword( ${helpers.predefined_type( "clip-path", - "basic_shape::ClippingShape", - "generics::basic_shape::ShapeSource::None", + "basic_shape::ClipPath", + "generics::basic_shape::ClipPath::None", engines="gecko", - animation_value_type="basic_shape::ClippingShape", + animation_value_type="basic_shape::ClipPath", flags="CREATES_STACKING_CONTEXT", spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", )} @@ -110,7 +111,7 @@ ${helpers.predefined_type( ${helpers.predefined_type( "mask-position-" + axis, "position::" + direction + "Position", - "computed::LengthPercentage::zero()", + "computed::LengthPercentage::zero_percent()", engines="gecko", extra_prefixes="webkit", initial_specified_value="specified::PositionComponent::Center", @@ -164,6 +165,7 @@ ${helpers.single_keyword( "mask-composite", "add subtract intersect exclude", engines="gecko", + gecko_enum_prefix="StyleMaskComposite", vector=True, extra_prefixes="webkit", animation_value_type="discrete", @@ -172,10 +174,10 @@ ${helpers.single_keyword( ${helpers.predefined_type( "mask-image", - "ImageLayer", + "Image", engines="gecko", - initial_value="computed::ImageLayer::none()", - initial_specified_value="specified::ImageLayer::none()", + initial_value="computed::Image::None", + initial_specified_value="specified::Image::None", parse_method="parse_with_cors_anonymous", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", vector=True, diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs index 05d22abd9e9..8aaee392662 100644 --- a/components/style/properties/longhands/ui.mako.rs +++ b/components/style/properties/longhands/ui.mako.rs @@ -56,7 +56,7 @@ ${helpers.single_keyword( ${helpers.single_keyword( "-moz-window-shadow", - "none default menu tooltip sheet", + "default none menu tooltip sheet", engines="gecko", gecko_ffi_name="mWindowShadow", gecko_enum_prefix="StyleWindowShadow", diff --git a/components/style/properties/longhands/xul.mako.rs b/components/style/properties/longhands/xul.mako.rs index d5c4011eaf6..a981f5ba795 100644 --- a/components/style/properties/longhands/xul.mako.rs +++ b/components/style/properties/longhands/xul.mako.rs @@ -64,10 +64,12 @@ ${helpers.single_keyword( spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)", )} +// NOTE(heycam): Odd that the initial value is 1 yet 0 is a valid value. There +// are uses of `-moz-box-ordinal-group: 0` in the tree, too. ${helpers.predefined_type( "-moz-box-ordinal-group", "Integer", - "0", + "1", engines="gecko", parse_method="parse_non_negative", alias="-webkit-box-ordinal-group", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index d3e61867518..4bebe0040e7 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -312,7 +312,7 @@ impl Clone for PropertyDeclaration { trait AssertCopy { fn assert() {} } trait AssertNotCopy { fn assert() {} } impl<T: Copy> AssertCopy for Helper<T> {} - % for ty in set(x["type"] for x in others): + % for ty in sorted(set(x["type"] for x in others)): impl AssertNotCopy for Helper<${ty}> {} Helper::<${ty}>::assert(); % endfor @@ -729,10 +729,10 @@ impl NonCustomPropertyIdSet { <%def name="static_non_custom_property_id_set(name, is_member)"> static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet { <% - storage = [0] * ((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32) + storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32) for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()): if is_member(property): - storage[i / 32] |= 1 << (i % 32) + storage[int(i / 32)] |= 1 << (i % 32) %> storage: [${", ".join("0x%x" % word for word in storage)}] }; @@ -741,15 +741,61 @@ static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet { <%def name="static_longhand_id_set(name, is_member)"> static ${name}: LonghandIdSet = LonghandIdSet { <% - storage = [0] * ((len(data.longhands) - 1 + 32) / 32) + storage = [0] * int((len(data.longhands) - 1 + 32) / 32) for i, property in enumerate(data.longhands): if is_member(property): - storage[i / 32] |= 1 << (i % 32) + storage[int(i / 32)] |= 1 << (i % 32) %> storage: [${", ".join("0x%x" % word for word in storage)}] }; </%def> +<% + logical_groups = defaultdict(list) + for prop in data.longhands: + if prop.logical_group: + logical_groups[prop.logical_group].append(prop) + + for group, props in logical_groups.items(): + logical_count = sum(1 for p in props if p.logical) + if logical_count * 2 != len(props): + raise RuntimeError("Logical group {} has ".format(group) + + "unbalanced logical / physical properties") + + FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data) + FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data) + MARKER_RESTRICTIONS = PropertyRestrictions.marker(data) + PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data) + CUE_RESTRICTIONS = PropertyRestrictions.cue(data) + + def restriction_flags(property): + name = property.name + flags = [] + if name in FIRST_LINE_RESTRICTIONS: + flags.append("APPLIES_TO_FIRST_LINE") + if name in FIRST_LETTER_RESTRICTIONS: + flags.append("APPLIES_TO_FIRST_LETTER") + if name in PLACEHOLDER_RESTRICTIONS: + flags.append("APPLIES_TO_PLACEHOLDER") + if name in MARKER_RESTRICTIONS: + flags.append("APPLIES_TO_MARKER") + if name in CUE_RESTRICTIONS: + flags.append("APPLIES_TO_CUE") + return flags + +%> + +/// A group for properties which may override each other +/// via logical resolution. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub enum LogicalGroup { + % for group in sorted(logical_groups.keys()): + /// ${group} + ${to_camel_case(group)}, + % endfor +} + + /// A set of longhand properties #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)] pub struct LonghandIdSet { @@ -837,6 +883,30 @@ impl LonghandIdSet { &HAS_NO_EFFECT_ON_SCROLLBARS } + /// Returns the set of padding properties for the purpose of disabling + /// native appearance. + #[inline] + pub fn padding_properties() -> &'static Self { + <% assert "padding" in logical_groups %> + ${static_longhand_id_set( + "PADDING_PROPERTIES", + lambda p: p.logical_group == "padding" + )} + &PADDING_PROPERTIES + } + + /// Returns the set of border properties for the purpose of disabling native + /// appearance. + #[inline] + pub fn border_background_properties() -> &'static Self { + ${static_longhand_id_set( + "BORDER_BACKGROUND_PROPERTIES", + lambda p: (p.logical_group and p.logical_group.startswith("border")) or \ + p.name in ["background-color", "background-image"] + )} + &BORDER_BACKGROUND_PROPERTIES + } + /// Iterate over the current longhand id set. pub fn iter(&self) -> LonghandIdSetIterator { LonghandIdSetIterator { longhands: self, cur: 0, } @@ -998,53 +1068,8 @@ bitflags! { } } -<% - logical_groups = defaultdict(list) - for prop in data.longhands: - if prop.logical_group: - logical_groups[prop.logical_group].append(prop) - - for group, props in logical_groups.iteritems(): - logical_count = sum(1 for p in props if p.logical) - if logical_count * 2 != len(props): - raise RuntimeError("Logical group {} has ".format(group) + - "unbalanced logical / physical properties") - - FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data) - FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data) - MARKER_RESTRICTIONS = PropertyRestrictions.marker(data) - PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data) - CUE_RESTRICTIONS = PropertyRestrictions.cue(data) - - def restriction_flags(property): - name = property.name - flags = [] - if name in FIRST_LINE_RESTRICTIONS: - flags.append("APPLIES_TO_FIRST_LINE") - if name in FIRST_LETTER_RESTRICTIONS: - flags.append("APPLIES_TO_FIRST_LETTER") - if name in PLACEHOLDER_RESTRICTIONS: - flags.append("APPLIES_TO_PLACEHOLDER") - if name in MARKER_RESTRICTIONS: - flags.append("APPLIES_TO_MARKER") - if name in CUE_RESTRICTIONS: - flags.append("APPLIES_TO_CUE") - return flags - -%> - -/// A group for properties which may override each other -/// via logical resolution. -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub enum LogicalGroup { - % for group in logical_groups.iterkeys(): - /// ${group} - ${to_camel_case(group)}, - % endfor -} - /// An identifier for a given longhand property. -#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(u16)] pub enum LonghandId { % for i, property in enumerate(data.longhands): @@ -1089,6 +1114,7 @@ impl LonghandId { // could potentially do so, which would speed up serialization // algorithms and what not, I guess. <% + from functools import cmp_to_key longhand_to_shorthand_map = {} num_sub_properties = {} for shorthand in data.shorthands: @@ -1099,6 +1125,9 @@ impl LonghandId { longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case) + def cmp(a, b): + return (a > b) - (a < b) + def preferred_order(x, y): # Since we want properties in order from most subproperties to least, # reverse the arguments to cmp from the expected order. @@ -1110,8 +1139,8 @@ impl LonghandId { # Sort the lists of shorthand properties according to preferred order: # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order - for shorthand_list in longhand_to_shorthand_map.itervalues(): - shorthand_list.sort(cmp=preferred_order) + for shorthand_list in longhand_to_shorthand_map.values(): + shorthand_list.sort(key=cmp_to_key(preferred_order)) %> // based on lookup results for each longhand, create result arrays @@ -1353,7 +1382,7 @@ where } /// An identifier for a given shorthand property. -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(u16)] pub enum ShorthandId { % for i, property in enumerate(data.shorthands): @@ -1590,10 +1619,7 @@ impl UnparsedValue { } else { CSSWideKeyword::Initial }; - PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { - id: longhand_id, - keyword, - }) + PropertyDeclaration::css_wide_keyword(longhand_id, keyword) }; let css = match crate::custom_properties::substitute( @@ -1630,10 +1656,7 @@ impl UnparsedValue { let mut input = Parser::new(&mut input); input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. if let Ok(keyword) = input.try(CSSWideKeyword::parse) { - return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { - id: longhand_id, - keyword, - }); + return PropertyDeclaration::css_wide_keyword(longhand_id, keyword); } let declaration = input.parse_entirely(|input| { @@ -2239,6 +2262,12 @@ impl PropertyDeclaration { } } + /// Returns a CSS-wide keyword declaration for a given property. + #[inline] + pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self { + Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword }) + } + /// Returns a CSS-wide keyword if the declaration's value is one. #[inline] pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> { @@ -2367,9 +2396,7 @@ impl PropertyDeclaration { PropertyId::Longhand(id) => { input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. input.try(CSSWideKeyword::parse).map(|keyword| { - PropertyDeclaration::CSSWideKeyword( - WideKeywordDeclaration { id, keyword }, - ) + PropertyDeclaration::css_wide_keyword(id, keyword) }).or_else(|()| { input.look_for_var_or_env_functions(); input.parse_entirely(|input| id.parse_value(context, input)) @@ -2403,12 +2430,7 @@ impl PropertyDeclaration { declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) } else { for longhand in id.longhands() { - declarations.push(PropertyDeclaration::CSSWideKeyword( - WideKeywordDeclaration { - id: longhand, - keyword, - }, - )) + declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword)); } } } else { @@ -2550,12 +2572,7 @@ impl<'a> Iterator for AllShorthandDeclarationIterator<'a> { match *self.all_shorthand { AllShorthand::NotSet => None, AllShorthand::CSSWideKeyword(ref keyword) => { - Some(PropertyDeclaration::CSSWideKeyword( - WideKeywordDeclaration { - id: self.longhands.next()?, - keyword: *keyword - } - )) + Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword)) } AllShorthand::WithVariables(ref unparsed) => { Some(PropertyDeclaration::WithVariables( @@ -3003,6 +3020,13 @@ impl ComputedValues { % endfor set } + + /// Create a `TransitionPropertyIterator` for this styles transition properties. + pub fn transition_properties<'a>( + &'a self + ) -> animated_properties::TransitionPropertyIterator<'a> { + animated_properties::TransitionPropertyIterator::from_style(self) + } } #[cfg(feature = "servo")] diff --git a/components/style/properties/shorthands/border.mako.rs b/components/style/properties/shorthands/border.mako.rs index 1f77b905021..e5122153db7 100644 --- a/components/style/properties/shorthands/border.mako.rs +++ b/components/style/properties/shorthands/border.mako.rs @@ -70,11 +70,10 @@ pub fn parse_border<'i, 't>( let mut width = None; let mut any = false; loop { - if color.is_none() { - if let Ok(value) = input.try(|i| Color::parse(context, i)) { - color = Some(value); + if width.is_none() { + if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) { + width = Some(value); any = true; - continue } } if style.is_none() { @@ -84,9 +83,9 @@ pub fn parse_border<'i, 't>( continue } } - if width.is_none() { - if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) { - width = Some(value); + if color.is_none() { + if let Ok(value) = input.try(|i| Color::parse(context, i)) { + color = Some(value); any = true; continue } diff --git a/components/style/properties/shorthands/position.mako.rs b/components/style/properties/shorthands/position.mako.rs index d9cb9da3342..48e56e6ef3c 100644 --- a/components/style/properties/shorthands/position.mako.rs +++ b/components/style/properties/shorthands/position.mako.rs @@ -549,7 +549,7 @@ use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow}; use crate::values::generics::grid::GridTemplateComponent; use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks}; - use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas}; + use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas}; pub fn parse_value<'i, 't>( context: &ParserContext, @@ -566,28 +566,28 @@ input: &mut Parser<'i, 't>, is_row: bool, ) -> Result<GridAutoFlow, ParseError<'i>> { - let mut auto_flow = None; - let mut dense = false; + let mut track = None; + let mut dense = GridAutoFlow::empty(); + for _ in 0..2 { if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() { - auto_flow = if is_row { - Some(AutoFlow::Row) + track = if is_row { + Some(GridAutoFlow::ROW) } else { - Some(AutoFlow::Column) + Some(GridAutoFlow::COLUMN) }; } else if input.try(|i| i.expect_ident_matching("dense")).is_ok() { - dense = true; + dense = GridAutoFlow::DENSE } else { break } } - auto_flow.map(|flow| { - GridAutoFlow { - autoflow: flow, - dense: dense, - } - }).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + if track.is_some() { + Ok(track.unwrap() | dense) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } } if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) { @@ -637,7 +637,7 @@ self.grid_template_areas, dest); } - if self.grid_auto_flow.autoflow == AutoFlow::Column { + if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) { // It should fail to serialize if other branch of the if condition's values are set. if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() { @@ -653,7 +653,7 @@ self.grid_template_rows.to_css(dest)?; dest.write_str(" / auto-flow")?; - if self.grid_auto_flow.dense { + if self.grid_auto_flow.contains(GridAutoFlow::DENSE) { dest.write_str(" dense")?; } @@ -676,7 +676,7 @@ } dest.write_str("auto-flow")?; - if self.grid_auto_flow.dense { + if self.grid_auto_flow.contains(GridAutoFlow::DENSE) { dest.write_str(" dense")?; } diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index 9841eb9e70f..1550a0368a4 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -55,11 +55,6 @@ pub fn containing_shadow_ignoring_svg_use<E: TElement>( } } -#[inline] -fn sort_rules_from(rules: &mut ApplicableDeclarationList, start: usize) { - rules[start..].sort_unstable_by_key(|block| (block.specificity, block.source_order())); -} - /// An object that we use with all the intermediate state needed for the /// cascade. /// @@ -82,6 +77,7 @@ where flags_setter: &'a mut F, matches_user_and_author_rules: bool, matches_document_author_rules: bool, + in_sort_scope: bool, } impl<'a, 'b: 'a, E, F: 'a> RuleCollector<'a, 'b, E, F> @@ -134,9 +130,36 @@ where rules, matches_user_and_author_rules, matches_document_author_rules: matches_user_and_author_rules, + in_sort_scope: false, } } + /// Sets up the state necessary to collect rules from a given DOM tree + /// (either the document tree, or a shadow tree). + /// + /// All rules in the same tree need to be matched together, and this + /// function takes care of sorting them by specificity and source order. + #[inline] + fn in_tree(&mut self, host: Option<E>, f: impl FnOnce(&mut Self)) { + debug_assert!(!self.in_sort_scope, "Nested sorting makes no sense"); + let start = self.rules.len(); + self.in_sort_scope = true; + let old_host = self.context.current_host.take(); + self.context.current_host = host.map(|e| e.opaque()); + f(self); + if start != self.rules.len() { + self.rules[start..] + .sort_unstable_by_key(|block| (block.specificity, block.source_order())); + } + self.context.current_host = old_host; + self.in_sort_scope = false; + } + + #[inline] + fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) { + self.in_tree(Some(host), f); + } + fn collect_stylist_rules(&mut self, origin: Origin) { let cascade_level = match origin { Origin::UserAgent => CascadeLevel::UANormal, @@ -150,7 +173,9 @@ where None => return, }; - self.collect_rules_internal(None, map, cascade_level); + self.in_tree(None, |collector| { + collector.collect_rules_in_map(map, cascade_level); + }); } fn collect_user_agent_rules(&mut self) { @@ -189,39 +214,30 @@ where } } - fn collect_rules_in_shadow_tree( - &mut self, - shadow_host: E, - map: &SelectorMap<Rule>, - cascade_level: CascadeLevel, - ) { - debug_assert!(shadow_host.shadow_root().is_some()); - self.collect_rules_internal(Some(shadow_host), map, cascade_level); + #[inline] + fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel) { + debug_assert!(self.in_sort_scope, "Rules gotta be sorted"); + SelectorMap::get_matching_rules( + self.element, + part_rules, + &mut self.rules, + &mut self.context, + &mut self.flags_setter, + cascade_level, + ); } #[inline] - fn collect_rules_internal( - &mut self, - shadow_host: Option<E>, - map: &SelectorMap<Rule>, - cascade_level: CascadeLevel, - ) { - let element = self.element; - let rule_hash_target = self.rule_hash_target; - let rules = &mut self.rules; - let flags_setter = &mut self.flags_setter; - let start = rules.len(); - self.context.with_shadow_host(shadow_host, |context| { - map.get_all_matching_rules( - element, - rule_hash_target, - rules, - context, - flags_setter, - cascade_level, - ); - }); - sort_rules_from(rules, start); + fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel) { + debug_assert!(self.in_sort_scope, "Rules gotta be sorted"); + map.get_all_matching_rules( + self.element, + self.rule_hash_target, + &mut self.rules, + &mut self.context, + &mut self.flags_setter, + cascade_level, + ); } /// Collects the rules for the ::slotted pseudo-element and the :host @@ -258,17 +274,16 @@ where None => continue, }; - self.collect_rules_in_shadow_tree( - shadow.host(), - slotted_rules, - CascadeLevel::AuthorNormal { + self.in_shadow_tree(shadow.host(), |collector| { + let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, - }, - ); + }; + collector.collect_rules_in_map(slotted_rules, cascade_level); + }); } } - fn collect_normal_rules_from_containing_shadow_tree(&mut self) { + fn collect_rules_from_containing_shadow_tree(&mut self) { if !self.matches_user_and_author_rules { return; } @@ -281,11 +296,34 @@ where self.matches_document_author_rules = false; - let cascade_data = containing_shadow.style_data(); - let host = containing_shadow.host(); - if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) { - self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal()); - } + let cascade_data = match containing_shadow.style_data() { + Some(c) => c, + None => return, + }; + + let cascade_level = CascadeLevel::same_tree_author_normal(); + self.in_shadow_tree(containing_shadow.host(), |collector| { + if let Some(map) = cascade_data.normal_rules(collector.pseudo_element) { + collector.collect_rules_in_map(map, cascade_level); + } + + // Collect rules from :host::part() and such + let hash_target = collector.rule_hash_target; + if !hash_target.has_part_attr() { + return; + } + + let part_rules = match cascade_data.part_rules(collector.pseudo_element) { + Some(p) => p, + None => return, + }; + + hash_target.each_part(|part| { + if let Some(part_rules) = part_rules.get(part) { + collector.collect_rules_in_list(part_rules, cascade_level); + } + }); + }); } /// Collects the rules for the :host pseudo-class. @@ -311,13 +349,12 @@ where }; let rule_hash_target = self.rule_hash_target; - self.collect_rules_in_shadow_tree( - rule_hash_target, - host_rules, - CascadeLevel::AuthorNormal { + self.in_shadow_tree(rule_hash_target, |collector| { + let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, - }, - ); + }; + collector.collect_rules_in_map(host_rules, cascade_level); + }); } fn collect_document_author_rules(&mut self) { @@ -328,7 +365,7 @@ where self.collect_stylist_rules(Origin::Author); } - fn collect_part_rules(&mut self) { + fn collect_part_rules_from_outer_trees(&mut self) { if !self.rule_hash_target.has_part_attr() { return; } @@ -363,28 +400,16 @@ where if let Some(part_rules) = part_rules { let containing_host = outer_shadow.map(|s| s.host()); - let element = self.element; - let rules = &mut self.rules; - let flags_setter = &mut self.flags_setter; let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, }; - let start = rules.len(); - self.context.with_shadow_host(containing_host, |context| { + self.in_tree(containing_host, |collector| { for p in &parts { if let Some(part_rules) = part_rules.get(p) { - SelectorMap::get_matching_rules( - element, - &part_rules, - rules, - context, - flags_setter, - cascade_level, - ); + collector.collect_rules_in_list(part_rules, cascade_level); } } }); - sort_rules_from(rules, start); shadow_cascade_order.inc(); } @@ -393,14 +418,13 @@ where None => break, // Nowhere to export to. }; - parts.retain(|part| { - let exported_part = match inner_shadow_host.exported_part(part) { - Some(part) => part, - None => return false, - }; - std::mem::replace(part, exported_part); - true - }); + let mut new_parts = SmallVec::new(); + for part in &parts { + inner_shadow_host.each_exported_part(part, |exported_part| { + new_parts.push(exported_part.clone()); + }); + } + parts = new_parts; } } @@ -461,10 +485,10 @@ where return; } self.collect_host_and_slotted_rules(); - self.collect_normal_rules_from_containing_shadow_tree(); + self.collect_rules_from_containing_shadow_tree(); self.collect_document_author_rules(); self.collect_style_attribute(); - self.collect_part_rules(); + self.collect_part_rules_from_outer_trees(); self.collect_animation_rules(); } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 113fa9cb9bb..484cdee5183 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -7,8 +7,6 @@ //! The rule tree. use crate::applicable_declarations::ApplicableDeclarationList; -#[cfg(feature = "gecko")] -use crate::gecko::selector_parser::PseudoElement; use crate::hash::{self, FxHashMap}; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; @@ -136,29 +134,6 @@ impl StyleSource { let _ = write!(writer, " -> {:?}", self.read(guard).declarations()); } - // This is totally unsafe, should be removed when we figure out the cause of - // bug 1607553. - #[cfg(feature = "gecko")] - unsafe fn dump_unchecked<W: Write>(&self, writer: &mut W) { - if let Some(ref rule) = self.0.as_first() { - let rule = rule.read_unchecked(); - let _ = write!(writer, "{:?}", rule.selectors); - } - let _ = write!(writer, " -> {:?}", self.read_unchecked().declarations()); - } - - // This is totally unsafe, should be removed when we figure out the cause of - // bug 1607553. - #[inline] - #[cfg(feature = "gecko")] - unsafe fn read_unchecked(&self) -> &PropertyDeclarationBlock { - let block: &Locked<PropertyDeclarationBlock> = match self.0.borrow() { - ArcUnionBorrow::First(ref rule) => &rule.get().read_unchecked().block, - ArcUnionBorrow::Second(ref block) => block.get(), - }; - block.read_unchecked() - } - /// Read the style source guard, and obtain thus read access to the /// underlying property declaration block. #[inline] @@ -1441,198 +1416,6 @@ impl StrongRuleNode { } } - /// Returns true if any properties specified by `rule_type_mask` was set by - /// an author rule. - #[cfg(feature = "gecko")] - pub fn has_author_specified_rules<E>( - &self, - mut element: E, - mut pseudo: Option<PseudoElement>, - guards: &StylesheetGuards, - rule_type_mask: u32, - author_colors_allowed: bool, - ) -> bool - where - E: crate::dom::TElement, - { - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BACKGROUND; - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BORDER; - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING; - use crate::properties::{CSSWideKeyword, LonghandId}; - use crate::properties::{PropertyDeclaration, PropertyDeclarationId}; - use std::borrow::Cow; - - // Reset properties: - const BACKGROUND_PROPS: &'static [LonghandId] = - &[LonghandId::BackgroundColor, LonghandId::BackgroundImage]; - - const BORDER_PROPS: &'static [LonghandId] = &[ - LonghandId::BorderTopColor, - LonghandId::BorderTopStyle, - LonghandId::BorderTopWidth, - LonghandId::BorderRightColor, - LonghandId::BorderRightStyle, - LonghandId::BorderRightWidth, - LonghandId::BorderBottomColor, - LonghandId::BorderBottomStyle, - LonghandId::BorderBottomWidth, - LonghandId::BorderLeftColor, - LonghandId::BorderLeftStyle, - LonghandId::BorderLeftWidth, - LonghandId::BorderTopLeftRadius, - LonghandId::BorderTopRightRadius, - LonghandId::BorderBottomRightRadius, - LonghandId::BorderBottomLeftRadius, - LonghandId::BorderInlineStartColor, - LonghandId::BorderInlineStartStyle, - LonghandId::BorderInlineStartWidth, - LonghandId::BorderInlineEndColor, - LonghandId::BorderInlineEndStyle, - LonghandId::BorderInlineEndWidth, - LonghandId::BorderBlockStartColor, - LonghandId::BorderBlockStartStyle, - LonghandId::BorderBlockStartWidth, - LonghandId::BorderBlockEndColor, - LonghandId::BorderBlockEndStyle, - LonghandId::BorderBlockEndWidth, - ]; - - const PADDING_PROPS: &'static [LonghandId] = &[ - LonghandId::PaddingTop, - LonghandId::PaddingRight, - LonghandId::PaddingBottom, - LonghandId::PaddingLeft, - LonghandId::PaddingInlineStart, - LonghandId::PaddingInlineEnd, - LonghandId::PaddingBlockStart, - LonghandId::PaddingBlockEnd, - ]; - - // Set of properties that we are currently interested in. - let mut properties = LonghandIdSet::new(); - - if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 { - for id in BACKGROUND_PROPS { - properties.insert(*id); - } - } - if rule_type_mask & NS_AUTHOR_SPECIFIED_BORDER != 0 { - for id in BORDER_PROPS { - properties.insert(*id); - } - } - if rule_type_mask & NS_AUTHOR_SPECIFIED_PADDING != 0 { - for id in PADDING_PROPS { - properties.insert(*id); - } - } - - // If author colors are not allowed, don't look at those properties - // (except for background-color which is special and we handle below). - if !author_colors_allowed { - properties.remove_all(LonghandIdSet::ignored_when_colors_disabled()); - if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 { - properties.insert(LonghandId::BackgroundColor); - } - } - - let mut element_rule_node = Cow::Borrowed(self); - - loop { - // We need to be careful not to count styles covered up by - // user-important or UA-important declarations. But we do want to - // catch explicit inherit styling in those and check our parent - // element to see whether we have user styling for those properties. - // Note that we don't care here about inheritance due to lack of a - // specified value, since all the properties we care about are reset - // properties. - - let mut inherited_properties = LonghandIdSet::new(); - let mut have_explicit_ua_inherit = false; - - for node in element_rule_node.self_and_ancestors() { - let source = node.style_source(); - let declarations = if source.is_some() { - source - .as_ref() - .unwrap() - .read(node.cascade_level().guard(guards)) - .declaration_importance_iter() - } else { - continue; - }; - - // Iterate over declarations of the longhands we care about. - let node_importance = node.importance(); - let longhands = declarations.rev().filter_map(|(declaration, importance)| { - if importance != node_importance { - return None; - } - match declaration.id() { - PropertyDeclarationId::Longhand(id) => Some((id, declaration)), - _ => None, - } - }); - - let is_author = node.cascade_level().origin() == Origin::Author; - for (id, declaration) in longhands { - if !properties.contains(id) { - continue; - } - - if is_author { - if !author_colors_allowed { - if let PropertyDeclaration::BackgroundColor(ref color) = *declaration { - if color.is_transparent() { - return true; - } - continue; - } - } - return true; - } - - // This property was set by a non-author rule. - // Stop looking for it in this element's rule - // nodes. - properties.remove(id); - - // However, if it is inherited, then it might be - // inherited from an author rule from an - // ancestor element's rule nodes. - if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) { - have_explicit_ua_inherit = true; - inherited_properties.insert(id); - } - } - } - - if !have_explicit_ua_inherit { - break; - } - - // Continue to the parent element and search for the inherited properties. - if let Some(pseudo) = pseudo.take() { - if pseudo.inherits_from_default_values() { - break; - } - } else { - element = match element.inheritance_parent() { - Some(parent) => parent, - None => break, - }; - - let parent_data = element.mutate_data().unwrap(); - let parent_rule_node = parent_data.styles.primary().rules().clone(); - element_rule_node = Cow::Owned(parent_rule_node); - } - - properties = inherited_properties; - } - - false - } - /// Returns true if there is either animation or transition level rule. pub fn has_animation_or_transition_rules(&self) -> bool { self.self_and_ancestors() @@ -1742,47 +1525,6 @@ impl Drop for StrongRuleNode { return; } - #[cfg(feature = "gecko")] - #[inline(always)] - fn assert_on_release() -> bool { - crate::gecko_bindings::structs::GECKO_IS_NIGHTLY - } - - #[cfg(feature = "servo")] - fn assert_on_release() -> bool { - false - } - - if cfg!(debug_assertions) || assert_on_release() { - let children = node.children.read(); - if !children.is_empty() { - let mut crash_str = vec![]; - - #[cfg(feature = "gecko")] - unsafe { - // Try to unsafely collect some information of this before - // crashing the process. - if let Some(ref s) = node.source { - s.dump_unchecked(&mut crash_str); - crash_str.push(b'\n'); - } - children.each(|child| { - (*child.ptr()) - .source - .as_ref() - .unwrap() - .dump_unchecked(&mut crash_str); - crash_str.push(b'\n'); - }); - } - - panic!( - "Children left in the rule tree on drop: {}", - String::from_utf8_lossy(&crash_str).trim() - ); - } - } - if node.parent.is_none() { debug!("Dropping root node!"); // The free list should be null by this point diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 599b603f4ff..2537cae9f83 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -709,10 +709,6 @@ impl ElementSnapshot for ServoElementSnapshot { false } - fn exported_part(&self, _: &Atom) -> Option<Atom> { - None - } - fn imported_part(&self, _: &Atom) -> Option<Atom> { None } diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index a2c6e5a2928..c2a15f7fa0e 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -227,15 +227,21 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { fn set_bits(&mut self) { let display = self.style.get_box().clone_display(); - if !display.is_contents() && - !self + if !display.is_contents() { + if !self .style .get_text() .clone_text_decoration_line() .is_empty() - { - self.style - .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); + { + self.style + .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); + } + + if self.style.get_effects().clone_opacity() == 0. { + self.style + .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE); + } } if self.style.is_pseudo_element() { @@ -488,6 +494,35 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } + /// <textarea>'s editor root needs to inherit the overflow value from its + /// parent, but we need to make sure it's still scrollable. + #[cfg(feature = "gecko")] + fn adjust_for_text_control_editing_root(&mut self) { + use crate::selector_parser::PseudoElement; + + if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) { + return; + } + + let box_style = self.style.get_box(); + let overflow_x = box_style.clone_overflow_x(); + let overflow_y = box_style.clone_overflow_y(); + + fn scrollable(v: Overflow) -> bool { + v != Overflow::MozHiddenUnscrollable && v != Overflow::Visible + } + + // If at least one is scrollable we'll adjust the other one in + // adjust_for_overflow if needed. + if scrollable(overflow_x) || scrollable(overflow_y) { + return; + } + + let box_style = self.style.mutate_box(); + box_style.set_overflow_x(Overflow::Auto); + box_style.set_overflow_y(Overflow::Auto); + } + /// If a <fieldset> has grid/flex display type, we need to inherit /// this type into its ::-moz-fieldset-content anonymous box. /// @@ -496,9 +531,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { /// normal cascading process. #[cfg(feature = "gecko")] fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) { - match self.style.pseudo { - Some(ref p) if p.is_fieldset_content() => {}, - _ => return, + use crate::selector_parser::PseudoElement; + + if self.style.pseudo != Some(&PseudoElement::FieldsetContent) { + return; } debug_assert_eq!(self.style.get_box().clone_display(), Display::Block); @@ -780,6 +816,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { { self.adjust_for_prohibited_display_contents(element); self.adjust_for_fieldset_content(layout_parent_style); + // NOTE: It's important that this happens before + // adjust_for_overflow. + self.adjust_for_text_control_editing_root(); } self.adjust_for_top_layer(); self.blockify_if_necessary(layout_parent_style, element); diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index cca08c9d233..a9cd39eef20 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -294,7 +294,9 @@ where // Removing sheets makes us tear down the whole cascade and invalidation // data, but only if the sheet has been involved in at least one flush. // Checking whether the sheet has been committed allows us to avoid - // rebuilding the world when sites quickly append and remove a stylesheet. + // rebuilding the world when sites quickly append and remove a + // stylesheet. + // // See bug 1434756. if sheet.committed { self.set_data_validity_at_least(DataValidity::FullyInvalid); diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index c9697ead615..daf97f20e3c 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -58,8 +58,8 @@ pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; pub use self::rules_iterator::{AllRules, EffectiveRules}; pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator}; pub use self::style_rule::StyleRule; +pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind}; pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; -pub use self::stylesheet::{SanitizationData, SanitizationKind}; pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; pub use self::supports_rule::SupportsRule; pub use self::viewport_rule::ViewportRule; @@ -369,6 +369,7 @@ impl CssRule { shared_lock: &SharedRwLock, state: State, loader: Option<&dyn StylesheetLoader>, + allow_import_rules: AllowImportRules, ) -> Result<Self, RulesMutateError> { let url_data = parent_stylesheet_contents.url_data.read(); let context = ParserContext::new( @@ -395,6 +396,7 @@ impl CssRule { dom_error: None, namespaces: &mut *guard, insert_rule_context: Some(insert_rule_context), + allow_import_rules, }; parse_one_rule(&mut input, &mut rule_parser) diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs index 05f93eca489..d84a738bca0 100644 --- a/components/style/stylesheets/rule_list.rs +++ b/components/style/stylesheets/rule_list.rs @@ -10,7 +10,7 @@ use crate::str::CssStringWriter; use crate::stylesheets::loader::StylesheetLoader; use crate::stylesheets::rule_parser::{InsertRuleContext, State}; use crate::stylesheets::stylesheet::StylesheetContents; -use crate::stylesheets::{CssRule, RulesMutateError}; +use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; use servo_arc::{Arc, RawOffsetArc}; @@ -128,6 +128,7 @@ pub trait CssRulesHelpers { index: usize, nested: bool, loader: Option<&dyn StylesheetLoader>, + allow_import_rules: AllowImportRules, ) -> Result<CssRule, RulesMutateError>; } @@ -140,6 +141,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> { index: usize, nested: bool, loader: Option<&dyn StylesheetLoader>, + allow_import_rules: AllowImportRules, ) -> Result<CssRule, RulesMutateError> { let new_rule = { let read_guard = lock.read(); @@ -176,6 +178,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> { lock, state, loader, + allow_import_rules, )? }; diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index aefea6c504d..74425afafcd 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -19,6 +19,7 @@ use crate::stylesheets::keyframes_rule::parse_keyframe_list; use crate::stylesheets::stylesheet::Namespaces; use crate::stylesheets::supports_rule::SupportsCondition; use crate::stylesheets::viewport_rule; +use crate::stylesheets::AllowImportRules; use crate::stylesheets::{CorsMode, DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; use crate::stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader}; use crate::stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule}; @@ -50,7 +51,7 @@ pub struct TopLevelRuleParser<'a> { /// This won't contain any namespaces, and only nested parsers created with /// `ParserContext::new_with_rule_type` will. pub context: ParserContext<'a>, - /// The current state of the parser. + /// The current stajkj/te of the parser. pub state: State, /// Whether we have tried to parse was invalid due to being in the wrong /// place (e.g. an @import rule was found while in the `Body` state). Reset @@ -62,6 +63,8 @@ pub struct TopLevelRuleParser<'a> { pub namespaces: &'a mut Namespaces, /// The info we need insert a rule in a list. pub insert_rule_context: Option<InsertRuleContext<'a>>, + /// Whether @import rules will be allowed. + pub allow_import_rules: AllowImportRules, } impl<'b> TopLevelRuleParser<'b> { @@ -189,6 +192,10 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) } + if let AllowImportRules::No = self.allow_import_rules { + return Err(input.new_custom_error(StyleParseErrorKind::DisallowedImportRule)) + } + // FIXME(emilio): We should always be able to have a loader // around! See bug 1533783. if self.loader.is_none() { @@ -203,6 +210,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { let media = Arc::new(self.shared_lock.wrap(media)); let prelude = AtRuleNonBlockPrelude::Import(url, media); + return Ok(AtRuleType::WithoutBlock(prelude)); }, "namespace" => { diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 068bfb6502a..6679f587a41 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -81,6 +81,7 @@ impl StylesheetContents { quirks_mode: QuirksMode, line_number_offset: u32, use_counters: Option<&UseCounters>, + allow_import_rules: AllowImportRules, sanitization_data: Option<&mut SanitizationData>, ) -> Self { let namespaces = RwLock::new(Namespaces::default()); @@ -95,6 +96,7 @@ impl StylesheetContents { quirks_mode, line_number_offset, use_counters, + allow_import_rules, sanitization_data, ); @@ -355,6 +357,16 @@ pub enum SanitizationKind { NoConditionalRules, } +/// Whether @import rules are allowed. +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AllowImportRules { + /// @import rules will be parsed. + Yes, + /// @import rules will not be parsed. + No, +} + impl SanitizationKind { fn allows(self, rule: &CssRule) -> bool { debug_assert_ne!(self, SanitizationKind::None); @@ -415,6 +427,7 @@ impl Stylesheet { stylesheet_loader: Option<&dyn StylesheetLoader>, error_reporter: Option<&dyn ParseErrorReporter>, line_number_offset: u32, + allow_import_rules: AllowImportRules, ) { let namespaces = RwLock::new(Namespaces::default()); @@ -430,6 +443,7 @@ impl Stylesheet { existing.contents.quirks_mode, line_number_offset, /* use_counters = */ None, + allow_import_rules, /* sanitization_data = */ None, ); @@ -457,6 +471,7 @@ impl Stylesheet { quirks_mode: QuirksMode, line_number_offset: u32, use_counters: Option<&UseCounters>, + allow_import_rules: AllowImportRules, mut sanitization_data: Option<&mut SanitizationData>, ) -> (Vec<CssRule>, Option<String>, Option<String>) { let mut rules = Vec::new(); @@ -481,6 +496,7 @@ impl Stylesheet { dom_error: None, insert_rule_context: None, namespaces, + allow_import_rules, }; { @@ -537,6 +553,7 @@ impl Stylesheet { error_reporter: Option<&dyn ParseErrorReporter>, quirks_mode: QuirksMode, line_number_offset: u32, + allow_import_rules: AllowImportRules, ) -> Self { // FIXME: Consider adding use counters to Servo? let contents = StylesheetContents::from_str( @@ -549,6 +566,7 @@ impl Stylesheet { quirks_mode, line_number_offset, /* use_counters = */ None, + allow_import_rules, /* sanitized_output = */ None, ); diff --git a/components/style/traversal.rs b/components/style/traversal.rs index a6aa220ccfa..48e42656e86 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -684,7 +684,7 @@ fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData) where E: TElement, { - use crate::values::generics::image::{GenericImageLayer, Image}; + use crate::values::generics::image::Image; use style_traits::ToCss; // We speculatively evaluate any paint worklets during styling. @@ -694,9 +694,7 @@ where if let Some(ref values) = data.styles.primary { for image in &values.get_background().background_image.0 { let (name, arguments) = match *image { - GenericImageLayer::Image(Image::PaintWorklet(ref worklet)) => { - (&worklet.name, &worklet.arguments) - }, + Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments), _ => continue, }; let painter = match context.shared.registered_speculative_painters.get(name) { diff --git a/components/style/values/animated/length.rs b/components/style/values/animated/length.rs deleted file mode 100644 index 04690446e64..00000000000 --- a/components/style/values/animated/length.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* 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/. */ - -//! Animation implementation for various length-related types. - -use super::{Animate, Procedure}; -use crate::values::computed::length::LengthPercentage; -use crate::values::computed::Percentage; -use style_traits::values::specified::AllowedNumericType; - -/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc> -impl Animate for LengthPercentage { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| { - if this.is_none() && other.is_none() { - return Ok(None); - } - let this = this.unwrap_or_default(); - let other = other.unwrap_or_default(); - Ok(Some(this.animate(&other, procedure)?)) - }; - - let length = self - .unclamped_length() - .animate(&other.unclamped_length(), procedure)?; - let percentage = - animate_percentage_half(self.specified_percentage(), other.specified_percentage())?; - - // Gets clamped as needed after the animation if needed, so no need to - // specify any particular AllowedNumericType. - Ok(LengthPercentage::new_calc( - length, - percentage, - AllowedNumericType::All, - )) - } -} diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index 7e699542fd4..226c01a9a49 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -23,7 +23,6 @@ pub mod color; pub mod effects; mod font; mod grid; -mod length; mod svg; pub mod transform; @@ -109,9 +108,6 @@ pub fn animate_multiplicative_factor( /// If a variant is annotated with `#[animation(error)]`, the corresponding /// `match` arm returns an error. /// -/// If the two values are not similar, an error is returned unless a fallback -/// function has been specified through `#[animate(fallback)]`. -/// /// Trait bounds for type parameter `Foo` can be opted out of with /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for /// fields can be opted into with `#[animation(field_bound)]` on the field. @@ -457,6 +453,16 @@ where } } +impl<T> ToAnimatedZero for Box<[T]> +where + T: ToAnimatedZero, +{ + #[inline] + fn to_animated_zero(&self) -> Result<Self, ()> { + self.iter().map(|v| v.to_animated_zero()).collect() + } +} + impl<T> ToAnimatedZero for crate::OwnedSlice<T> where T: ToAnimatedZero, diff --git a/components/style/values/computed/basic_shape.rs b/components/style/values/computed/basic_shape.rs index 27a091a1115..fa30220157b 100644 --- a/components/style/values/computed/basic_shape.rs +++ b/components/style/values/computed/basic_shape.rs @@ -14,11 +14,11 @@ use crate::values::generics::basic_shape as generic; /// A computed alias for FillRule. pub use crate::values::generics::basic_shape::FillRule; -/// A computed clipping shape. -pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>; +/// A computed `clip-path` value. +pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>; -/// A computed float area shape. -pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>; +/// A computed `shape-outside` value. +pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>; /// A computed basic shape. pub type BasicShape = generic::GenericBasicShape< diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 6867f582d7d..0f821123c20 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -178,7 +178,7 @@ impl ToAnimatedValue for FontSize { } } -#[derive(Clone, Debug, Eq, PartialEq, ToResolvedValue)] +#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)] #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf))] /// Specifies a prioritized list of font family names or generic family names. pub struct FontFamily { @@ -227,7 +227,9 @@ impl ToCss for FontFamily { } } -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)] +#[derive( + Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, +)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] /// The name of a font family of choice pub struct FamilyName { @@ -270,7 +272,9 @@ impl ToCss for FamilyName { } } -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)] +#[derive( + Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, +)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] /// Font family names must either be given quoted as strings, /// or unquoted as a sequence of one or more identifiers. @@ -285,7 +289,9 @@ pub enum FontFamilyNameSyntax { Identifiers, } -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)] +#[derive( + Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem, +)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))] /// A set of faces that vary in weight, width or slope. pub enum SingleFontFamily { @@ -301,15 +307,28 @@ pub enum SingleFontFamily { /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s /// sSingleGenerics are updated as well. #[derive( - Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, Parse, ToCss, ToResolvedValue, ToShmem, + Clone, + Copy, + Debug, + Eq, + Hash, + MallocSizeOf, + PartialEq, + Parse, + ToCss, + ToComputedValue, + ToResolvedValue, + ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[repr(u8)] #[allow(missing_docs)] pub enum GenericFontFamily { /// No generic family specified, only for internal usage. + /// + /// NOTE(emilio): Gecko code relies on this variant being zero. #[css(skip)] - None, + None = 0, Serif, SansSerif, #[parse(aliases = "-moz-fixed")] @@ -350,19 +369,22 @@ impl SingleFontFamily { }; let mut value = first_ident.as_ref().to_owned(); + let mut serialize_quoted = value.contains(' '); // These keywords are not allowed by themselves. // The only way this value can be valid with with another keyword. if reserved { let ident = input.expect_ident()?; + serialize_quoted = serialize_quoted || ident.contains(' '); value.push(' '); value.push_str(&ident); } while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { + serialize_quoted = serialize_quoted || ident.contains(' '); value.push(' '); value.push_str(&ident); } - let syntax = if value.starts_with(' ') || value.ends_with(' ') || value.contains(" ") { + let syntax = if serialize_quoted { // For font family names which contains special white spaces, e.g. // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them // as identifiers correctly. Just mark them quoted so we don't need @@ -422,16 +444,22 @@ impl SingleFontFamily { } #[cfg(feature = "servo")] -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)] +#[derive( + Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, +)] /// A list of SingleFontFamily pub struct FontFamilyList(Box<[SingleFontFamily]>); #[cfg(feature = "gecko")] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)] /// A list of SingleFontFamily pub enum FontFamilyList { /// A strong reference to a Gecko SharedFontList object. - SharedFontList(RefPtr<structs::SharedFontList>), + SharedFontList( + #[compute(no_field_bound)] + #[resolve(no_field_bound)] + RefPtr<structs::SharedFontList>, + ), /// A font-family generic ID. Generic(GenericFontFamily), } @@ -675,7 +703,7 @@ pub type FontVariationSettings = FontSettings<VariationValue<Number>>; /// (see http://www.microsoft.com/typography/otspec/languagetags.htm). #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)] #[repr(C)] -pub struct FontLanguageOverride(u32); +pub struct FontLanguageOverride(pub u32); impl FontLanguageOverride { #[inline] @@ -686,10 +714,7 @@ impl FontLanguageOverride { /// Returns this value as a `&str`, backed by `storage`. #[inline] - pub fn to_str(self, storage: &mut [u8; 4]) -> &str { - if self.0 == 0 { - return "normal"; - } + pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str { *storage = u32::to_be_bytes(self.0); // Safe because we ensure it's ASCII during computing let slice = if cfg!(debug_assertions) { @@ -730,10 +755,22 @@ impl ToCss for FontLanguageOverride { where W: fmt::Write, { + if self.0 == 0 { + return dest.write_str("normal"); + } self.to_str(&mut [0; 4]).to_css(dest) } } +// FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then +// remove this. +#[cfg(feature = "gecko")] +impl From<u32> for FontLanguageOverride { + fn from(v: u32) -> Self { + unsafe { Self::from_u32(v) } + } +} + #[cfg(feature = "gecko")] impl From<FontLanguageOverride> for u32 { fn from(v: FontLanguageOverride) -> u32 { diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index bd59acac547..a2b59809021 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -13,7 +13,8 @@ use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::NumberOrPercentage; use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{ - LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, ToComputedValue, + AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, + ToComputedValue, }; use crate::values::generics::image::{self as generic, GradientCompatMode}; use crate::values::specified::image::LineDirection as SpecifiedLineDirection; @@ -22,9 +23,6 @@ use std::f32::consts::PI; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; -/// A computed image layer. -pub type ImageLayer = generic::GenericImageLayer<Image>; - /// Computed values for an image according to CSS-IMAGES. /// <https://drafts.csswg.org/css-images/#image-values> pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl>; @@ -37,6 +35,8 @@ pub type Gradient = generic::GenericGradient< NonNegativeLength, NonNegativeLengthPercentage, Position, + Angle, + AngleOrPercentage, Color, >; @@ -57,15 +57,9 @@ pub enum LineDirection { Corner(HorizontalPositionKeyword, VerticalPositionKeyword), } -/// A computed gradient item. -pub type GradientItem = generic::GenericGradientItem<Color, LengthPercentage>; - -/// A computed color stop. -pub type ColorStop = generic::ColorStop<Color, LengthPercentage>; - /// Computed values for `-moz-image-rect(...)`. #[cfg(feature = "gecko")] -pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>; +pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>; /// Empty enum on non-gecko #[cfg(not(feature = "gecko"))] diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 32b0946493c..8c6dde8c738 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; use std::fmt::{self, Write}; -use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; use style_traits::{CSSPixel, CssWriter, ToCss}; pub use super::image::Image; @@ -203,6 +203,7 @@ impl Size { Serialize, ToAnimatedValue, ToAnimatedZero, + ToComputedValue, ToResolvedValue, ToShmem, )] @@ -331,6 +332,13 @@ impl Div<CSSFloat> for CSSPixelLength { } } +impl MulAssign<CSSFloat> for CSSPixelLength { + #[inline] + fn mul_assign(&mut self, other: CSSFloat) { + self.0 *= other; + } +} + impl Mul<CSSFloat> for CSSPixelLength { type Output = Self; diff --git a/components/style/values/computed/length_percentage.rs b/components/style/values/computed/length_percentage.rs index 96bf76c98d4..1889481ad84 100644 --- a/components/style/values/computed/length_percentage.rs +++ b/components/style/values/computed/length_percentage.rs @@ -25,15 +25,16 @@ //! our expectations. use super::{Context, Length, Percentage, ToComputedValue}; -use crate::values::animated::{ToAnimatedValue, ToAnimatedZero}; +use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; -use crate::values::generics::NonNegative; +use crate::values::generics::{calc, NonNegative}; use crate::values::specified::length::FontBaseSize; use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; use std::fmt::{self, Write}; use style_traits::values::specified::AllowedNumericType; use style_traits::{CssWriter, ToCss}; @@ -162,13 +163,20 @@ impl MallocSizeOf for LengthPercentage { } /// An unpacked `<length-percentage>` that borrows the `calc()` variant. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, ToCss)] enum Unpacked<'a> { Calc(&'a CalcLengthPercentage), Length(Length), Percentage(Percentage), } +/// An unpacked `<length-percentage>` that mutably borrows the `calc()` variant. +enum UnpackedMut<'a> { + Calc(&'a mut CalcLengthPercentage), + Length(Length), + Percentage(Percentage), +} + /// An unpacked `<length-percentage>` that owns the `calc()` variant, for /// serialization purposes. #[derive(Deserialize, PartialEq, Serialize)] @@ -185,6 +193,22 @@ impl LengthPercentage { Self::new_length(Length::new(1.)) } + /// 0% + #[inline] + pub fn zero_percent() -> Self { + Self::new_percent(Percentage::zero()) + } + + fn to_calc_node(&self) -> Cow<CalcNode> { + match self.unpack() { + Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))), + Unpacked::Percentage(p) => { + Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p))) + }, + Unpacked::Calc(p) => Cow::Borrowed(&p.node), + } + } + /// Constructs a length value. #[inline] pub fn new_length(length: Length) -> Self { @@ -211,25 +235,46 @@ impl LengthPercentage { percent } + /// Given a `LengthPercentage` value `v`, construct the value representing + /// `calc(100% - v)`. + pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self { + // TODO: This could in theory take ownership of the calc node in `v` if + // possible instead of cloning. + let mut node = v.to_calc_node().into_owned(); + node.negate(); + + let new_node = CalcNode::Sum( + vec![ + CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())), + node, + ] + .into(), + ); + + Self::new_calc(new_node, clamping_mode) + } + /// Constructs a `calc()` value. #[inline] - pub fn new_calc( - length: Length, - percentage: Option<Percentage>, - clamping_mode: AllowedNumericType, - ) -> Self { - let percentage = match percentage { - Some(p) => p, - None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))), - }; - if length.is_zero() { - return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0))); + pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self { + node.simplify_and_sort(); + + match node { + CalcNode::Leaf(l) => { + return match l { + CalcLengthPercentageLeaf::Length(l) => { + Self::new_length(Length::new(clamping_mode.clamp(l.px()))) + }, + CalcLengthPercentageLeaf::Percentage(p) => { + Self::new_percent(Percentage(clamping_mode.clamp(p.0))) + }, + } + }, + _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage { + clamping_mode, + node, + })), } - Self::new_calc_unchecked(Box::new(CalcLengthPercentage { - length, - percentage, - clamping_mode, - })) } /// Private version of new_calc() that constructs a calc() variant without @@ -262,7 +307,18 @@ impl LengthPercentage { LengthPercentageUnion::TAG_CALC => Tag::Calc, LengthPercentageUnion::TAG_LENGTH => Tag::Length, LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage, - _ => unreachable!("Bogus tag?"), + _ => unsafe { debug_unreachable!("Bogus tag?") }, + } + } + + #[inline] + fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> { + unsafe { + match self.tag() { + Tag::Calc => UnpackedMut::Calc(&mut *self.calc_ptr()), + Tag::Length => UnpackedMut::Length(self.0.length.length), + Tag::Percentage => UnpackedMut::Percentage(self.0.percentage.percentage), + } } } @@ -313,57 +369,7 @@ impl LengthPercentage { match self.unpack() { Unpacked::Length(l) => l.px() == 0.0, Unpacked::Percentage(p) => p.0 == 0.0, - Unpacked::Calc(ref c) => { - debug_assert_ne!( - c.length.px(), - 0.0, - "Should've been simplified to a percentage" - ); - false - }, - } - } - - /// Returns the `<length>` component of this `calc()`, unclamped. - #[inline] - pub fn unclamped_length(&self) -> Length { - match self.unpack() { - Unpacked::Length(l) => l, - Unpacked::Percentage(..) => Zero::zero(), - Unpacked::Calc(c) => c.unclamped_length(), - } - } - - /// Returns this `calc()` as a `<length>`. - /// - /// Panics in debug mode if a percentage is present in the expression. - #[inline] - fn length(&self) -> Length { - debug_assert!(!self.has_percentage()); - self.length_component() - } - - /// Returns the `<length>` component of this `calc()`, clamped. - #[inline] - pub fn length_component(&self) -> Length { - match self.unpack() { - Unpacked::Length(l) => l, - Unpacked::Percentage(..) => Zero::zero(), - Unpacked::Calc(c) => c.length_component(), - } - } - - /// Returns the `<percentage>` component of this `calc()`, unclamped, as a - /// float. - /// - /// FIXME: This are very different semantics from length(), we should - /// probably rename this. - #[inline] - pub fn percentage(&self) -> CSSFloat { - match self.unpack() { - Unpacked::Length(..) => 0., - Unpacked::Percentage(p) => p.0, - Unpacked::Calc(c) => c.percentage.0, + Unpacked::Calc(..) => false, } } @@ -407,25 +413,8 @@ impl LengthPercentage { #[inline] pub fn to_percentage(&self) -> Option<Percentage> { match self.unpack() { - Unpacked::Length(..) => None, Unpacked::Percentage(p) => Some(p), - Unpacked::Calc(ref c) => { - debug_assert!(!c.length.is_zero()); - None - }, - } - } - - /// Return the specified percentage if any. - #[inline] - pub fn specified_percentage(&self) -> Option<Percentage> { - match self.unpack() { - Unpacked::Length(..) => None, - Unpacked::Percentage(p) => Some(p), - Unpacked::Calc(ref c) => { - debug_assert!(self.has_percentage()); - Some(c.percentage) - }, + Unpacked::Length(..) | Unpacked::Calc(..) => None, } } @@ -452,19 +441,22 @@ impl LengthPercentage { /// the height property), they apply whenever a calc() expression contains /// percentages. pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> { - if self.has_percentage() { - return Some(self.resolve(container_len?)); + if let Unpacked::Length(l) = self.unpack() { + return Some(l); } - Some(self.length()) + Some(self.resolve(container_len?)) } /// Returns the clamped non-negative values. #[inline] - pub fn clamp_to_non_negative(&self) -> Self { - match self.unpack() { - Unpacked::Length(l) => Self::new_length(l.clamp_to_non_negative()), - Unpacked::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()), - Unpacked::Calc(c) => c.clamp_to_non_negative(), + pub fn clamp_to_non_negative(mut self) -> Self { + match self.unpack_mut() { + UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()), + UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()), + UnpackedMut::Calc(ref mut c) => { + c.clamping_mode = AllowedNumericType::NonNegative; + self + }, } } } @@ -549,7 +541,7 @@ impl ToCss for LengthPercentage { where W: Write, { - specified::LengthPercentage::from_computed_value(self).to_css(dest) + self.unpack().to_css(dest) } } @@ -584,46 +576,138 @@ impl<'de> Deserialize<'de> for LengthPercentage { } } -/// The representation of a calc() function with mixed lengths and percentages. -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)] -#[repr(C)] -pub struct CalcLengthPercentage { - length: Length, +/// The leaves of a `<length-percentage>` calc expression. +#[derive( + Clone, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + ToAnimatedZero, + ToCss, + ToResolvedValue, +)] +#[allow(missing_docs)] +#[repr(u8)] +pub enum CalcLengthPercentageLeaf { + Length(Length), + Percentage(Percentage), +} - percentage: Percentage, +impl CalcLengthPercentageLeaf { + fn is_zero_length(&self) -> bool { + match *self { + Self::Length(ref l) => l.is_zero(), + Self::Percentage(..) => false, + } + } +} - #[animation(constant)] - clamping_mode: AllowedNumericType, +impl PartialOrd for CalcLengthPercentageLeaf { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + use self::CalcLengthPercentageLeaf::*; + + if std::mem::discriminant(self) != std::mem::discriminant(other) { + return None; + } + + match (self, other) { + (&Length(ref one), &Length(ref other)) => one.partial_cmp(other), + (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other), + _ => { + match *self { + Length(..) | Percentage(..) => {}, + } + unsafe { + debug_unreachable!("Forgot a branch?"); + } + }, + } + } } -impl CalcLengthPercentage { - /// Returns the length component of this `calc()`, clamped. - #[inline] - fn length_component(&self) -> Length { - Length::new(self.clamping_mode.clamp(self.length.px())) +impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf { + fn is_negative(&self) -> bool { + match *self { + Self::Length(ref l) => l.px() < 0., + Self::Percentage(ref p) => p.0 < 0., + } } - /// Resolves the percentage. - #[inline] - pub fn resolve(&self, basis: Length) -> Length { - let length = self.length.px() + basis.px() * self.percentage.0; - Length::new(self.clamping_mode.clamp(length)) + fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> { + use self::CalcLengthPercentageLeaf::*; + + // 0px plus anything else is equal to the right hand side. + if self.is_zero_length() { + *self = other.clone(); + return Ok(()); + } + + if other.is_zero_length() { + return Ok(()); + } + + match (self, other) { + (&mut Length(ref mut one), &Length(ref other)) => { + *one += *other; + }, + (&mut Percentage(ref mut one), &Percentage(ref other)) => { + one.0 += other.0; + }, + _ => return Err(()), + } + + Ok(()) } - /// Returns the length, without clamping. - #[inline] - fn unclamped_length(&self) -> Length { - self.length + fn mul_by(&mut self, scalar: f32) { + match *self { + Self::Length(ref mut l) => *l = *l * scalar, + Self::Percentage(ref mut p) => p.0 *= scalar, + } } - /// Returns the clamped non-negative values. + fn simplify(&mut self) {} + + fn sort_key(&self) -> calc::SortKey { + match *self { + Self::Length(..) => calc::SortKey::Px, + Self::Percentage(..) => calc::SortKey::Percentage, + } + } +} + +/// The computed version of a calc() node for `<length-percentage>` values. +pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>; + +/// The representation of a calc() function with mixed lengths and percentages. +#[derive( + Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss, +)] +#[repr(C)] +pub struct CalcLengthPercentage { + #[animation(constant)] + #[css(skip)] + clamping_mode: AllowedNumericType, + node: CalcNode, +} + +impl CalcLengthPercentage { + /// Resolves the percentage. #[inline] - fn clamp_to_non_negative(&self) -> LengthPercentage { - LengthPercentage::new_calc( - self.length, - Some(self.percentage), - AllowedNumericType::NonNegative, - ) + fn resolve(&self, basis: Length) -> Length { + // unwrap() is fine because the conversion below is infallible. + let px = self + .node + .resolve(|l| { + Ok(match *l { + CalcLengthPercentageLeaf::Length(l) => l.px(), + CalcLengthPercentageLeaf::Percentage(ref p) => basis.px() * p.0, + }) + }) + .unwrap(); + Length::new(self.clamping_mode.clamp(px)) } } @@ -641,7 +725,7 @@ impl CalcLengthPercentage { // maybe. impl PartialEq for CalcLengthPercentage { fn eq(&self, other: &Self) -> bool { - self.length == other.length && self.percentage == other.percentage + self.node == other.node } } @@ -656,43 +740,22 @@ impl specified::CalcLengthPercentage { where F: Fn(Length) -> Length, { - use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength}; - use std::f32; - - let mut length = 0.; - - if let Some(absolute) = self.absolute { - length += zoom_fn(absolute.to_computed_value(context)).px(); - } - - for val in &[ - self.vw.map(ViewportPercentageLength::Vw), - self.vh.map(ViewportPercentageLength::Vh), - self.vmin.map(ViewportPercentageLength::Vmin), - self.vmax.map(ViewportPercentageLength::Vmax), - ] { - if let Some(val) = *val { - let viewport_size = context.viewport_size_for_viewport_unit_resolution(); - length += val.to_computed_value(viewport_size).px(); - } - } - - for val in &[ - self.ch.map(FontRelativeLength::Ch), - self.em.map(FontRelativeLength::Em), - self.ex.map(FontRelativeLength::Ex), - self.rem.map(FontRelativeLength::Rem), - ] { - if let Some(val) = *val { - length += val.to_computed_value(context, base_size).px(); - } - } + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; + + let node = self.node.map_leaves(|leaf| match *leaf { + Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)), + Leaf::Length(l) => CalcLengthPercentageLeaf::Length(match l { + NoCalcLength::Absolute(ref abs) => zoom_fn(abs.to_computed_value(context)), + NoCalcLength::FontRelative(ref fr) => fr.to_computed_value(context, base_size), + other => other.to_computed_value(context), + }), + Leaf::Number(..) | Leaf::Angle(..) | Leaf::Time(..) => { + unreachable!("Shouldn't have parsed") + }, + }); - LengthPercentage::new_calc( - Length::new(length.min(f32::MAX).max(f32::MIN)), - self.percentage, - self.clamping_mode, - ) + LengthPercentage::new_calc(node, self.clamping_mode) } /// Compute font-size or line-height taking into account text-zoom if necessary. @@ -711,25 +774,14 @@ impl specified::CalcLengthPercentage { /// Compute the value into pixel length as CSSFloat without context, /// so it returns Err(()) if there is any non-absolute unit. pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> { - if self.vw.is_some() || - self.vh.is_some() || - self.vmin.is_some() || - self.vmax.is_some() || - self.em.is_some() || - self.ex.is_some() || - self.ch.is_some() || - self.rem.is_some() || - self.percentage.is_some() - { - return Err(()); - } - - match self.absolute { - Some(abs) => Ok(abs.to_px()), - None => { - debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self); - Err(()) - }, + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; + + // Simplification should've turned this into an absolute length, + // otherwise it wouldn't have been able to. + match self.node { + calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()), + _ => Err(()), } } @@ -740,17 +792,51 @@ impl specified::CalcLengthPercentage { #[inline] fn from_computed_value(computed: &CalcLengthPercentage) -> Self { - use crate::values::specified::length::AbsoluteLength; + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; specified::CalcLengthPercentage { clamping_mode: computed.clamping_mode, - absolute: Some(AbsoluteLength::from_computed_value(&computed.length)), - percentage: Some(computed.percentage), - ..Default::default() + node: computed.node.map_leaves(|l| match l { + CalcLengthPercentageLeaf::Length(ref l) => { + Leaf::Length(NoCalcLength::from_px(l.px())) + }, + CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0), + }), } } } +/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc +/// https://drafts.csswg.org/css-values-4/#combine-math +/// https://drafts.csswg.org/css-values-4/#combine-mixed +impl Animate for LengthPercentage { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + Ok(match (self.unpack(), other.unpack()) { + (Unpacked::Length(one), Unpacked::Length(other)) => { + Self::new_length(one.animate(&other, procedure)?) + }, + (Unpacked::Percentage(one), Unpacked::Percentage(other)) => { + Self::new_percent(one.animate(&other, procedure)?) + }, + _ => { + let mut one = self.to_calc_node().into_owned(); + let mut other = other.to_calc_node().into_owned(); + let (l, r) = procedure.weights(); + + one.mul_by(l as f32); + other.mul_by(r as f32); + + Self::new_calc( + CalcNode::Sum(vec![one, other].into()), + AllowedNumericType::All, + ) + }, + }) + } +} + /// A wrapper of LengthPercentage, whose value must be >= 0. pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 7cfd1e20abe..841cc3df22a 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -21,10 +21,9 @@ use crate::media_queries::Device; use crate::properties; use crate::properties::{ComputedValues, LonghandId, StyleBuilder}; use crate::rule_cache::RuleCacheConditions; -use crate::Atom; -#[cfg(feature = "servo")] -use crate::Prefix; +use crate::{ArcSlice, Atom}; use euclid::default::Size2D; +use servo_arc::Arc; use std::cell::RefCell; use std::cmp; use std::f32; @@ -57,7 +56,7 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis}; pub use self::font::{FontVariantAlternates, FontWeight}; pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; -pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect}; +pub use self::image::{Gradient, Image, LineDirection, MozImageRect}; pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; @@ -78,7 +77,7 @@ pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::text::TextUnderlinePosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing}; -pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle}; +pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk}; pub use self::time::Time; pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; @@ -450,6 +449,46 @@ where } } +// NOTE(emilio): This is implementable more generically, but it's unlikely +// what you want there, as it forces you to have an extra allocation. +// +// We could do that if needed, ideally with specialization for the case where +// ComputedValue = T. But we don't need it for now. +impl<T> ToComputedValue for Arc<T> +where + T: ToComputedValue<ComputedValue = T>, +{ + type ComputedValue = Self; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self { + self.clone() + } + + #[inline] + fn from_computed_value(computed: &Self) -> Self { + computed.clone() + } +} + +// Same caveat as above applies. +impl<T> ToComputedValue for ArcSlice<T> +where + T: ToComputedValue<ComputedValue = T>, +{ + type ComputedValue = Self; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self { + self.clone() + } + + #[inline] + fn from_computed_value(computed: &Self) -> Self { + computed.clone() + } +} + trivial_to_computed_value!(()); trivial_to_computed_value!(bool); trivial_to_computed_value!(f32); @@ -460,10 +499,13 @@ trivial_to_computed_value!(u32); trivial_to_computed_value!(usize); trivial_to_computed_value!(Atom); #[cfg(feature = "servo")] -trivial_to_computed_value!(Prefix); +trivial_to_computed_value!(html5ever::Namespace); +#[cfg(feature = "servo")] +trivial_to_computed_value!(html5ever::Prefix); trivial_to_computed_value!(String); trivial_to_computed_value!(Box<str>); trivial_to_computed_value!(crate::OwnedStr); +trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType); #[allow(missing_docs)] #[derive( diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs index 7b22c37f73f..3eff231de88 100644 --- a/components/style/values/computed/position.rs +++ b/components/style/values/computed/position.rs @@ -9,6 +9,7 @@ use crate::values::computed::{Integer, LengthPercentage, Percentage}; use crate::values::generics::position::Position as GenericPosition; +use crate::values::generics::position::PositionComponent as GenericPositionComponent; use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto; use crate::values::generics::position::ZIndex as GenericZIndex; pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas}; @@ -56,5 +57,14 @@ impl ToCss for Position { } } +impl GenericPositionComponent for LengthPercentage { + fn is_center(&self) -> bool { + match self.to_percentage() { + Some(Percentage(per)) => per == 0.5, + _ => false, + } + } +} + /// A computed value for the `z-index` property. pub type ZIndex = GenericZIndex<Integer>; diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs index 54648572f86..a348d071ab9 100644 --- a/components/style/values/computed/svg.rs +++ b/components/style/values/computed/svg.rs @@ -31,7 +31,7 @@ impl SVGPaint { } /// <length> | <percentage> | <number> | context-value -pub type SVGLength = generic::SVGLength<LengthPercentage>; +pub type SVGLength = generic::GenericSVGLength<LengthPercentage>; impl SVGLength { /// `0px` @@ -41,7 +41,7 @@ impl SVGLength { } /// An non-negative wrapper of SVGLength. -pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>; +pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>; impl SVGWidth { /// `1px`. diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 0ca2e6044ed..b77695e06c0 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -18,8 +18,7 @@ use crate::Zero; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; -pub use crate::values::specified::TextAlignKeyword as TextAlign; -pub use crate::values::specified::TextUnderlinePosition; +pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition}; pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; @@ -30,6 +29,9 @@ pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>; /// Implements type for `text-decoration-thickness` property. pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>; +/// The computed value of `text-align`. +pub type TextAlign = specified::TextAlignKeyword; + /// A computed value for the `letter-spacing` property. #[repr(transparent)] #[derive( diff --git a/components/style/values/computed/ui.rs b/components/style/values/computed/ui.rs index 21914995951..ae12dfcdae0 100644 --- a/components/style/values/computed/ui.rs +++ b/components/style/values/computed/ui.rs @@ -13,10 +13,10 @@ pub use crate::values::specified::ui::CursorKind; pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect}; /// A computed value for the `cursor` property. -pub type Cursor = generics::Cursor<CursorImage>; +pub type Cursor = generics::GenericCursor<CursorImage>; /// A computed value for item of `image cursors`. -pub type CursorImage = generics::CursorImage<ComputedImageUrl, Number>; +pub type CursorImage = generics::GenericCursorImage<ComputedImageUrl, Number>; /// A computed value for `scrollbar-color` property. pub type ScrollbarColor = generics::GenericScrollbarColor<Color>; diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs index 67c735676b5..a4259ce8c6b 100644 --- a/components/style/values/distance.rs +++ b/components/style/values/distance.rs @@ -19,9 +19,6 @@ use std::ops::Add; /// If a variant is annotated with `#[animation(error)]`, the corresponding /// `match` arm returns an error. /// -/// If the two values are not similar, an error is returned unless a fallback -/// function has been specified through `#[distance(fallback)]`. -/// /// Trait bounds for type parameter `Foo` can be opted out of with /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for /// fields can be opted into with `#[distance(field_bound)]` on the field. @@ -81,6 +78,16 @@ impl ComputeSquaredDistance for Au { } } +impl<T> ComputeSquaredDistance for Box<T> +where + T: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + (**self).compute_squared_distance(&**other) + } +} + impl<T> ComputeSquaredDistance for Option<T> where T: ComputeSquaredDistance, diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index d91fa58eb0d..745d6e07bbf 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -15,18 +15,17 @@ use crate::Zero; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; -/// A clipping shape, for `clip-path`. -pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>; - /// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box> #[allow(missing_docs)] #[derive( Animate, Clone, + ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, + Parse, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, @@ -34,15 +33,27 @@ pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, U ToResolvedValue, ToShmem, )] -pub enum GeometryBox { +#[repr(u8)] +pub enum ShapeGeometryBox { + /// Depending on which kind of element this style value applied on, the + /// default value of the reference-box can be different. For an HTML + /// element, the default value of reference-box is border-box; for an SVG + /// element, the default value is fill-box. Since we can not determine the + /// default value at parsing time, we keep this value to make a decision on + /// it. + #[css(skip)] + ElementDependent, FillBox, StrokeBox, ViewBox, ShapeBox(ShapeBox), } -/// A float area shape, for `shape-outside`. -pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>; +impl Default for ShapeGeometryBox { + fn default() -> Self { + Self::ElementDependent + } +} /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box #[allow(missing_docs)] @@ -51,6 +62,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I Animate, Clone, Copy, + ComputeSquaredDistance, Debug, Eq, MallocSizeOf, @@ -63,6 +75,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I ToResolvedValue, ToShmem, )] +#[repr(u8)] pub enum ShapeBox { MarginBox, BorderBox, @@ -70,12 +83,19 @@ pub enum ShapeBox { ContentBox, } -/// A shape source, for some reference box. +impl Default for ShapeBox { + fn default() -> Self { + ShapeBox::MarginBox + } +} + +/// A value for the `clip-path` property. #[allow(missing_docs)] -#[animation(no_bound(ImageOrUrl))] +#[animation(no_bound(U))] #[derive( Animate, Clone, + ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, @@ -86,18 +106,54 @@ pub enum ShapeBox { ToResolvedValue, ToShmem, )] -pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> { +#[repr(u8)] +pub enum GenericClipPath<BasicShape, U> { #[animation(error)] - ImageOrUrl(ImageOrUrl), - Shape(Box<BasicShape>, Option<ReferenceBox>), + None, #[animation(error)] - Box(ReferenceBox), + Url(U), #[css(function)] Path(Path), + Shape( + Box<BasicShape>, + #[css(skip_if = "is_default")] ShapeGeometryBox, + ), + #[animation(error)] + Box(ShapeGeometryBox), +} + +pub use self::GenericClipPath as ClipPath; + +/// A value for the `shape-outside` property. +#[allow(missing_docs)] +#[animation(no_bound(I))] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum GenericShapeOutside<BasicShape, I> { #[animation(error)] None, + #[animation(error)] + Image(I), + Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox), + #[animation(error)] + Box(ShapeBox), } +pub use self::GenericShapeOutside as ShapeOutside; + #[allow(missing_docs)] #[derive( Animate, @@ -252,7 +308,7 @@ pub use self::GenericShapeRadius as ShapeRadius; #[repr(C)] pub struct GenericPolygon<LengthPercentage> { /// The filling rule for a polygon. - #[css(skip_if = "fill_is_default")] + #[css(skip_if = "is_default")] pub fill: FillRule, /// A collection of (x, y) coordinates to draw the polygon. #[css(iterable)] @@ -311,6 +367,7 @@ pub enum FillRule { #[derive( Animate, Clone, + ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, @@ -321,39 +378,23 @@ pub enum FillRule { ToResolvedValue, ToShmem, )] +#[repr(C)] pub struct Path { /// The filling rule for the svg path. - #[css(skip_if = "fill_is_default")] + #[css(skip_if = "is_default")] #[animation(constant)] pub fill: FillRule, /// The svg path data. pub path: SVGPathData, } -// FIXME(nox): Implement ComputeSquaredDistance for T types and stop -// using PartialEq here, this will let us derive this impl. -impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U> -where - B: ComputeSquaredDistance, - T: PartialEq, -{ - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - ( - &ShapeSource::Shape(ref this, ref this_box), - &ShapeSource::Shape(ref other, ref other_box), - ) if this_box == other_box => this.compute_squared_distance(other), - (&ShapeSource::Path(ref this), &ShapeSource::Path(ref other)) - if this.fill == other.fill => - { - this.path.compute_squared_distance(&other.path) - }, - _ => Err(()), - } +impl<B, U> ToAnimatedZero for ClipPath<B, U> { + fn to_animated_zero(&self) -> Result<Self, ()> { + Err(()) } } -impl<B, T, U> ToAnimatedZero for ShapeSource<B, T, U> { +impl<B, U> ToAnimatedZero for ShapeOutside<B, U> { fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } @@ -488,6 +529,6 @@ impl Default for FillRule { } #[inline] -fn fill_is_default(fill: &FillRule) -> bool { - *fill == FillRule::default() +fn is_default<T: Default + PartialEq>(fill: &T) -> bool { + *fill == Default::default() } diff --git a/components/style/values/generics/calc.rs b/components/style/values/generics/calc.rs new file mode 100644 index 00000000000..074dd97f4d0 --- /dev/null +++ b/components/style/values/generics/calc.rs @@ -0,0 +1,573 @@ +/* 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/. */ + +//! [Calc expressions][calc]. +//! +//! [calc]: https://drafts.csswg.org/css-values/#calc-notation + +use crate::Zero; +use smallvec::SmallVec; +use std::fmt::{self, Write}; +use std::ops::Add; +use std::{cmp, mem}; +use style_traits::{CssWriter, ToCss}; + +/// Whether we're a `min` or `max` function. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + ToAnimatedZero, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum MinMaxOp { + /// `min()` + Min, + /// `max()` + Max, +} + +/// This determines the order in which we serialize members of a calc() sum. +/// +/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[allow(missing_docs)] +pub enum SortKey { + Number, + Percentage, + Ch, + Deg, + Em, + Ex, + Px, + Rem, + Sec, + Vh, + Vmax, + Vmin, + Vw, + Other, +} + +/// A generic node in a calc expression. +/// +/// FIXME: This would be much more elegant if we used `Self` in the types below, +/// but we can't because of https://github.com/serde-rs/serde/issues/1565. +#[repr(u8)] +#[derive( + Clone, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + ToAnimatedZero, + ToResolvedValue, + ToShmem, +)] +pub enum GenericCalcNode<L> { + /// A leaf node. + Leaf(L), + /// A sum node, representing `a + b + c` where a, b, and c are the + /// arguments. + Sum(crate::OwnedSlice<GenericCalcNode<L>>), + /// A `min` or `max` function. + MinMax(crate::OwnedSlice<GenericCalcNode<L>>, MinMaxOp), + /// A `clamp()` function. + Clamp { + /// The minimum value. + min: Box<GenericCalcNode<L>>, + /// The central value. + center: Box<GenericCalcNode<L>>, + /// The maximum value. + max: Box<GenericCalcNode<L>>, + }, +} + +pub use self::GenericCalcNode as CalcNode; + +/// A trait that represents all the stuff a valid leaf of a calc expression. +pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss { + /// Whether this value is known-negative. + fn is_negative(&self) -> bool; + + /// Tries to merge one sum to another, that is, perform `x` + `y`. + fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>; + + /// Multiplies the leaf by a given scalar number. + fn mul_by(&mut self, scalar: f32); + + /// Negates the leaf. + fn negate(&mut self) { + self.mul_by(-1.); + } + + /// Canonicalizes the expression if necessary. + fn simplify(&mut self); + + /// Returns the sort key for simplification. + fn sort_key(&self) -> SortKey; +} + +impl<L: CalcNodeLeaf> CalcNode<L> { + /// Negates the node. + pub fn negate(&mut self) { + self.mul_by(-1.); + } + + fn sort_key(&self) -> SortKey { + match *self { + Self::Leaf(ref l) => l.sort_key(), + _ => SortKey::Other, + } + } + + /// Tries to merge one sum to another, that is, perform `x` + `y`. + fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> { + match (self, other) { + (&mut CalcNode::Leaf(ref mut one), &CalcNode::Leaf(ref other)) => { + one.try_sum_in_place(other) + }, + _ => Err(()), + } + } + + /// Convert this `CalcNode` into a `CalcNode` with a different leaf kind. + pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O> + where + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + self.map_leaves_internal(&mut map) + } + + fn map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O> + where + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + fn map_children<L, O, F>( + children: &[CalcNode<L>], + map: &mut F, + ) -> crate::OwnedSlice<CalcNode<O>> + where + L: CalcNodeLeaf, + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + children + .iter() + .map(|c| c.map_leaves_internal(map)) + .collect() + } + + match *self { + Self::Leaf(ref l) => CalcNode::Leaf(map(l)), + Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)), + Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op), + Self::Clamp { + ref min, + ref center, + ref max, + } => { + let min = Box::new(min.map_leaves_internal(map)); + let center = Box::new(center.map_leaves_internal(map)); + let max = Box::new(max.map_leaves_internal(map)); + CalcNode::Clamp { min, center, max } + }, + } + } + + /// Resolves the expression returning a value of `O`, given a function to + /// turn a leaf into the relevant value. + pub fn resolve<O>( + &self, + mut leaf_to_output_fn: impl FnMut(&L) -> Result<O, ()>, + ) -> Result<O, ()> + where + O: PartialOrd + PartialEq + Add<Output = O> + Zero, + { + self.resolve_internal(&mut leaf_to_output_fn) + } + + fn resolve_internal<O, F>(&self, leaf_to_output_fn: &mut F) -> Result<O, ()> + where + O: PartialOrd + PartialEq + Add<Output = O> + Zero, + F: FnMut(&L) -> Result<O, ()>, + { + Ok(match *self { + Self::Leaf(ref l) => return leaf_to_output_fn(l), + Self::Sum(ref c) => { + let mut result = Zero::zero(); + for child in &**c { + result = result + child.resolve_internal(leaf_to_output_fn)?; + } + result + }, + Self::MinMax(ref nodes, op) => { + let mut result = nodes[0].resolve_internal(leaf_to_output_fn)?; + for node in nodes.iter().skip(1) { + let candidate = node.resolve_internal(leaf_to_output_fn)?; + let candidate_wins = match op { + MinMaxOp::Min => candidate < result, + MinMaxOp::Max => candidate > result, + }; + if candidate_wins { + result = candidate; + } + } + result + }, + Self::Clamp { + ref min, + ref center, + ref max, + } => { + let min = min.resolve_internal(leaf_to_output_fn)?; + let center = center.resolve_internal(leaf_to_output_fn)?; + let max = max.resolve_internal(leaf_to_output_fn)?; + + let mut result = center; + if result > max { + result = max; + } + if result < min { + result = min + } + result + }, + }) + } + + fn is_negative_leaf(&self) -> bool { + match *self { + Self::Leaf(ref l) => l.is_negative(), + _ => false, + } + } + + /// Multiplies the node by a scalar. + pub fn mul_by(&mut self, scalar: f32) { + match *self { + Self::Leaf(ref mut l) => l.mul_by(scalar), + // Multiplication is distributive across this. + Self::Sum(ref mut children) => { + for node in &mut **children { + node.mul_by(scalar); + } + }, + // This one is a bit trickier. + Self::MinMax(ref mut children, ref mut op) => { + for node in &mut **children { + node.mul_by(scalar); + } + + // For negatives we need to invert the operation. + if scalar < 0. { + *op = match *op { + MinMaxOp::Min => MinMaxOp::Max, + MinMaxOp::Max => MinMaxOp::Min, + } + } + }, + // This one is slightly tricky too. + Self::Clamp { + ref mut min, + ref mut center, + ref mut max, + } => { + min.mul_by(scalar); + center.mul_by(scalar); + max.mul_by(scalar); + // For negatives we need to swap min / max. + if scalar < 0. { + mem::swap(min, max); + } + }, + } + } + + /// Visits all the nodes in this calculation tree recursively, starting by + /// the leaves and bubbling all the way up. + /// + /// This is useful for simplification, but can also be used for validation + /// and such. + pub fn visit_depth_first(&mut self, mut f: impl FnMut(&mut Self)) { + self.visit_depth_first_internal(&mut f); + } + + fn visit_depth_first_internal(&mut self, f: &mut impl FnMut(&mut Self)) { + match *self { + Self::Clamp { + ref mut min, + ref mut center, + ref mut max, + } => { + min.visit_depth_first_internal(f); + center.visit_depth_first_internal(f); + max.visit_depth_first_internal(f); + }, + Self::Sum(ref mut children) | Self::MinMax(ref mut children, _) => { + for child in &mut **children { + child.visit_depth_first_internal(f); + } + }, + Self::Leaf(..) => {}, + } + f(self); + } + + /// Simplifies and sorts the calculation of a given node. All the nodes + /// below it should be simplified already, this only takes care of + /// simplifying directly nested nodes. So, probably should always be used in + /// combination with `visit_depth_first()`. + /// + /// This is only needed if it's going to be preserved after parsing (so, for + /// `<length-percentage>`). Otherwise we can just evaluate it using + /// `resolve()`, and we'll come up with a simplified value anyways. + pub fn simplify_and_sort_direct_children(&mut self) { + macro_rules! replace_self_with { + ($slot:expr) => {{ + let dummy = Self::MinMax(Default::default(), MinMaxOp::Max); + let result = mem::replace($slot, dummy); + mem::replace(self, result); + }}; + } + match *self { + Self::Clamp { + ref mut min, + ref mut center, + ref mut max, + } => { + // NOTE: clamp() is max(min, min(center, max)) + let min_cmp_center = match min.partial_cmp(¢er) { + Some(o) => o, + None => return, + }; + + // So if we can prove that min is more than center, then we won, + // as that's what we should always return. + if matches!(min_cmp_center, cmp::Ordering::Greater) { + return replace_self_with!(&mut **min); + } + + // Otherwise try with max. + let max_cmp_center = match max.partial_cmp(¢er) { + Some(o) => o, + None => return, + }; + + if matches!(max_cmp_center, cmp::Ordering::Less) { + // max is less than center, so we need to return effectively + // `max(min, max)`. + let max_cmp_min = match max.partial_cmp(&min) { + Some(o) => o, + None => { + debug_assert!( + false, + "We compared center with min and max, how are \ + min / max not comparable with each other?" + ); + return; + }, + }; + + if matches!(max_cmp_min, cmp::Ordering::Less) { + return replace_self_with!(&mut **min); + } + + return replace_self_with!(&mut **max); + } + + // Otherwise we're the center node. + return replace_self_with!(&mut **center); + }, + Self::MinMax(ref mut children, op) => { + let winning_order = match op { + MinMaxOp::Min => cmp::Ordering::Less, + MinMaxOp::Max => cmp::Ordering::Greater, + }; + + let mut result = 0; + for i in 1..children.len() { + let o = match children[i].partial_cmp(&children[result]) { + // We can't compare all the children, so we can't + // know which one will actually win. Bail out and + // keep ourselves as a min / max function. + // + // TODO: Maybe we could simplify compatible children, + // see https://github.com/w3c/csswg-drafts/issues/4756 + None => return, + Some(o) => o, + }; + + if o == winning_order { + result = i; + } + } + + replace_self_with!(&mut children[result]); + }, + Self::Sum(ref mut children_slot) => { + let mut sums_to_merge = SmallVec::<[_; 3]>::new(); + let mut extra_kids = 0; + for (i, child) in children_slot.iter().enumerate() { + if let Self::Sum(ref children) = *child { + extra_kids += children.len(); + sums_to_merge.push(i); + } + } + + // If we only have one kid, we've already simplified it, and it + // doesn't really matter whether it's a sum already or not, so + // lift it up and continue. + if children_slot.len() == 1 { + return replace_self_with!(&mut children_slot[0]); + } + + let mut children = mem::replace(children_slot, Default::default()).into_vec(); + + if !sums_to_merge.is_empty() { + children.reserve(extra_kids - sums_to_merge.len()); + // Merge all our nested sums, in reverse order so that the + // list indices are not invalidated. + for i in sums_to_merge.drain(..).rev() { + let kid_children = match children.swap_remove(i) { + Self::Sum(c) => c, + _ => unreachable!(), + }; + + // This would be nicer with + // https://github.com/rust-lang/rust/issues/59878 fixed. + children.extend(kid_children.into_vec()); + } + } + + debug_assert!(children.len() >= 2, "Should still have multiple kids!"); + + // Sort by spec order. + children.sort_unstable_by_key(|c| c.sort_key()); + + // NOTE: if the function returns true, by the docs of dedup_by, + // a is removed. + children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok()); + + if children.len() == 1 { + // If only one children remains, lift it up, and carry on. + replace_self_with!(&mut children[0]); + } else { + // Else put our simplified children back. + mem::replace(children_slot, children.into_boxed_slice().into()); + } + }, + Self::Leaf(ref mut l) => { + l.simplify(); + }, + } + } + + /// Simplifies and sorts the kids in the whole calculation subtree. + pub fn simplify_and_sort(&mut self) { + self.visit_depth_first(|node| node.simplify_and_sort_direct_children()) + } + + fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result + where + W: Write, + { + let write_closing_paren = match *self { + Self::MinMax(_, op) => { + dest.write_str(match op { + MinMaxOp::Max => "max(", + MinMaxOp::Min => "min(", + })?; + true + }, + Self::Clamp { .. } => { + dest.write_str("clamp(")?; + true + }, + _ => { + if is_outermost { + dest.write_str("calc(")?; + } + is_outermost + }, + }; + + match *self { + Self::MinMax(ref children, _) => { + let mut first = true; + for child in &**children { + if !first { + dest.write_str(", ")?; + } + first = false; + child.to_css_impl(dest, false)?; + } + }, + Self::Sum(ref children) => { + let mut first = true; + for child in &**children { + if !first { + if child.is_negative_leaf() { + dest.write_str(" - ")?; + let mut c = child.clone(); + c.negate(); + c.to_css_impl(dest, false)?; + } else { + dest.write_str(" + ")?; + child.to_css_impl(dest, false)?; + } + } else { + first = false; + child.to_css_impl(dest, false)?; + } + } + }, + Self::Clamp { + ref min, + ref center, + ref max, + } => { + min.to_css_impl(dest, false)?; + dest.write_str(", ")?; + center.to_css_impl(dest, false)?; + dest.write_str(", ")?; + max.to_css_impl(dest, false)?; + }, + Self::Leaf(ref l) => l.to_css(dest)?, + } + + if write_closing_paren { + dest.write_str(")")?; + } + Ok(()) + } +} + +impl<L: CalcNodeLeaf> PartialOrd for CalcNode<L> { + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + match (self, other) { + (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => one.partial_cmp(other), + _ => None, + } + } +} + +impl<L: CalcNodeLeaf> ToCss for CalcNode<L> { + /// <https://drafts.csswg.org/css-values/#calc-serialize> + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + self.to_css_impl(dest, /* is_outermost = */ true) + } +} diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs index 5666e2c5818..6f0f155912d 100644 --- a/components/style/values/generics/grid.rs +++ b/components/style/values/generics/grid.rs @@ -655,8 +655,10 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> { pub struct LineNameList { /// The optional `<line-name-list>` pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>, - /// Indicates the line name that requires `auto-fill`, if in bounds. - pub fill_idx: usize, + /// Indicates the starting line names that requires `auto-fill`, if in bounds. + pub fill_start: usize, + /// Indicates the number of line names in the auto-fill + pub fill_len: usize, } impl Parse for LineNameList { @@ -666,7 +668,7 @@ impl Parse for LineNameList { ) -> Result<Self, ParseError<'i>> { input.expect_ident_matching("subgrid")?; let mut line_names = vec![]; - let mut fill_idx = None; + let mut fill_data = None; loop { let repeat_parse_result = input.try(|input| { @@ -682,8 +684,7 @@ impl Parse for LineNameList { Ok((names_list, count)) }) }); - - if let Ok((mut names_list, count)) = repeat_parse_result { + if let Ok((names_list, count)) = repeat_parse_result { match count { // FIXME(emilio): we shouldn't expand repeat() at // parse time for subgrid. (bug 1583429) @@ -694,19 +695,11 @@ impl Parse for LineNameList { .cycle() .take(num.value() as usize * names_list.len()), ), - RepeatCount::AutoFill if fill_idx.is_none() => { - // `repeat(autof-fill, ..)` should have just one line name. - // FIXME(bug 1341507) the above comment is wrong per: - // https://drafts.csswg.org/css-grid-2/#typedef-name-repeat - if names_list.len() != 1 { - return Err( - input.new_custom_error(StyleParseErrorKind::UnspecifiedError) - ); - } - let names = names_list.pop().unwrap(); - - line_names.push(names); - fill_idx = Some(line_names.len() - 1); + RepeatCount::AutoFill if fill_data.is_none() => { + let fill_idx = line_names.len(); + let fill_len = names_list.len(); + fill_data = Some((fill_idx, fill_len)); + line_names.extend(names_list.into_iter()); }, _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), } @@ -721,9 +714,12 @@ impl Parse for LineNameList { line_names.truncate(MAX_GRID_LINE as usize); } + let (fill_start, fill_len) = fill_data.unwrap_or((usize::MAX, 0)); + Ok(LineNameList { names: line_names.into(), - fill_idx: fill_idx.unwrap_or(usize::MAX), + fill_start: fill_start, + fill_len: fill_len, }) } } @@ -734,9 +730,10 @@ impl ToCss for LineNameList { W: Write, { dest.write_str("subgrid")?; - let fill_idx = self.fill_idx; + let fill_start = self.fill_start; + let fill_len = self.fill_len; for (i, names) in self.names.iter().enumerate() { - if i == fill_idx { + if i == fill_start { dest.write_str(" repeat(auto-fill,")?; } @@ -751,7 +748,7 @@ impl ToCss for LineNameList { } dest.write_str("]")?; - if i == fill_idx { + if i == fill_start + fill_len - 1 { dest.write_str(")")?; } } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index a6b45bc82ad..42d541dc6f2 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -9,55 +9,28 @@ use crate::custom_properties; use crate::values::serialize_atom_identifier; use crate::Atom; +use crate::Zero; use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; +use values::generics::position::PositionComponent; -/// An <image> | <none> (for background-image, for example). -#[derive( - Clone, - Debug, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -pub enum GenericImageLayer<Image> { - /// The `none` value. - None, - /// The `<image>` value. - Image(Image), -} - -pub use self::GenericImageLayer as ImageLayer; - -impl<I> ImageLayer<I> { - /// Returns `none`. - #[inline] - pub fn none() -> Self { - ImageLayer::None - } -} - -/// An [image]. +/// An `<image> | none` value. /// -/// [image]: https://drafts.csswg.org/css-images/#image-values +/// https://drafts.csswg.org/css-images/#image-values #[derive( Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C, u8)] -pub enum GenericImage<Gradient, MozImageRect, ImageUrl> { +pub enum GenericImage<G, MozImageRect, ImageUrl> { + /// `none` variant. + None, /// A `<url()>` image. Url(ImageUrl), /// A `<gradient>` image. Gradients are rather large, and not nearly as /// common as urls, so we box them here to keep the size of this enum sane. - Gradient(Box<Gradient>), - + Gradient(Box<G>), /// A `-moz-image-rect` image. Also fairly large and rare. // not cfg’ed out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used` // Instead we make MozImageRect an empty enum @@ -80,27 +53,51 @@ pub use self::GenericImage as Image; /// <https://drafts.csswg.org/css-images/#gradients> #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(C)] -pub struct GenericGradient< +pub enum GenericGradient< LineDirection, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, Position, + Angle, + AngleOrPercentage, Color, > { - /// Gradients can be linear or radial. - pub kind: GenericGradientKind< - LineDirection, - NonNegativeLength, - NonNegativeLengthPercentage, - Position, - >, - /// The color stops and interpolation hints. - pub items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>, - /// True if this is a repeating gradient. - pub repeating: bool, - /// Compatibility mode. - pub compat_mode: GradientCompatMode, + /// A linear gradient. + Linear { + /// Line direction + direction: LineDirection, + /// The color stops and interpolation hints. + items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>, + /// True if this is a repeating gradient. + repeating: bool, + /// Compatibility mode. + compat_mode: GradientCompatMode, + }, + /// A radial gradient. + Radial { + /// Shape of gradient + shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>, + /// Center of gradient + position: Position, + /// The color stops and interpolation hints. + items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>, + /// True if this is a repeating gradient. + repeating: bool, + /// Compatibility mode. + compat_mode: GradientCompatMode, + }, + /// A conic gradient. + Conic { + /// Start angle of gradient + angle: Angle, + /// Center of gradient + position: Position, + /// The color stops and interpolation hints. + items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>, + /// True if this is a repeating gradient. + repeating: bool, + }, } pub use self::GenericGradient as Gradient; @@ -117,26 +114,6 @@ pub enum GradientCompatMode { Moz, } -/// A gradient kind. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] -#[repr(C, u8)] -pub enum GenericGradientKind< - LineDirection, - NonNegativeLength, - NonNegativeLengthPercentage, - Position, -> { - /// A linear gradient. - Linear(LineDirection), - /// A radial gradient. - Radial( - GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>, - Position, - ), -} - -pub use self::GenericGradientKind as GradientKind; - /// A radial gradient's ending shape. #[derive( Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, @@ -209,7 +186,7 @@ pub enum ShapeExtent { Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(C, u8)] -pub enum GenericGradientItem<Color, LengthPercentage> { +pub enum GenericGradientItem<Color, T> { /// A simple color stop, without position. SimpleColorStop(Color), /// A complex color stop, with a position. @@ -217,10 +194,10 @@ pub enum GenericGradientItem<Color, LengthPercentage> { /// The color for the stop. color: Color, /// The position for the stop. - position: LengthPercentage, + position: T, }, /// An interpolation hint. - InterpolationHint(LengthPercentage), + InterpolationHint(T), } pub use self::GenericGradientItem as GradientItem; @@ -230,17 +207,17 @@ pub use self::GenericGradientItem as GradientItem; #[derive( Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] -pub struct ColorStop<Color, LengthPercentage> { +pub struct ColorStop<Color, T> { /// The color of this stop. pub color: Color, /// The position of this stop. - pub position: Option<LengthPercentage>, + pub position: Option<T>, } -impl<Color, LengthPercentage> ColorStop<Color, LengthPercentage> { +impl<Color, T> ColorStop<Color, T> { /// Convert the color stop into an appropriate `GradientItem`. #[inline] - pub fn into_item(self) -> GradientItem<Color, LengthPercentage> { + pub fn into_item(self) -> GradientItem<Color, T> { match self.position { Some(position) => GradientItem::ComplexColorStop { color: self.color, @@ -261,6 +238,8 @@ pub struct PaintWorklet { /// The arguments for the worklet. /// TODO: store a parsed representation of the arguments. #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] + #[compute(no_field_bound)] + #[resolve(no_field_bound)] pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>, } @@ -285,7 +264,7 @@ impl ToCss for PaintWorklet { /// /// `-moz-image-rect(<uri>, top, right, bottom, left);` #[allow(missing_docs)] -#[css(comma, function)] +#[css(comma, function = "-moz-image-rect")] #[derive( Clone, Debug, @@ -297,7 +276,8 @@ impl ToCss for PaintWorklet { ToResolvedValue, ToShmem, )] -pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> { +#[repr(C)] +pub struct GenericMozImageRect<NumberOrPercentage, MozImageRectUrl> { pub url: MozImageRectUrl, pub top: NumberOrPercentage, pub right: NumberOrPercentage, @@ -305,6 +285,8 @@ pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> { pub left: NumberOrPercentage, } +pub use self::GenericMozImageRect as MozImageRect; + impl<G, R, U> fmt::Debug for Image<G, R, U> where G: ToCss, @@ -327,6 +309,7 @@ where W: Write, { match *self { + Image::None => dest.write_str("none"), Image::Url(ref url) => url.to_css(dest), Image::Gradient(ref gradient) => gradient.to_css(dest), Image::Rect(ref rect) => rect.to_css(dest), @@ -342,81 +325,146 @@ where } } -impl<D, LP, NL, NLP, P, C> ToCss for Gradient<D, LP, NL, NLP, P, C> +impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C> where D: LineDirection, LP: ToCss, NL: ToCss, NLP: ToCss, - P: ToCss, + P: PositionComponent + ToCss, + A: ToCss, + AoP: ToCss, C: ToCss, { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { - match self.compat_mode { + let (compat_mode, repeating) = match *self { + Gradient::Linear { + compat_mode, + repeating, + .. + } => (compat_mode, repeating), + Gradient::Radial { + compat_mode, + repeating, + .. + } => (compat_mode, repeating), + Gradient::Conic { repeating, .. } => (GradientCompatMode::Modern, repeating), + }; + + match compat_mode { GradientCompatMode::WebKit => dest.write_str("-webkit-")?, GradientCompatMode::Moz => dest.write_str("-moz-")?, _ => {}, } - if self.repeating { + if repeating { dest.write_str("repeating-")?; } - dest.write_str(self.kind.label())?; - dest.write_str("-gradient(")?; - let mut skip_comma = match self.kind { - GradientKind::Linear(ref direction) if direction.points_downwards(self.compat_mode) => { - true - }, - GradientKind::Linear(ref direction) => { - direction.to_css(dest, self.compat_mode)?; - false + + match *self { + Gradient::Linear { + ref direction, + ref items, + compat_mode, + .. + } => { + dest.write_str("linear-gradient(")?; + let mut skip_comma = if !direction.points_downwards(compat_mode) { + direction.to_css(dest, compat_mode)?; + false + } else { + true + }; + for item in &**items { + if !skip_comma { + dest.write_str(", ")?; + } + skip_comma = false; + item.to_css(dest)?; + } }, - GradientKind::Radial(ref shape, ref position) => { + Gradient::Radial { + ref shape, + ref position, + ref items, + compat_mode, + .. + } => { + dest.write_str("radial-gradient(")?; let omit_shape = match *shape { EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true, _ => false, }; - if self.compat_mode == GradientCompatMode::Modern { + let omit_position = position.is_center(); + if compat_mode == GradientCompatMode::Modern { + if !omit_shape { + shape.to_css(dest)?; + if !omit_position { + dest.write_str(" ")?; + } + } + if !omit_position { + dest.write_str("at ")?; + position.to_css(dest)?; + } + } else { + if !omit_position { + position.to_css(dest)?; + if !omit_shape { + dest.write_str(", ")?; + } + } if !omit_shape { shape.to_css(dest)?; + } + } + let mut skip_comma = omit_shape && omit_position; + for item in &**items { + if !skip_comma { + dest.write_str(", ")?; + } + skip_comma = false; + item.to_css(dest)?; + } + }, + Gradient::Conic { + ref angle, + ref position, + ref items, + .. + } => { + dest.write_str("conic-gradient(")?; + let omit_angle = angle.is_zero(); + let omit_position = position.is_center(); + if !omit_angle { + dest.write_str("from ")?; + angle.to_css(dest)?; + if !omit_position { dest.write_str(" ")?; } + } + if !omit_position { dest.write_str("at ")?; position.to_css(dest)?; - } else { - position.to_css(dest)?; - if !omit_shape { + } + let mut skip_comma = omit_angle && omit_position; + for item in &**items { + if !skip_comma { dest.write_str(", ")?; - shape.to_css(dest)?; } + skip_comma = false; + item.to_css(dest)?; } - false }, - }; - for item in &*self.items { - if !skip_comma { - dest.write_str(", ")?; - } - skip_comma = false; - item.to_css(dest)?; } dest.write_str(")") } } -impl<D, L, LoP, P> GradientKind<D, L, LoP, P> { - fn label(&self) -> &str { - match *self { - GradientKind::Linear(..) => "linear", - GradientKind::Radial(..) => "radial", - } - } -} - /// The direction of a linear gradient. pub trait LineDirection { /// Whether this direction points towards, and thus can be omitted. diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index a97d9e1018a..6689405b2b1 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -19,6 +19,7 @@ pub mod basic_shape; pub mod border; #[path = "box.rs"] pub mod box_; +pub mod calc; pub mod color; pub mod column; pub mod counters; diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs index a3552ea3eab..00a9a219df4 100644 --- a/components/style/values/generics/position.rs +++ b/components/style/values/generics/position.rs @@ -31,6 +31,17 @@ pub struct GenericPosition<H, V> { pub vertical: V, } +impl<H, V> PositionComponent for Position<H, V> +where + H: PositionComponent, + V: PositionComponent, +{ + #[inline] + fn is_center(&self) -> bool { + self.horizontal.is_center() && self.vertical.is_center() + } +} + pub use self::GenericPosition as Position; impl<H, V> Position<H, V> { @@ -43,6 +54,13 @@ impl<H, V> Position<H, V> { } } +/// Implements a method that checks if the position is centered. +pub trait PositionComponent { + /// Returns if the position component is 50% or center. + /// For pixel lengths, it always returns false. + fn is_center(&self) -> bool; +} + /// A generic type for representing an `Auto | <position>`. /// This is used by <offset-anchor> for now. /// https://drafts.fxtf.org/motion-1/#offset-anchor-property diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index 82183c30564..2b04d36db78 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -152,7 +152,8 @@ impl<C: Parse, U: Parse> Parse for SVGPaint<C, U> { ToResolvedValue, ToShmem, )] -pub enum SVGLength<L> { +#[repr(C, u8)] +pub enum GenericSVGLength<L> { /// `<length> | <percentage> | <number>` LengthPercentage(L), /// `context-value` @@ -160,6 +161,8 @@ pub enum SVGLength<L> { ContextValue, } +pub use self::GenericSVGLength as SVGLength; + /// Generic value for stroke-dasharray. #[derive( Clone, diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index b77bba40b82..ef414c1f9a0 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -354,7 +354,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage { match *self { Length(len) => len.to_computed_pixel_length_without_context(), Calc(ref calc) => calc.to_computed_pixel_length_without_context(), - _ => Err(()), + Percentage(..) => Err(()), } } } diff --git a/components/style/values/generics/ui.rs b/components/style/values/generics/ui.rs index 945f60fb5dd..6dfbb4a9c94 100644 --- a/components/style/values/generics/ui.rs +++ b/components/style/values/generics/ui.rs @@ -21,19 +21,22 @@ use values::specified::ui::CursorKind; ToResolvedValue, ToShmem, )] -pub struct Cursor<Image> { +#[repr(C)] +pub struct GenericCursor<Image> { /// The parsed images for the cursor. - pub images: Box<[Image]>, + pub images: crate::OwnedSlice<Image>, /// The kind of the cursor [default | help | ...]. pub keyword: CursorKind, } +pub use self::GenericCursor as Cursor; + impl<Image> Cursor<Image> { /// Set `cursor` to `auto` #[inline] pub fn auto() -> Self { Self { - images: vec![].into_boxed_slice(), + images: Default::default(), keyword: CursorKind::Auto, } } @@ -63,24 +66,31 @@ impl<Image: ToCss> ToCss for Cursor<Image> { ToResolvedValue, ToShmem, )] -pub struct CursorImage<ImageUrl, Number> { +#[repr(C)] +pub struct GenericCursorImage<ImageUrl, Number> { /// The url to parse images from. pub url: ImageUrl, - /// The <x> and <y> coordinates. - pub hotspot: Option<(Number, Number)>, + /// Whether the image has a hotspot or not. + pub has_hotspot: bool, + /// The x coordinate. + pub hotspot_x: Number, + /// The y coordinate. + pub hotspot_y: Number, } +pub use self::GenericCursorImage as CursorImage; + impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { self.url.to_css(dest)?; - if let Some((ref x, ref y)) = self.hotspot { + if self.has_hotspot { dest.write_str(" ")?; - x.to_css(dest)?; + self.hotspot_x.to_css(dest)?; dest.write_str(" ")?; - y.to_css(dest)?; + self.hotspot_y.to_css(dest)?; } Ok(()) } diff --git a/components/style/values/resolved/mod.rs b/components/style/values/resolved/mod.rs index 022cb7893c6..45cc5cbd8cf 100644 --- a/components/style/values/resolved/mod.rs +++ b/components/style/values/resolved/mod.rs @@ -6,7 +6,9 @@ //! there are used values. use crate::properties::ComputedValues; +use crate::ArcSlice; use cssparser; +use servo_arc::Arc; use smallvec::SmallVec; mod color; @@ -77,8 +79,11 @@ trivial_to_resolved_value!(computed::url::ComputedUrl); #[cfg(feature = "gecko")] trivial_to_resolved_value!(computed::url::ComputedImageUrl); #[cfg(feature = "servo")] +trivial_to_resolved_value!(html5ever::Namespace); +#[cfg(feature = "servo")] trivial_to_resolved_value!(html5ever::Prefix); trivial_to_resolved_value!(computed::LengthPercentage); +trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType); impl<A, B> ToResolvedValue for (A, B) where @@ -214,3 +219,43 @@ where Self::from(Box::from_resolved_value(resolved.into_box())) } } + +// NOTE(emilio): This is implementable more generically, but it's unlikely what +// you want there, as it forces you to have an extra allocation. +// +// We could do that if needed, ideally with specialization for the case where +// ResolvedValue = T. But we don't need it for now. +impl<T> ToResolvedValue for Arc<T> +where + T: ToResolvedValue<ResolvedValue = T>, +{ + type ResolvedValue = Self; + + #[inline] + fn to_resolved_value(self, _: &Context) -> Self { + self + } + + #[inline] + fn from_resolved_value(resolved: Self) -> Self { + resolved + } +} + +// Same caveat as above applies. +impl<T> ToResolvedValue for ArcSlice<T> +where + T: ToResolvedValue<ResolvedValue = T>, +{ + type ResolvedValue = Self; + + #[inline] + fn to_resolved_value(self, _: &Context) -> Self { + self + } + + #[inline] + fn from_resolved_value(resolved: Self) -> Self { + resolved + } +} diff --git a/components/style/values/specified/align.rs b/components/style/values/specified/align.rs index d0160a32ae6..10f7f3efbfc 100644 --- a/components/style/values/specified/align.rs +++ b/components/style/values/specified/align.rs @@ -556,7 +556,7 @@ impl SpecifiedValueInfo for AlignItems { /// Value of the `justify-items` property /// /// <https://drafts.csswg.org/css-align/#justify-items-property> -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)] #[repr(C)] pub struct JustifyItems(pub AlignFlags); diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 3c32144e71c..b1782c5b294 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -9,8 +9,7 @@ use crate::parser::{Parse, ParserContext}; use crate::values::generics::basic_shape as generic; -use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord}; -use crate::values::generics::basic_shape::{ShapeBox, ShapeSource}; +use crate::values::generics::basic_shape::{Path, PolygonCoord}; use crate::values::generics::rect::Rect; use crate::values::specified::border::BorderRadius; use crate::values::specified::image::Image; @@ -25,14 +24,14 @@ use style_traits::{ParseError, StyleParseErrorKind}; /// A specified alias for FillRule. pub use crate::values::generics::basic_shape::FillRule; -/// A specified clipping shape. -pub type ClippingShape = generic::ClippingShape<BasicShape, SpecifiedUrl>; +/// A specified `clip-path` value. +pub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>; -/// A specified float area shape. -pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>; +/// A specified `shape-outside` value. +pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>; /// A specified basic shape. -pub type BasicShape = generic::BasicShape< +pub type BasicShape = generic::GenericBasicShape< HorizontalPosition, VerticalPosition, LengthPercentage, @@ -65,99 +64,90 @@ fn is_clip_path_path_enabled(_: &ParserContext) -> bool { false } -impl Parse for ClippingShape { - #[inline] - fn parse<'i, 't>( +/// A helper for both clip-path and shape-outside parsing of shapes. +fn parse_shape_or_box<'i, 't, R, ReferenceBox>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R, + to_reference_box: impl FnOnce(ReferenceBox) -> R, +) -> Result<R, ParseError<'i>> +where + ReferenceBox: Default + Parse, +{ + fn parse_component<U: Parse>( context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - if is_clip_path_path_enabled(context) { - if let Ok(p) = input.try(|i| Path::parse(context, i)) { - return Ok(ShapeSource::Path(p)); - } + input: &mut Parser, + component: &mut Option<U>, + ) -> bool { + if component.is_some() { + return false; // already parsed this component } - if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { - return Ok(ShapeSource::ImageOrUrl(url)); - } + *component = input.try(|i| U::parse(context, i)).ok(); + component.is_some() + } + + let mut shape = None; + let mut ref_box = None; - Self::parse_common(context, input) + while parse_component(context, input, &mut shape) || + parse_component(context, input, &mut ref_box) + { + // } -} -impl Parse for FloatAreaShape { - #[inline] - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - if let Ok(image) = input.try(|i| Image::parse_with_cors_anonymous(context, i)) { - return Ok(ShapeSource::ImageOrUrl(image)); - } + if let Some(shp) = shape { + return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default())); + } - Self::parse_common(context, input) + match ref_box { + Some(r) => Ok(to_reference_box(r)), + None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), } } -impl<ReferenceBox, ImageOrUrl> ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> -where - ReferenceBox: Parse, -{ - /// The internal parser for ShapeSource. - fn parse_common<'i, 't>( +impl Parse for ClipPath { + #[inline] + fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { if input.try(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(ShapeSource::None); + return Ok(ClipPath::None); } - fn parse_component<U: Parse>( - context: &ParserContext, - input: &mut Parser, - component: &mut Option<U>, - ) -> bool { - if component.is_some() { - return false; // already parsed this component + if is_clip_path_path_enabled(context) { + if let Ok(p) = input.try(|i| Path::parse(context, i)) { + return Ok(ClipPath::Path(p)); } - - *component = input.try(|i| U::parse(context, i)).ok(); - component.is_some() } - let mut shape = None; - let mut ref_box = None; - - while parse_component(context, input, &mut shape) || - parse_component(context, input, &mut ref_box) - { - // - } - - if let Some(shp) = shape { - return Ok(ShapeSource::Shape(Box::new(shp), ref_box)); + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + return Ok(ClipPath::Url(url)); } - ref_box - .map(ShapeSource::Box) - .ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + parse_shape_or_box(context, input, ClipPath::Shape, ClipPath::Box) } } -impl Parse for GeometryBox { +impl Parse for ShapeOutside { + #[inline] fn parse<'i, 't>( - _context: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { - if let Ok(shape_box) = input.try(ShapeBox::parse) { - return Ok(GeometryBox::ShapeBox(shape_box)); + // Need to parse this here so that `Image::parse_with_cors_anonymous` + // doesn't parse it. + if input.try(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(ShapeOutside::None); } - try_match_ident_ignore_ascii_case! { input, - "fill-box" => Ok(GeometryBox::FillBox), - "stroke-box" => Ok(GeometryBox::StrokeBox), - "view-box" => Ok(GeometryBox::ViewBox), + if let Ok(image) = input.try(|i| Image::parse_with_cors_anonymous(context, i)) { + debug_assert_ne!(image, Image::None); + return Ok(ShapeOutside::Image(image)); } + + parse_shape_or_box(context, input, ShapeOutside::Shape, ShapeOutside::Box) } } diff --git a/components/style/values/specified/border.rs b/components/style/values/specified/border.rs index 1b1ed74d568..28be8177cf4 100644 --- a/components/style/values/specified/border.rs +++ b/components/style/values/specified/border.rs @@ -234,7 +234,18 @@ impl Parse for BorderSpacing { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( - Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, )] pub enum BorderImageRepeatKeyword { Stretch, diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index fa5d5a2d43a..987a0567432 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -104,6 +104,8 @@ pub enum DisplayInside { #[cfg(feature = "gecko")] MozGridLine, #[cfg(feature = "gecko")] + MozStack, + #[cfg(feature = "gecko")] MozDeck, #[cfg(feature = "gecko")] MozPopup, @@ -227,6 +229,8 @@ impl Display { #[cfg(feature = "gecko")] pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine); #[cfg(feature = "gecko")] + pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack); + #[cfg(feature = "gecko")] pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck); #[cfg(feature = "gecko")] pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup); @@ -616,6 +620,8 @@ impl Parse for Display { #[cfg(feature = "gecko")] "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine, #[cfg(feature = "gecko")] + "-moz-stack" if moz_display_values_enabled(context) => Display::MozStack, + #[cfg(feature = "gecko")] "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck, #[cfg(feature = "gecko")] "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup, @@ -1595,7 +1601,7 @@ pub enum Appearance { Meterchunk, /// The "arrowed" part of the dropdown button that open up a dropdown list. #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] - MozMenulistButton, + MozMenulistArrowButton, /// For HTML's <input type=number> NumberInput, /// A horizontal progress bar. @@ -1624,7 +1630,7 @@ pub enum Appearance { RadioLabel, /// nsRangeFrame and its subparts Range, - RangeThumb, + RangeThumb, // FIXME: This should not be exposed to content. /// The resizer background area in a status bar for the resizer widget in /// the corner of a window. #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] @@ -1850,6 +1856,14 @@ pub enum Appearance { Count, } +impl Appearance { + /// Returns whether we're the `none` value. + #[inline] + pub fn is_none(self) -> bool { + self == Appearance::None + } +} + /// A kind of break between two boxes. /// /// https://drafts.csswg.org/css-break/#break-between diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index 9736491adce..9947fc7a8cc 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -7,15 +7,16 @@ //! [calc]: https://drafts.csswg.org/css-values/#calc-notation use crate::parser::ParserContext; -use crate::values::computed; +use crate::values::generics::calc as generic; +use crate::values::generics::calc::{MinMaxOp, SortKey}; use crate::values::specified::length::ViewportPercentageLength; use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength}; use crate::values::specified::{self, Angle, Time}; use crate::values::{CSSFloat, CSSInteger}; use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token}; use smallvec::SmallVec; +use std::cmp; use std::fmt::{self, Write}; -use std::{cmp, mem}; use style_traits::values::specified::AllowedNumericType; use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; @@ -32,40 +33,9 @@ pub enum MathFunction { Clamp, } -/// This determines the order in which we serialize members of a calc() -/// sum. -/// -/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum SortKey { - Number, - Percentage, - Ch, - Deg, - Em, - Ex, - Px, - Rem, - Sec, - Vh, - Vmax, - Vmin, - Vw, - Other, -} - -/// Whether we're a `min` or `max` function. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum MinMaxOp { - /// `min()` - Min, - /// `max()` - Max, -} - -/// A node inside a `Calc` expression's AST. -#[derive(Clone, Debug, PartialEq)] -pub enum CalcNode { +/// A leaf node inside a `Calc` expression's AST. +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] +pub enum Leaf { /// `<length>` Length(NoCalcLength), /// `<angle>` @@ -76,27 +46,28 @@ pub enum CalcNode { Percentage(CSSFloat), /// `<number>` Number(CSSFloat), - /// An expression of the form `x + y + ...`. Subtraction is represented by - /// the negated expression of the right hand side. - Sum(Box<[CalcNode]>), - /// A `min()` / `max()` function. - MinMax(Box<[CalcNode]>, MinMaxOp), - /// A `clamp()` function. - Clamp { - /// The minimum value. - min: Box<CalcNode>, - /// The central value. - center: Box<CalcNode>, - /// The maximum value. - max: Box<CalcNode>, - }, +} + +impl ToCss for Leaf { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + match *self { + Self::Length(ref l) => l.to_css(dest), + Self::Number(ref n) => n.to_css(dest), + Self::Percentage(p) => crate::values::serialize_percentage(p, dest), + Self::Angle(ref a) => a.to_css(dest), + Self::Time(ref t) => t.to_css(dest), + } + } } /// An expected unit we intend to parse within a `calc()` expression. /// /// This is used as a hint for the parser to fast-reject invalid expressions. #[derive(Clone, Copy, PartialEq)] -pub enum CalcUnit { +enum CalcUnit { /// `<number>` Number, /// `<length>` @@ -117,178 +88,50 @@ pub enum CalcUnit { /// relative lengths, and to_computed_pixel_length_without_context() handles /// this case. Therefore, if you want to add a new field, please make sure this /// function work properly. -#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] #[allow(missing_docs)] pub struct CalcLengthPercentage { + #[css(skip)] pub clamping_mode: AllowedNumericType, - pub absolute: Option<AbsoluteLength>, - pub vw: Option<CSSFloat>, - pub vh: Option<CSSFloat>, - pub vmin: Option<CSSFloat>, - pub vmax: Option<CSSFloat>, - pub em: Option<CSSFloat>, - pub ex: Option<CSSFloat>, - pub ch: Option<CSSFloat>, - pub rem: Option<CSSFloat>, - pub percentage: Option<computed::Percentage>, -} - -impl ToCss for CalcLengthPercentage { - /// <https://drafts.csswg.org/css-values/#calc-serialize> - /// - /// FIXME(emilio): Should this simplify away zeros? - #[allow(unused_assignments)] - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - use num_traits::Zero; - - let mut first_value = true; - macro_rules! first_value_check { - ($val:expr) => { - if !first_value { - dest.write_str(if $val < Zero::zero() { " - " } else { " + " })?; - } else if $val < Zero::zero() { - dest.write_str("-")?; - } - first_value = false; - }; - } - - macro_rules! serialize { - ( $( $val:ident ),* ) => { - $( - if let Some(val) = self.$val { - first_value_check!(val); - val.abs().to_css(dest)?; - dest.write_str(stringify!($val))?; - } - )* - }; - } - - macro_rules! serialize_abs { - ( $( $val:ident ),+ ) => { - $( - if let Some(AbsoluteLength::$val(v)) = self.absolute { - first_value_check!(v); - AbsoluteLength::$val(v.abs()).to_css(dest)?; - } - )+ - }; - } - - dest.write_str("calc(")?; - - // NOTE(emilio): Percentages first because of web-compat problems, see: - // https://github.com/w3c/csswg-drafts/issues/1731 - if let Some(val) = self.percentage { - first_value_check!(val.0); - val.abs().to_css(dest)?; - } - - // NOTE(emilio): The order here it's very intentional, and alphabetic - // per the spec linked above. - serialize!(ch); - serialize_abs!(Cm); - serialize!(em, ex); - serialize_abs!(In, Mm, Pc, Pt, Px, Q); - serialize!(rem, vh, vmax, vmin, vw); - - dest.write_str(")") - } + pub node: CalcNode, } impl SpecifiedValueInfo for CalcLengthPercentage {} -macro_rules! impl_generic_to_type { - ($self:ident, $self_variant:ident, $to_self:ident, $to_float:ident, $from_float:path) => {{ - if let Self::$self_variant(ref v) = *$self { - return Ok(v.clone()); - } - - Ok(match *$self { - Self::Sum(ref expressions) => { - let mut sum = 0.; - for sub in &**expressions { - sum += sub.$to_self()?.$to_float(); - } - $from_float(sum) - }, - Self::Clamp { - ref min, - ref center, - ref max, - } => { - let min = min.$to_self()?; - let center = center.$to_self()?; - let max = max.$to_self()?; - - // Equivalent to cmp::max(min, cmp::min(center, max)) - // - // But preserving units when appropriate. - let center_float = center.$to_float(); - let min_float = min.$to_float(); - let max_float = max.$to_float(); - - let mut result = center; - let mut result_float = center_float; - - if result_float > max_float { - result = max; - result_float = max_float; - } +impl PartialOrd for Leaf { + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + use self::Leaf::*; - if result_float < min_float { - min - } else { - result - } - }, - Self::MinMax(ref nodes, op) => { - let mut result = nodes[0].$to_self()?; - let mut result_float = result.$to_float(); - for node in nodes.iter().skip(1) { - let candidate = node.$to_self()?; - let candidate_float = candidate.$to_float(); - let candidate_wins = match op { - MinMaxOp::Min => candidate_float < result_float, - MinMaxOp::Max => candidate_float > result_float, - }; - if candidate_wins { - result = candidate; - result_float = candidate_float; - } - } - result - }, - Self::Length(..) | - Self::Angle(..) | - Self::Time(..) | - Self::Percentage(..) | - Self::Number(..) => return Err(()), - }) - }}; -} + if std::mem::discriminant(self) != std::mem::discriminant(other) { + return None; + } -impl PartialOrd for CalcNode { - fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { - use self::CalcNode::*; match (self, other) { (&Length(ref one), &Length(ref other)) => one.partial_cmp(other), (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other), (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()), (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()), (&Number(ref one), &Number(ref other)) => one.partial_cmp(other), - _ => None, + _ => { + match *self { + Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..) => {}, + } + unsafe { + debug_unreachable!("Forgot a branch?"); + } + }, } } } -impl CalcNode { - fn negate(&mut self) { - self.mul_by(-1.); +impl generic::CalcNodeLeaf for Leaf { + fn is_negative(&self) -> bool { + match *self { + Self::Length(ref l) => l.is_negative(), + Self::Percentage(n) | Self::Number(n) => n < 0., + Self::Angle(ref a) => a.degrees() < 0., + Self::Time(ref t) => t.seconds() < 0., + } } fn mul_by(&mut self, scalar: f32) { @@ -310,44 +153,10 @@ impl CalcNode { Self::Percentage(ref mut p) => { *p *= scalar; }, - // Multiplication is distributive across this. - Self::Sum(ref mut children) => { - for node in &mut **children { - node.mul_by(scalar); - } - }, - // This one is a bit trickier. - Self::MinMax(ref mut children, ref mut op) => { - for node in &mut **children { - node.mul_by(scalar); - } - - // For negatives we need to invert the operation. - if scalar < 0. { - *op = match *op { - MinMaxOp::Min => MinMaxOp::Max, - MinMaxOp::Max => MinMaxOp::Min, - } - } - }, - // Multiplication is distributive across these. - Self::Clamp { - ref mut min, - ref mut center, - ref mut max, - } => { - min.mul_by(scalar); - center.mul_by(scalar); - max.mul_by(scalar); - // For negatives we need to swap min / max. - if scalar < 0. { - mem::swap(min, max); - } - }, } } - fn calc_node_sort_key(&self) -> SortKey { + fn sort_key(&self) -> SortKey { match *self { Self::Number(..) => SortKey::Number, Self::Percentage(..) => SortKey::Percentage, @@ -369,7 +178,12 @@ impl CalcNode { }, NoCalcLength::ServoCharacterWidth(..) => unreachable!(), }, - Self::Sum(..) | Self::MinMax(..) | Self::Clamp { .. } => SortKey::Other, + } + } + + fn simplify(&mut self) { + if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self { + *abs = AbsoluteLength::Px(abs.to_px()); } } @@ -378,7 +192,11 @@ impl CalcNode { /// Only handles leaf nodes, it's the caller's responsibility to simplify /// them before calling this if needed. fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> { - use self::CalcNode::*; + use self::Leaf::*; + + if std::mem::discriminant(self) != std::mem::discriminant(other) { + return Err(()); + } match (self, other) { (&mut Number(ref mut one), &Number(ref other)) | @@ -394,170 +212,24 @@ impl CalcNode { (&mut Length(ref mut one), &Length(ref other)) => { *one = one.try_sum(other)?; }, - _ => return Err(()), - } - - Ok(()) - } - - /// Simplifies and sorts the calculation. This is only needed if it's going - /// to be preserved after parsing (so, for `<length-percentage>`). Otherwise - /// we can just evaluate it and we'll come up with a simplified value - /// anyways. - fn simplify_and_sort_children(&mut self) { - macro_rules! replace_self_with { - ($slot:expr) => {{ - let result = mem::replace($slot, Self::Number(0.)); - mem::replace(self, result); - }}; - } - match *self { - Self::Clamp { - ref mut min, - ref mut center, - ref mut max, - } => { - min.simplify_and_sort_children(); - center.simplify_and_sort_children(); - max.simplify_and_sort_children(); - - // NOTE: clamp() is max(min, min(center, max)) - let min_cmp_center = match min.partial_cmp(¢er) { - Some(o) => o, - None => return, - }; - - // So if we can prove that min is more than center, then we won, - // as that's what we should always return. - if matches!(min_cmp_center, cmp::Ordering::Greater) { - return replace_self_with!(&mut **min); - } - - // Otherwise try with max. - let max_cmp_center = match max.partial_cmp(¢er) { - Some(o) => o, - None => return, - }; - - if matches!(max_cmp_center, cmp::Ordering::Less) { - // max is less than center, so we need to return effectively - // `max(min, max)`. - let max_cmp_min = match max.partial_cmp(&min) { - Some(o) => o, - None => { - debug_assert!( - false, - "We compared center with min and max, how are \ - min / max not comparable with each other?" - ); - return; - }, - }; - - if matches!(max_cmp_min, cmp::Ordering::Less) { - return replace_self_with!(&mut **min); - } - - return replace_self_with!(&mut **max); - } - - // Otherwise we're the center node. - return replace_self_with!(&mut **center); - }, - Self::MinMax(ref mut children, op) => { - for child in &mut **children { - child.simplify_and_sort_children(); - } - - let winning_order = match op { - MinMaxOp::Min => cmp::Ordering::Less, - MinMaxOp::Max => cmp::Ordering::Greater, - }; - - let mut result = 0; - for i in 1..children.len() { - let o = match children[i].partial_cmp(&children[result]) { - // We can't compare all the children, so we can't - // know which one will actually win. Bail out and - // keep ourselves as a min / max function. - // - // TODO: Maybe we could simplify compatible children, - // see https://github.com/w3c/csswg-drafts/issues/4756 - None => return, - Some(o) => o, - }; - - if o == winning_order { - result = i; - } - } - - replace_self_with!(&mut children[result]); - }, - Self::Sum(ref mut children_slot) => { - let mut sums_to_merge = SmallVec::<[_; 3]>::new(); - let mut extra_kids = 0; - for (i, child) in children_slot.iter_mut().enumerate() { - child.simplify_and_sort_children(); - if let Self::Sum(ref mut children) = *child { - extra_kids += children.len(); - sums_to_merge.push(i); - } - } - - // If we only have one kid, we've already simplified it, and it - // doesn't really matter whether it's a sum already or not, so - // lift it up and continue. - if children_slot.len() == 1 { - return replace_self_with!(&mut children_slot[0]); - } - - let mut children = mem::replace(children_slot, Box::new([])).into_vec(); - - if !sums_to_merge.is_empty() { - children.reserve(extra_kids - sums_to_merge.len()); - // Merge all our nested sums, in reverse order so that the - // list indices are not invalidated. - for i in sums_to_merge.drain(..).rev() { - let kid_children = match children.swap_remove(i) { - Self::Sum(c) => c, - _ => unreachable!(), - }; - - // This would be nicer with - // https://github.com/rust-lang/rust/issues/59878 fixed. - children.extend(kid_children.into_vec()); - } - } - - debug_assert!(children.len() >= 2, "Should still have multiple kids!"); - - // Sort by spec order. - children.sort_unstable_by_key(|c| c.calc_node_sort_key()); - - // NOTE: if the function returns true, by the docs of dedup_by, - // a is removed. - children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok()); - - if children.len() == 1 { - // If only one children remains, lift it up, and carry on. - replace_self_with!(&mut children[0]); - } else { - // Else put our simplified children back. - mem::replace(children_slot, children.into_boxed_slice()); + _ => { + match *other { + Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) => {}, } - }, - Self::Length(ref mut len) => { - if let NoCalcLength::Absolute(ref mut absolute_length) = *len { - *absolute_length = AbsoluteLength::Px(absolute_length.to_px()); + unsafe { + debug_unreachable!(); } }, - Self::Percentage(..) | Self::Angle(..) | Self::Time(..) | Self::Number(..) => { - // These are leaves already, nothing to do. - }, } + + Ok(()) } +} + +/// A calc node representation for specified values. +pub type CalcNode = generic::GenericCalcNode<Leaf>; +impl CalcNode { /// Tries to parse a single element in the expression, that is, a /// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to /// `expected_unit`. @@ -571,7 +243,7 @@ impl CalcNode { ) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); match (input.next()?, expected_unit) { - (&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)), + (&Token::Number { value, .. }, _) => Ok(CalcNode::Leaf(Leaf::Number(value))), ( &Token::Dimension { value, ref unit, .. @@ -583,18 +255,22 @@ impl CalcNode { value, ref unit, .. }, CalcUnit::LengthPercentage, - ) => NoCalcLength::parse_dimension(context, value, unit) - .map(CalcNode::Length) - .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + ) => match NoCalcLength::parse_dimension(context, value, unit) { + Ok(l) => Ok(CalcNode::Leaf(Leaf::Length(l))), + Err(()) => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + }, ( &Token::Dimension { value, ref unit, .. }, CalcUnit::Angle, ) => { - Angle::parse_dimension(value, unit, /* from_calc = */ true) - .map(CalcNode::Angle) - .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + match Angle::parse_dimension(value, unit, /* from_calc = */ true) { + Ok(a) => Ok(CalcNode::Leaf(Leaf::Angle(a))), + Err(()) => { + Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + }, + } }, ( &Token::Dimension { @@ -602,13 +278,16 @@ impl CalcNode { }, CalcUnit::Time, ) => { - Time::parse_dimension(value, unit, /* from_calc = */ true) - .map(CalcNode::Time) - .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + match Time::parse_dimension(value, unit, /* from_calc = */ true) { + Ok(t) => Ok(CalcNode::Leaf(Leaf::Time(t))), + Err(()) => { + Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + }, + } }, (&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) | (&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => { - Ok(CalcNode::Percentage(unit_value)) + Ok(CalcNode::Leaf(Leaf::Percentage(unit_value))) }, (&Token::ParenthesisBlock, _) => input.parse_nested_block(|input| { CalcNode::parse_argument(context, input, expected_unit) @@ -654,11 +333,9 @@ impl CalcNode { // // Consider adding an API to cssparser to specify the // initial vector capacity? - let arguments = input - .parse_comma_separated(|input| { - Self::parse_argument(context, input, expected_unit) - })? - .into_boxed_slice(); + let arguments = input.parse_comma_separated(|input| { + Self::parse_argument(context, input, expected_unit) + })?; let op = match function { MathFunction::Min => MinMaxOp::Min, @@ -666,7 +343,7 @@ impl CalcNode { _ => unreachable!(), }; - Ok(Self::MinMax(arguments, op)) + Ok(Self::MinMax(arguments.into(), op)) }, } }) @@ -712,7 +389,7 @@ impl CalcNode { Ok(if sum.len() == 1 { sum.drain(..).next().unwrap() } else { - Self::Sum(sum.into_boxed_slice()) + Self::Sum(sum.into_boxed_slice().into()) }) } @@ -773,106 +450,64 @@ impl CalcNode { Ok(node) } - /// Tries to simplify this expression into a `<length>` or `<percentage`> + /// Tries to simplify this expression into a `<length>` or `<percentage>` /// value. - fn to_length_or_percentage( - &mut self, + fn into_length_or_percentage( + mut self, clamping_mode: AllowedNumericType, ) -> Result<CalcLengthPercentage, ()> { - let mut ret = CalcLengthPercentage { - clamping_mode, - ..Default::default() - }; - self.simplify_and_sort_children(); - self.add_length_or_percentage_to(&mut ret, 1.0)?; - Ok(ret) - } + // Keep track of whether there's any invalid member of the calculation, + // so as to reject the calculation properly at parse-time. + let mut any_invalid = false; + self.visit_depth_first(|node| { + if let CalcNode::Leaf(ref l) = *node { + any_invalid |= !matches!(*l, Leaf::Percentage(..) | Leaf::Length(..)); + } + node.simplify_and_sort_direct_children(); + }); - /// Puts this `<length>` or `<percentage>` into `ret`, or error. - /// - /// `factor` is the sign or multiplicative factor to account for the sign - /// (this allows adding and substracting into the return value). - fn add_length_or_percentage_to( - &self, - ret: &mut CalcLengthPercentage, - factor: CSSFloat, - ) -> Result<(), ()> { - match *self { - CalcNode::Percentage(pct) => { - ret.percentage = Some(computed::Percentage( - ret.percentage.map_or(0., |p| p.0) + pct * factor, - )); - }, - CalcNode::Length(ref l) => match *l { - NoCalcLength::Absolute(abs) => { - ret.absolute = Some(match ret.absolute { - Some(value) => value + abs * factor, - None => abs * factor, - }); - }, - NoCalcLength::FontRelative(rel) => match rel { - FontRelativeLength::Em(em) => { - ret.em = Some(ret.em.unwrap_or(0.) + em * factor); - }, - FontRelativeLength::Ex(ex) => { - ret.ex = Some(ret.ex.unwrap_or(0.) + ex * factor); - }, - FontRelativeLength::Ch(ch) => { - ret.ch = Some(ret.ch.unwrap_or(0.) + ch * factor); - }, - FontRelativeLength::Rem(rem) => { - ret.rem = Some(ret.rem.unwrap_or(0.) + rem * factor); - }, - }, - NoCalcLength::ViewportPercentage(rel) => match rel { - ViewportPercentageLength::Vh(vh) => { - ret.vh = Some(ret.vh.unwrap_or(0.) + vh * factor) - }, - ViewportPercentageLength::Vw(vw) => { - ret.vw = Some(ret.vw.unwrap_or(0.) + vw * factor) - }, - ViewportPercentageLength::Vmax(vmax) => { - ret.vmax = Some(ret.vmax.unwrap_or(0.) + vmax * factor) - }, - ViewportPercentageLength::Vmin(vmin) => { - ret.vmin = Some(ret.vmin.unwrap_or(0.) + vmin * factor) - }, - }, - NoCalcLength::ServoCharacterWidth(..) => unreachable!(), - }, - CalcNode::Sum(ref children) => { - for child in &**children { - child.add_length_or_percentage_to(ret, factor)?; - } - }, - CalcNode::MinMax(..) | CalcNode::Clamp { .. } => { - // FIXME(emilio): Implement min/max/clamp for length-percentage. - return Err(()); - }, - CalcNode::Angle(..) | CalcNode::Time(..) | CalcNode::Number(..) => return Err(()), + if any_invalid { + return Err(()); } - Ok(()) + Ok(CalcLengthPercentage { + clamping_mode, + node: self, + }) } /// Tries to simplify this expression into a `<time>` value. fn to_time(&self) -> Result<Time, ()> { - impl_generic_to_type!(self, Time, to_time, seconds, Time::from_calc) + let seconds = self.resolve(|leaf| match *leaf { + Leaf::Time(ref t) => Ok(t.seconds()), + _ => Err(()), + })?; + Ok(Time::from_calc(seconds)) } /// Tries to simplify this expression into an `Angle` value. fn to_angle(&self) -> Result<Angle, ()> { - impl_generic_to_type!(self, Angle, to_angle, degrees, Angle::from_calc) + let degrees = self.resolve(|leaf| match *leaf { + Leaf::Angle(ref angle) => Ok(angle.degrees()), + _ => Err(()), + })?; + Ok(Angle::from_calc(degrees)) } /// Tries to simplify this expression into a `<number>` value. fn to_number(&self) -> Result<CSSFloat, ()> { - impl_generic_to_type!(self, Number, to_number, clone, From::from) + self.resolve(|leaf| match *leaf { + Leaf::Number(n) => Ok(n), + _ => Err(()), + }) } /// Tries to simplify this expression into a `<percentage>` value. fn to_percentage(&self) -> Result<CSSFloat, ()> { - impl_generic_to_type!(self, Percentage, to_percentage, clone, From::from) + self.resolve(|leaf| match *leaf { + Leaf::Percentage(p) => Ok(p), + _ => Err(()), + }) } /// Given a function name, and the location from where the token came from, @@ -926,7 +561,7 @@ impl CalcNode { function: MathFunction, ) -> Result<CalcLengthPercentage, ParseError<'i>> { Self::parse(context, input, function, CalcUnit::LengthPercentage)? - .to_length_or_percentage(clamping_mode) + .into_length_or_percentage(clamping_mode) .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } @@ -949,7 +584,7 @@ impl CalcNode { function: MathFunction, ) -> Result<CalcLengthPercentage, ParseError<'i>> { Self::parse(context, input, function, CalcUnit::Length)? - .to_length_or_percentage(clamping_mode) + .into_length_or_percentage(clamping_mode) .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index be969f27cd4..e697f2523c9 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -140,8 +140,10 @@ pub enum SystemColor { Windowframe, Windowtext, MozButtondefault, - MozDefaultColor, - MozDefaultBackgroundColor, + #[parse(aliases = "-moz-default-color")] + Canvastext, + #[parse(aliases = "-moz-default-background-color")] + Canvas, MozDialog, MozDialogtext, /// Used to highlight valid regions to drop something onto. @@ -230,9 +232,12 @@ pub enum SystemColor { /// colors. MozNativehyperlinktext, - MozHyperlinktext, - MozActivehyperlinktext, - MozVisitedhyperlinktext, + #[parse(aliases = "-moz-hyperlinktext")] + Linktext, + #[parse(aliases = "-moz-activehyperlinktext")] + Activetext, + #[parse(aliases = "-moz-visitedhyperlinktext")] + Visitedtext, /// Combobox widgets MozComboboxtext, @@ -253,11 +258,11 @@ impl SystemColor { let prefs = cx.device().pref_sheet_prefs(); convert_nscolor_to_computedcolor(match *self { - SystemColor::MozDefaultColor => prefs.mDefaultColor, - SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor, - SystemColor::MozHyperlinktext => prefs.mLinkColor, - SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor, - SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor, + SystemColor::Canvastext => prefs.mDefaultColor, + SystemColor::Canvas => prefs.mDefaultBackgroundColor, + SystemColor::Linktext => prefs.mLinkColor, + SystemColor::Activetext => prefs.mActiveLinkColor, + SystemColor::Visitedtext => prefs.mVisitedLinkColor, _ => unsafe { bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document()) diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 94c28d068f9..dd9f9d3b86d 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -492,7 +492,9 @@ impl ToComputedValue for FontStretch { SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, + ToComputedValue, ToCss, + ToResolvedValue, ToShmem, )] #[allow(missing_docs)] @@ -534,7 +536,9 @@ impl Default for KeywordSize { PartialEq, ToAnimatedValue, ToAnimatedZero, + ToComputedValue, ToCss, + ToResolvedValue, ToShmem, )] /// Additional information for keyword-derived font sizes. @@ -567,7 +571,7 @@ impl KeywordInfo { /// Computes the final size for this font-size keyword, accounting for /// text-zoom. fn to_computed_value(&self, context: &Context) -> CSSPixelLength { - let base = context.maybe_zoom_text(self.kw.to_computed_value(context).0); + let base = context.maybe_zoom_text(self.kw.to_length(context).0); base * self.factor + context.maybe_zoom_text(self.offset) } @@ -760,11 +764,10 @@ const LARGER_FONT_SIZE_RATIO: f32 = 1.2; /// The default font size. pub const FONT_MEDIUM_PX: i32 = 16; -#[cfg(feature = "servo")] -impl ToComputedValue for KeywordSize { - type ComputedValue = NonNegativeLength; +impl KeywordSize { #[inline] - fn to_computed_value(&self, _: &Context) -> NonNegativeLength { + #[cfg(feature = "servo")] + fn to_length(&self, _: &Context) -> NonNegativeLength { let medium = Length::new(FONT_MEDIUM_PX as f32); // https://drafts.csswg.org/css-fonts-3/#font-size-prop NonNegative(match *self { @@ -779,17 +782,9 @@ impl ToComputedValue for KeywordSize { }) } + #[cfg(feature = "gecko")] #[inline] - fn from_computed_value(_: &NonNegativeLength) -> Self { - unreachable!() - } -} - -#[cfg(feature = "gecko")] -impl ToComputedValue for KeywordSize { - type ComputedValue = NonNegativeLength; - #[inline] - fn to_computed_value(&self, cx: &Context) -> NonNegativeLength { + fn to_length(&self, cx: &Context) -> NonNegativeLength { use crate::context::QuirksMode; // The tables in this function are originally from @@ -857,11 +852,6 @@ impl ToComputedValue for KeywordSize { base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0 }) } - - #[inline] - fn from_computed_value(_: &NonNegativeLength) -> Self { - unreachable!() - } } impl FontSize { diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index 4830aea24d6..54d31d5826d 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -185,16 +185,6 @@ impl TrackRepeat<LengthPercentage, Integer> { values.push(track_size); names.push(current_names); - if is_auto { - // FIXME: In the older version of the spec - // (https://www.w3.org/TR/2015/WD-css-grid-1-20150917/#typedef-auto-repeat), - // if the repeat type is `<auto-repeat>` we shouldn't try to parse more than - // one `TrackSize`. But in current version of the spec, this is deprecated - // but we are adding this for gecko parity. We should remove this when - // gecko implements new spec. - names.push(input.try(parse_line_names).unwrap_or_default()); - break; - } } else { if values.is_empty() { // expecting at least one <track-size> diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 14a2a9ad9c8..59250c45a6f 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -19,7 +19,8 @@ use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPosi use crate::values::specified::position::{Position, PositionComponent, Side}; use crate::values::specified::url::SpecifiedImageUrl; use crate::values::specified::{ - Angle, Color, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, + Angle, AngleOrPercentage, Color, Length, LengthPercentage, NonNegativeLength, + NonNegativeLengthPercentage, }; use crate::values::specified::{Number, NumberOrPercentage, Percentage}; use crate::Atom; @@ -32,24 +33,6 @@ use std::fmt::{self, Write}; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; -/// A specified image layer. -pub type ImageLayer = generic::GenericImageLayer<Image>; - -impl ImageLayer { - /// This is a specialization of Either with an alternative parse - /// method to provide anonymous CORS headers for the Image url fetch. - pub fn parse_with_cors_anonymous<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - if let Ok(v) = input.try(|i| Image::parse_with_cors_anonymous(context, i)) { - return Ok(generic::GenericImageLayer::Image(v)); - } - input.expect_ident_matching("none")?; - Ok(generic::GenericImageLayer::None) - } -} - /// Specified values for an image according to CSS-IMAGES. /// <https://drafts.csswg.org/css-images/#image-values> pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl>; @@ -62,9 +45,23 @@ pub type Gradient = generic::Gradient< NonNegativeLength, NonNegativeLengthPercentage, Position, + Angle, + AngleOrPercentage, Color, >; +type LengthPercentageItemList = crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>; + +#[cfg(feature = "gecko")] +fn conic_gradients_enabled() -> bool { + static_prefs::pref!("layout.css.conic-gradient.enabled") +} + +#[cfg(feature = "servo")] +fn conic_gradients_enabled() -> bool { + false +} + impl SpecifiedValueInfo for Gradient { const SUPPORTED_TYPES: u8 = CssType::GRADIENT; @@ -85,13 +82,13 @@ impl SpecifiedValueInfo for Gradient { "-moz-repeating-radial-gradient", "-webkit-gradient", ]); + + if conic_gradients_enabled() { + f(&["conic-gradient", "repeating-conic-gradient"]); + } } } -/// A specified gradient kind. -pub type GradientKind = - generic::GradientKind<LineDirection, NonNegativeLength, NonNegativeLengthPercentage, Position>; - /// A specified gradient line direction. /// /// FIXME(emilio): This should be generic over Angle. @@ -110,16 +107,10 @@ pub enum LineDirection { /// A specified ending shape. pub type EndingShape = generic::EndingShape<NonNegativeLength, NonNegativeLengthPercentage>; -/// A specified gradient item. -pub type GradientItem = generic::GradientItem<Color, LengthPercentage>; - -/// A computed color stop. -pub type ColorStop = generic::ColorStop<Color, LengthPercentage>; - /// Specified values for `moz-image-rect` /// -moz-image-rect(<uri>, top, right, bottom, left); -#[cfg(feature = "gecko")] -pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, SpecifiedImageUrl>; +#[cfg(all(feature = "gecko", not(feature = "cbindgen")))] +pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, SpecifiedImageUrl>; #[cfg(not(feature = "gecko"))] #[derive( @@ -141,6 +132,9 @@ impl Parse for Image { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Image, ParseError<'i>> { + if input.try(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(generic::Image::None); + } if let Ok(url) = input.try(|input| SpecifiedImageUrl::parse(context, input)) { return Ok(generic::Image::Url(url)); } @@ -211,10 +205,11 @@ impl Parse for Gradient { enum Shape { Linear, Radial, + Conic, } let func = input.expect_function()?; - let (shape, repeating, mut compat_mode) = match_ignore_ascii_case! { &func, + let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &func, "linear-gradient" => { (Shape::Linear, false, GradientCompatMode::Modern) }, @@ -255,6 +250,12 @@ impl Parse for Gradient { "-moz-repeating-radial-gradient" => { (Shape::Radial, true, GradientCompatMode::Moz) }, + "conic-gradient" if conic_gradients_enabled() => { + (Shape::Conic, false, GradientCompatMode::Modern) + }, + "repeating-conic-gradient" if conic_gradients_enabled() => { + (Shape::Conic, true, GradientCompatMode::Modern) + }, "-webkit-gradient" => { return input.parse_nested_block(|i| { Self::parse_webkit_gradient_argument(context, i) @@ -266,25 +267,13 @@ impl Parse for Gradient { } }; - let (kind, items) = input.parse_nested_block(|i| { - let shape = match shape { - Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?, - Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?, - }; - let items = GradientItem::parse_comma_separated(context, i)?; - Ok((shape, items)) - })?; - - if items.len() < 2 { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - Ok(Gradient { - items, - repeating, - kind, - compat_mode, - }) + Ok(input.parse_nested_block(|i| { + Ok(match shape { + Shape::Linear => Self::parse_linear(context, i, repeating, compat_mode)?, + Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?, + Shape::Conic => Self::parse_conic(context, i, repeating)?, + }) + })?) } } @@ -404,16 +393,21 @@ impl Gradient { let ident = input.expect_ident_cloned()?; input.expect_comma()?; - let (kind, reverse_stops) = match_ignore_ascii_case! { &ident, + Ok(match_ignore_ascii_case! { &ident, "linear" => { let first = Point::parse(context, input)?; input.expect_comma()?; let second = Point::parse(context, input)?; let direction = LineDirection::from_points(first, second); - let kind = generic::GradientKind::Linear(direction); + let items = Gradient::parse_webkit_gradient_stops(context, input, false)?; - (kind, false) + generic::Gradient::Linear { + direction, + items, + repeating: false, + compat_mode: GradientCompatMode::Modern, + } }, "radial" => { let first_point = Point::parse(context, input)?; @@ -433,16 +427,28 @@ impl Gradient { let rad = Circle::Radius(NonNegative(Length::from_px(radius.value))); let shape = generic::EndingShape::Circle(rad); let position: Position = point.into(); - - let kind = generic::GradientKind::Radial(shape, position); - (kind, reverse_stops) + let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?; + + generic::Gradient::Radial { + shape, + position, + items, + repeating: false, + compat_mode: GradientCompatMode::Modern, + } }, _ => { let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone()); return Err(input.new_custom_error(e)); }, - }; + }) + } + fn parse_webkit_gradient_stops<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + reverse_stops: bool, + ) -> Result<LengthPercentageItemList, ParseError<'i>> { let mut items = input .try(|i| { i.expect_comma()?; @@ -521,46 +527,62 @@ impl Gradient { } }) } + Ok(items.into()) + } - Ok(generic::Gradient { - kind, - items: items.into(), - repeating: false, - compat_mode: GradientCompatMode::Modern, - }) + /// Not used for -webkit-gradient syntax and conic-gradient + fn parse_stops<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<LengthPercentageItemList, ParseError<'i>> { + let items = + generic::GradientItem::parse_comma_separated(context, input, LengthPercentage::parse)?; + if items.len() < 2 { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + Ok(items) } -} -impl GradientKind { /// Parses a linear gradient. /// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword. fn parse_linear<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, - compat_mode: &mut GradientCompatMode, + repeating: bool, + mut compat_mode: GradientCompatMode, ) -> Result<Self, ParseError<'i>> { - let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) - { - input.expect_comma()?; - d - } else { - match *compat_mode { - GradientCompatMode::Modern => { - LineDirection::Vertical(VerticalPositionKeyword::Bottom) - }, - _ => LineDirection::Vertical(VerticalPositionKeyword::Top), - } - }; - Ok(generic::GradientKind::Linear(direction)) + let direction = + if let Ok(d) = input.try(|i| LineDirection::parse(context, i, &mut compat_mode)) { + input.expect_comma()?; + d + } else { + match compat_mode { + GradientCompatMode::Modern => { + LineDirection::Vertical(VerticalPositionKeyword::Bottom) + }, + _ => LineDirection::Vertical(VerticalPositionKeyword::Top), + } + }; + let items = Gradient::parse_stops(context, input)?; + + Ok(Gradient::Linear { + direction, + items, + repeating, + compat_mode, + }) } + + /// Parses a radial gradient. fn parse_radial<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, - compat_mode: &mut GradientCompatMode, + repeating: bool, + compat_mode: GradientCompatMode, ) -> Result<Self, ParseError<'i>> { - let (shape, position) = match *compat_mode { + let (shape, position) = match compat_mode { GradientCompatMode::Modern => { - let shape = input.try(|i| EndingShape::parse(context, i, *compat_mode)); + let shape = input.try(|i| EndingShape::parse(context, i, compat_mode)); let position = input.try(|i| { i.expect_ident_matching("at")?; Position::parse(context, i) @@ -573,7 +595,7 @@ impl GradientKind { if position.is_ok() { i.expect_comma()?; } - EndingShape::parse(context, i, *compat_mode) + EndingShape::parse(context, i, compat_mode) }); (shape, position.ok()) }, @@ -588,7 +610,54 @@ impl GradientKind { }); let position = position.unwrap_or(Position::center()); - Ok(generic::GradientKind::Radial(shape, position)) + + let items = Gradient::parse_stops(context, input)?; + + Ok(Gradient::Radial { + shape, + position, + items, + repeating, + compat_mode, + }) + } + fn parse_conic<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + repeating: bool, + ) -> Result<Self, ParseError<'i>> { + let angle = input.try(|i| { + i.expect_ident_matching("from")?; + // Spec allows unitless zero start angles + // https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle + Angle::parse_with_unitless(context, i) + }); + let position = input.try(|i| { + i.expect_ident_matching("at")?; + Position::parse(context, i) + }); + if angle.is_ok() || position.is_ok() { + input.expect_comma()?; + } + + let angle = angle.unwrap_or(Angle::zero()); + let position = position.unwrap_or(Position::center()); + let items = generic::GradientItem::parse_comma_separated( + context, + input, + AngleOrPercentage::parse_with_unitless, + )?; + + if items.len() < 2 { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(Gradient::Conic { + angle, + position, + items, + repeating, + }) } } @@ -793,10 +862,12 @@ impl ShapeExtent { } } -impl GradientItem { +impl<T> generic::GradientItem<Color, T> { fn parse_comma_separated<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + parse_position: impl for<'i1, 't1> Fn(&ParserContext, &mut Parser<'i1, 't1>) -> Result<T, ParseError<'i1>> + + Copy, ) -> Result<crate::OwnedSlice<Self>, ParseError<'i>> { let mut items = Vec::new(); let mut seen_stop = false; @@ -804,20 +875,20 @@ impl GradientItem { loop { input.parse_until_before(Delimiter::Comma, |input| { if seen_stop { - if let Ok(hint) = input.try(|i| LengthPercentage::parse(context, i)) { + if let Ok(hint) = input.try(|i| parse_position(context, i)) { seen_stop = false; items.push(generic::GradientItem::InterpolationHint(hint)); return Ok(()); } } - let stop = ColorStop::parse(context, input)?; + let stop = generic::ColorStop::parse(context, input, parse_position)?; - if let Ok(multi_position) = input.try(|i| LengthPercentage::parse(context, i)) { + if let Ok(multi_position) = input.try(|i| parse_position(context, i)) { let stop_color = stop.color.clone(); items.push(stop.into_item()); items.push( - ColorStop { + generic::ColorStop { color: stop_color, position: Some(multi_position), } @@ -845,14 +916,18 @@ impl GradientItem { } } -impl Parse for ColorStop { +impl<T> generic::ColorStop<Color, T> { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + parse_position: impl for<'i1, 't1> Fn( + &ParserContext, + &mut Parser<'i1, 't1>, + ) -> Result<T, ParseError<'i1>>, ) -> Result<Self, ParseError<'i>> { - Ok(ColorStop { + Ok(generic::ColorStop { color: Color::parse(context, input)?, - position: input.try(|i| LengthPercentage::parse(context, i)).ok(), + position: input.try(|i| parse_position(context, i)).ok(), }) } } diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index ff24404c09a..0b1bf0b68a7 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -16,7 +16,7 @@ use crate::values::generics::length::{ GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, }; use crate::values::generics::NonNegative; -use crate::values::specified::calc::CalcNode; +use crate::values::specified::calc::{self, CalcNode}; use crate::values::specified::NonNegativeNumber; use crate::values::CSSFloat; use crate::Zero; @@ -28,8 +28,8 @@ use std::ops::{Add, Mul}; use style_traits::values::specified::AllowedNumericType; use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind}; -pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use super::image::{GradientKind, Image}; +pub use super::image::Image; +pub use super::image::{EndingShape as GradientEndingShape, Gradient}; pub use crate::values::specified::calc::CalcLengthPercentage; /// Number of app units per pixel @@ -92,7 +92,16 @@ impl FontRelativeLength { FontRelativeLength::Em(v) | FontRelativeLength::Ex(v) | FontRelativeLength::Ch(v) | - FontRelativeLength::Rem(v) => v == 0.0, + FontRelativeLength::Rem(v) => v == 0., + } + } + + fn is_negative(&self) -> bool { + match *self { + FontRelativeLength::Em(v) | + FontRelativeLength::Ex(v) | + FontRelativeLength::Ch(v) | + FontRelativeLength::Rem(v) => v < 0., } } @@ -266,7 +275,16 @@ impl ViewportPercentageLength { ViewportPercentageLength::Vw(v) | ViewportPercentageLength::Vh(v) | ViewportPercentageLength::Vmin(v) | - ViewportPercentageLength::Vmax(v) => v == 0.0, + ViewportPercentageLength::Vmax(v) => v == 0., + } + } + + fn is_negative(&self) -> bool { + match *self { + ViewportPercentageLength::Vw(v) | + ViewportPercentageLength::Vh(v) | + ViewportPercentageLength::Vmin(v) | + ViewportPercentageLength::Vmax(v) => v < 0., } } @@ -370,6 +388,18 @@ impl AbsoluteLength { } } + fn is_negative(&self) -> bool { + match *self { + AbsoluteLength::Px(v) | + AbsoluteLength::In(v) | + AbsoluteLength::Cm(v) | + AbsoluteLength::Mm(v) | + AbsoluteLength::Q(v) | + AbsoluteLength::Pt(v) | + AbsoluteLength::Pc(v) => v < 0., + } + } + /// Convert this into a pixel value. #[inline] pub fn to_px(&self) -> CSSFloat { @@ -484,6 +514,16 @@ impl Mul<CSSFloat> for NoCalcLength { } impl NoCalcLength { + /// Returns whether the value of this length without unit is less than zero. + pub fn is_negative(&self) -> bool { + match *self { + NoCalcLength::Absolute(v) => v.is_negative(), + NoCalcLength::FontRelative(v) => v.is_negative(), + NoCalcLength::ViewportPercentage(v) => v.is_negative(), + NoCalcLength::ServoCharacterWidth(c) => c.0 < 0, + } + } + /// Parse a given absolute or relative dimension. pub fn parse_dimension( context: &ParserContext, @@ -912,9 +952,10 @@ impl From<Percentage> for LengthPercentage { #[inline] fn from(pc: Percentage) -> Self { if pc.is_calc() { + // FIXME(emilio): Hard-coding the clamping mode is suspect. LengthPercentage::Calc(Box::new(CalcLengthPercentage { - percentage: Some(computed::Percentage(pc.get())), - ..Default::default() + clamping_mode: AllowedNumericType::All, + node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())), })) } else { LengthPercentage::Percentage(computed::Percentage(pc.get())) diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 54401045809..63af3015386 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -55,8 +55,8 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis}; pub use self::font::{FontVariantAlternates, FontWeight}; pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; -pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect}; +pub use self::image::{EndingShape as GradientEndingShape, Gradient}; +pub use self::image::{Image, MozImageRect}; pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth}; pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber}; pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; @@ -80,6 +80,7 @@ pub use self::svg::MozContextProperties; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg_path::SVGPathData; +pub use self::text::TextAlignLast; pub use self::text::TextUnderlinePosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs index 75549dca3be..eac4d91cf8f 100644 --- a/components/style/values/specified/percentage.rs +++ b/components/style/values/specified/percentage.rs @@ -120,9 +120,6 @@ impl Percentage { Token::Function(ref name) => { let function = CalcNode::math_function(name, location)?; let value = CalcNode::parse_percentage(context, input, function)?; - - // TODO(emilio): -moz-image-rect is the only thing that uses - // the clamping mode... I guess we could disallow it... Ok(Percentage { value, calc_clamping_mode: Some(num_context), diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 3f01988e49d..b843de29a41 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -13,6 +13,7 @@ use crate::str::HTML_SPACE_CHARACTERS; use crate::values::computed::LengthPercentage as ComputedLengthPercentage; use crate::values::computed::{Context, Percentage, ToComputedValue}; use crate::values::generics::position::Position as GenericPosition; +use crate::values::generics::position::PositionComponent as GenericPositionComponent; use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto; use crate::values::generics::position::ZIndex as GenericZIndex; use crate::values::specified::{AllowQuirks, Integer, LengthPercentage}; @@ -262,6 +263,18 @@ impl<S: Parse> PositionComponent<S> { } } +impl<S> GenericPositionComponent for PositionComponent<S> { + fn is_center(&self) -> bool { + match *self { + PositionComponent::Center => true, + PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5, + // 50% from any side is still the center. + PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5, + _ => false, + } + } +} + impl<S> PositionComponent<S> { /// `0%` pub fn zero() -> Self { @@ -295,10 +308,8 @@ impl<S: Side> ToComputedValue for PositionComponent<S> { }, PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => { let length = length.to_computed_value(context); - let p = Percentage(1. - length.percentage()); - let l = -length.unclamped_length(); // We represent `<end-side> <length>` as `calc(100% - <length>)`. - ComputedLengthPercentage::new_calc(l, Some(p), AllowedNumericType::All) + ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All) }, PositionComponent::Side(_, Some(ref length)) | PositionComponent::Length(ref length) => length.to_computed_value(context), @@ -350,66 +361,25 @@ impl Side for VerticalPositionKeyword { } } -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -/// Auto-placement algorithm Option -pub enum AutoFlow { - /// The auto-placement algorithm places items by filling each row in turn, - /// adding new rows as necessary. - Row, - /// The auto-placement algorithm places items by filling each column in turn, - /// adding new columns as necessary. - Column, -} - -/// If `dense` is specified, `row` is implied. -fn is_row_dense(autoflow: &AutoFlow, dense: &bool) -> bool { - *autoflow == AutoFlow::Row && *dense -} - -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -/// Controls how the auto-placement algorithm works -/// specifying exactly how auto-placed items get flowed into the grid -pub struct GridAutoFlow { - /// Specifiy how auto-placement algorithm fills each `row` or `column` in turn - #[css(contextual_skip_if = "is_row_dense")] - pub autoflow: AutoFlow, - /// Specify use `dense` packing algorithm or not - #[css(represents_keyword)] - pub dense: bool, -} - -impl GridAutoFlow { - #[inline] - /// Get default `grid-auto-flow` as `row` - pub fn row() -> GridAutoFlow { - GridAutoFlow { - autoflow: AutoFlow::Row, - dense: false, - } +bitflags! { + /// Controls how the auto-placement algorithm works + /// specifying exactly how auto-placed items get flowed into the grid + #[derive( + MallocSizeOf, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem + )] + #[value_info(other_values = "row,column,dense")] + #[repr(C)] + pub struct GridAutoFlow: u8 { + /// 'row' - mutually exclusive with 'column' + const ROW = 1 << 0; + /// 'column' - mutually exclusive with 'row' + const COLUMN = 1 << 1; + /// 'dense' + const DENSE = 1 << 2; } } @@ -419,26 +389,26 @@ impl Parse for GridAutoFlow { _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<GridAutoFlow, ParseError<'i>> { - let mut value = None; - let mut dense = false; + let mut track = None; + let mut dense = GridAutoFlow::empty(); while !input.is_exhausted() { let location = input.current_source_location(); let ident = input.expect_ident()?; let success = match_ignore_ascii_case! { &ident, - "row" if value.is_none() => { - value = Some(AutoFlow::Row); + "row" if track.is_none() => { + track = Some(GridAutoFlow::ROW); true }, - "column" if value.is_none() => { - value = Some(AutoFlow::Column); + "column" if track.is_none() => { + track = Some(GridAutoFlow::COLUMN); true }, - "dense" if !dense => { - dense = true; + "dense" if dense.is_empty() => { + dense = GridAutoFlow::DENSE; true }, - _ => false + _ => false, }; if !success { return Err(location @@ -446,47 +416,37 @@ impl Parse for GridAutoFlow { } } - if value.is_some() || dense { - Ok(GridAutoFlow { - autoflow: value.unwrap_or(AutoFlow::Row), - dense: dense, - }) + if track.is_some() || !dense.is_empty() { + Ok(track.unwrap_or(GridAutoFlow::ROW) | dense) } else { Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } } } -#[cfg(feature = "gecko")] -impl From<u8> for GridAutoFlow { - fn from(bits: u8) -> GridAutoFlow { - use crate::gecko_bindings::structs; - - GridAutoFlow { - autoflow: if bits & structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8 != 0 { - AutoFlow::Row - } else { - AutoFlow::Column - }, - dense: bits & structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8 != 0, +impl ToCss for GridAutoFlow { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + if *self == GridAutoFlow::ROW { + return dest.write_str("row"); } - } -} -#[cfg(feature = "gecko")] -impl From<GridAutoFlow> for u8 { - fn from(v: GridAutoFlow) -> u8 { - use crate::gecko_bindings::structs; + if *self == GridAutoFlow::COLUMN { + return dest.write_str("column"); + } - let mut result: u8 = match v.autoflow { - AutoFlow::Row => structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8, - AutoFlow::Column => structs::NS_STYLE_GRID_AUTO_FLOW_COLUMN as u8, - }; + if *self == GridAutoFlow::ROW | GridAutoFlow::DENSE { + return dest.write_str("dense"); + } - if v.dense { - result |= structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8; + if *self == GridAutoFlow::COLUMN | GridAutoFlow::DENSE { + return dest.write_str("column dense"); } - result + + debug_assert!(false, "Unknown or invalid grid-autoflow value"); + Ok(()) } } @@ -592,7 +552,7 @@ impl TemplateAreas { Ok(TemplateAreas { areas: areas.into(), strings: strings.into(), - width: width, + width, }) } } @@ -642,7 +602,16 @@ impl Parse for TemplateAreasArc { /// A range of rows or columns. Using this instead of std::ops::Range for FFI /// purposes. #[repr(C)] -#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] pub struct UnsignedRange { /// The start of the range. pub start: u32, @@ -650,7 +619,16 @@ pub struct UnsignedRange { pub end: u32, } -#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] #[repr(C)] /// Not associated with any particular grid item, but can be referenced from the /// grid-placement properties. diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index e603ddfda07..cff899fce35 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -21,13 +21,13 @@ use style_traits::{StyleParseErrorKind, ToCss}; pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>; /// <length> | <percentage> | <number> | context-value -pub type SVGLength = generic::SVGLength<LengthPercentage>; +pub type SVGLength = generic::GenericSVGLength<LengthPercentage>; /// A non-negative version of SVGLength. -pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>; +pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>; /// [ <length> | <percentage> | <number> ]# | context-value -pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>; +pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>; /// Whether the `context-value` value is enabled. #[cfg(feature = "gecko")] diff --git a/components/style/values/specified/svg_path.rs b/components/style/values/specified/svg_path.rs index 288f396b73c..9f1c5c5e32c 100644 --- a/components/style/values/specified/svg_path.rs +++ b/components/style/values/specified/svg_path.rs @@ -159,6 +159,8 @@ impl ComputeSquaredDistance for SVGPathData { Serialize, SpecifiedValueInfo, ToAnimatedZero, + ToComputedValue, + ToResolvedValue, ToShmem, )] #[allow(missing_docs)] @@ -488,6 +490,8 @@ impl ToCss for PathCommand { Serialize, SpecifiedValueInfo, ToAnimatedZero, + ToComputedValue, + ToResolvedValue, ToShmem, )] #[repr(u8)] @@ -518,7 +522,9 @@ impl IsAbsolute { Serialize, SpecifiedValueInfo, ToAnimatedZero, + ToComputedValue, ToCss, + ToResolvedValue, ToShmem, )] #[repr(C)] @@ -534,7 +540,17 @@ impl CoordPair { /// The EllipticalArc flag type. #[derive( - Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, SpecifiedValueInfo, ToShmem, + Clone, + Copy, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, )] #[repr(C)] pub struct ArcFlag(bool); diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 0228d5123ce..703e51a5248 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -122,7 +122,18 @@ impl ToComputedValue for LineHeight { } /// A generic value for the `text-overflow` property. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[derive( + Clone, + Debug, + Eq, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] #[repr(C, u8)] pub enum TextOverflowSide { /// Clip inline content. @@ -517,6 +528,35 @@ impl ToCss for TextTransformOther { } } +/// Specified and computed value of text-align-last. +#[derive( + Clone, + Copy, + Debug, + Eq, + FromPrimitive, + Hash, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[allow(missing_docs)] +#[repr(u8)] +pub enum TextAlignLast { + Auto, + Start, + End, + Left, + Right, + Center, + Justify, +} + /// Specified value of text-align keyword value. #[derive( Clone, @@ -535,14 +575,18 @@ impl ToCss for TextTransformOther { ToShmem, )] #[allow(missing_docs)] +#[repr(u8)] pub enum TextAlignKeyword { Start, - End, Left, Right, Center, #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] Justify, + #[css(skip)] + #[cfg(feature = "gecko")] + Char, + End, #[cfg(feature = "gecko")] MozCenter, #[cfg(feature = "gecko")] @@ -555,9 +599,6 @@ pub enum TextAlignKeyword { ServoLeft, #[cfg(feature = "servo-layout-2013")] ServoRight, - #[css(skip)] - #[cfg(feature = "gecko")] - Char, } /// Specified value of text-align property. @@ -579,14 +620,6 @@ pub enum TextAlign { MozCenterOrInherit, } -impl TextAlign { - /// Convert an enumerated value coming from Gecko to a `TextAlign`. - #[cfg(feature = "gecko")] - pub fn from_gecko_keyword(kw: u32) -> Self { - TextAlign::Keyword(TextAlignKeyword::from_gecko_keyword(kw)) - } -} - impl ToComputedValue for TextAlign { type ComputedValue = TextAlignKeyword; @@ -665,7 +698,19 @@ pub enum TextEmphasisStyle { } /// Fill mode for the text-emphasis-style property -#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[derive( + Clone, + Copy, + Debug, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToCss, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] #[repr(u8)] pub enum TextEmphasisFillMode { /// `filled` @@ -684,7 +729,18 @@ impl TextEmphasisFillMode { /// Shape keyword for the text-emphasis-style property #[derive( - Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToCss, + ToComputedValue, + ToResolvedValue, + ToShmem, )] #[repr(u8)] pub enum TextEmphasisShapeKeyword { @@ -1010,7 +1066,7 @@ pub enum OverflowWrap { Anywhere, } -/// Implements text-decoration-skip-ink which takes the keywords auto | none +/// Implements text-decoration-skip-ink which takes the keywords auto | none | all /// /// https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property #[repr(u8)] @@ -1033,6 +1089,7 @@ pub enum OverflowWrap { pub enum TextDecorationSkipInk { Auto, None, + All, } /// Implements type for `text-decoration-thickness` property diff --git a/components/style/values/specified/ui.rs b/components/style/values/specified/ui.rs index 46272b2ae85..b94267162c9 100644 --- a/components/style/values/specified/ui.rs +++ b/components/style/values/specified/ui.rs @@ -14,10 +14,10 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; /// A specified value for the `cursor` property. -pub type Cursor = generics::Cursor<CursorImage>; +pub type Cursor = generics::GenericCursor<CursorImage>; /// A specified value for item of `image cursors`. -pub type CursorImage = generics::CursorImage<SpecifiedImageUrl, Number>; +pub type CursorImage = generics::GenericCursorImage<SpecifiedImageUrl, Number>; impl Parse for Cursor { /// cursor: [<url> [<number> <number>]?]# [auto | default | ...] @@ -34,7 +34,7 @@ impl Parse for Cursor { input.expect_comma()?; } Ok(Self { - images: images.into_boxed_slice(), + images: images.into(), keyword: CursorKind::parse(input)?, }) } @@ -45,12 +45,24 @@ impl Parse for CursorImage { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { + use crate::Zero; + + let url = SpecifiedImageUrl::parse(context, input)?; + let mut has_hotspot = false; + let mut hotspot_x = Number::zero(); + let mut hotspot_y = Number::zero(); + + if let Ok(x) = input.try(|input| Number::parse(context, input)) { + has_hotspot = true; + hotspot_x = x; + hotspot_y = Number::parse(context, input)?; + } + Ok(Self { - url: SpecifiedImageUrl::parse(context, input)?, - hotspot: match input.try(|input| Number::parse(context, input)) { - Ok(number) => Some((number, Number::parse(context, input)?)), - Err(_) => None, - }, + url, + has_hotspot, + hotspot_x, + hotspot_y, }) } } diff --git a/components/style_derive/animate.rs b/components/style_derive/animate.rs index 190568514fc..59b19214971 100644 --- a/components/style_derive/animate.rs +++ b/components/style_derive/animate.rs @@ -6,12 +6,11 @@ use darling::util::PathList; use derive_common::cg; use proc_macro2::TokenStream; use quote::TokenStreamExt; -use syn::{DeriveInput, Path, WhereClause}; +use syn::{DeriveInput, WhereClause}; use synstructure::{Structure, VariantInfo}; pub fn derive(mut input: DeriveInput) -> TokenStream { let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input); - let input_attrs = cg::parse_input_attrs::<AnimateInputAttrs>(&input); let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); let mut where_clause = input.generics.where_clause.take(); @@ -44,11 +43,6 @@ pub fn derive(mut input: DeriveInput) -> TokenStream { let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let fallback = match input_attrs.fallback { - Some(fallback) => quote! { #fallback(self, other, procedure) }, - None => quote! { Err(()) }, - }; - quote! { impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause { #[allow(unused_variables, unused_imports)] @@ -59,7 +53,7 @@ pub fn derive(mut input: DeriveInput) -> TokenStream { procedure: crate::values::animated::Procedure, ) -> Result<Self, ()> { if std::mem::discriminant(self) != std::mem::discriminant(other) { - return #fallback; + return Err(()); } match (self, other) { #match_body @@ -118,12 +112,6 @@ fn derive_variant_arm( } } -#[darling(attributes(animate), default)] -#[derive(Default, FromDeriveInput)] -struct AnimateInputAttrs { - fallback: Option<Path>, -} - #[darling(attributes(animation), default)] #[derive(Default, FromDeriveInput)] pub struct AnimationInputAttrs { diff --git a/components/style_derive/compute_squared_distance.rs b/components/style_derive/compute_squared_distance.rs index 5e130e75b06..b4e67f6c984 100644 --- a/components/style_derive/compute_squared_distance.rs +++ b/components/style_derive/compute_squared_distance.rs @@ -6,12 +6,11 @@ use crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantA use derive_common::cg; use proc_macro2::TokenStream; use quote::TokenStreamExt; -use syn::{DeriveInput, Path, WhereClause}; +use syn::{DeriveInput, WhereClause}; use synstructure; pub fn derive(mut input: DeriveInput) -> TokenStream { let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input); - let input_attrs = cg::parse_input_attrs::<DistanceInputAttrs>(&input); let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); let mut where_clause = input.generics.where_clause.take(); for param in input.generics.type_params() { @@ -43,11 +42,6 @@ pub fn derive(mut input: DeriveInput) -> TokenStream { match_body.append_all(quote! { _ => unsafe { debug_unreachable!() } }); } - let fallback = match input_attrs.fallback { - Some(fallback) => quote! { #fallback(self, other) }, - None => quote! { Err(()) }, - }; - let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); @@ -60,7 +54,7 @@ pub fn derive(mut input: DeriveInput) -> TokenStream { other: &Self, ) -> Result<crate::values::distance::SquaredDistance, ()> { if std::mem::discriminant(self) != std::mem::discriminant(other) { - return #fallback; + return Err(()); } match (self, other) { #match_body @@ -125,12 +119,6 @@ fn derive_variant_arm( } #[darling(attributes(distance), default)] -#[derive(Default, FromDeriveInput)] -struct DistanceInputAttrs { - fallback: Option<Path>, -} - -#[darling(attributes(distance), default)] #[derive(Default, FromField)] struct DistanceFieldAttrs { field_bound: bool, diff --git a/components/style_derive/to_animated_value.rs b/components/style_derive/to_animated_value.rs index 51a73cbf4e1..45282f0c448 100644 --- a/components/style_derive/to_animated_value.rs +++ b/components/style_derive/to_animated_value.rs @@ -11,33 +11,25 @@ pub fn derive(input: DeriveInput) -> TokenStream { let trait_impl = |from_body, to_body| { quote! { #[inline] - fn from_animated_value(animated: Self::AnimatedValue) -> Self { - match animated { - #from_body - } + fn from_animated_value(from: Self::AnimatedValue) -> Self { + #from_body } #[inline] fn to_animated_value(self) -> Self::AnimatedValue { - match self { - #to_body - } + #to_body } } }; - // TODO(emilio): Consider optimizing away non-generic cases as well? - let non_generic_implementation = || None; - to_computed_value::derive_to_value( input, parse_quote!(crate::values::animated::ToAnimatedValue), parse_quote!(AnimatedValue), BindStyle::Move, - |_| false, + |_| Default::default(), |binding| quote!(crate::values::animated::ToAnimatedValue::from_animated_value(#binding)), |binding| quote!(crate::values::animated::ToAnimatedValue::to_animated_value(#binding)), trait_impl, - non_generic_implementation, ) } diff --git a/components/style_derive/to_computed_value.rs b/components/style_derive/to_computed_value.rs index ed6e07a2f5a..fe6bddb7ed2 100644 --- a/components/style_derive/to_computed_value.rs +++ b/components/style_derive/to_computed_value.rs @@ -13,7 +13,7 @@ pub fn derive_to_value( output_type_name: Ident, bind_style: BindStyle, // Returns whether to apply the field bound for a given item. - mut field_bound: impl FnMut(&BindingInfo) -> bool, + mut binding_attrs: impl FnMut(&BindingInfo) -> ToValueAttrs, // Returns a token stream of the form: trait_path::from_foo(#binding) mut call_from: impl FnMut(&BindingInfo) -> TokenStream, mut call_to: impl FnMut(&BindingInfo) -> TokenStream, @@ -26,25 +26,9 @@ pub fn derive_to_value( // #second_arg // } mut trait_impl: impl FnMut(TokenStream, TokenStream) -> TokenStream, - // if this is provided, the derive for non-generic types will be simplified - // to this token stream, which should be the body of the impl block. - non_generic_implementation: impl FnOnce() -> Option<TokenStream>, ) -> TokenStream { let name = &input.ident; - if input.generics.type_params().next().is_none() { - if let Some(non_generic_implementation) = non_generic_implementation() { - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - return quote! { - impl #impl_generics #trait_path for #name #ty_generics - #where_clause - { - #non_generic_implementation - } - }; - } - } - let mut where_clause = input.generics.where_clause.take(); cg::propagate_clauses_to_output_type( &mut where_clause, @@ -52,33 +36,107 @@ pub fn derive_to_value( &trait_path, &output_type_name, ); - let (to_body, from_body) = { - let params = input.generics.type_params().collect::<Vec<_>>(); - for param in ¶ms { - cg::add_predicate(&mut where_clause, parse_quote!(#param: #trait_path)); - } - let to_body = cg::fmap_match(&input, bind_style, |binding| { - if field_bound(&binding) { - let ty = &binding.ast().ty; + let moves = match bind_style { + BindStyle::Move | BindStyle::MoveMut => true, + BindStyle::Ref | BindStyle::RefMut => false, + }; - let output_type = cg::map_type_params( - ty, - ¶ms, - &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name), - ); + let params = input.generics.type_params().collect::<Vec<_>>(); + for param in ¶ms { + cg::add_predicate(&mut where_clause, parse_quote!(#param: #trait_path)); + } - cg::add_predicate( - &mut where_clause, - parse_quote!( - #ty: #trait_path<#output_type_name = #output_type> - ), + let mut add_field_bound = |binding: &BindingInfo| { + let ty = &binding.ast().ty; + + let output_type = cg::map_type_params( + ty, + ¶ms, + &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name), + ); + + cg::add_predicate( + &mut where_clause, + parse_quote!( + #ty: #trait_path<#output_type_name = #output_type> + ), + ); + }; + + let (to_body, from_body) = if params.is_empty() { + let mut s = synstructure::Structure::new(&input); + s.variants_mut().iter_mut().for_each(|v| { + v.bind_with(|_| bind_style); + }); + + for variant in s.variants() { + for binding in variant.bindings() { + let attrs = binding_attrs(&binding); + assert!( + !attrs.field_bound, + "It is default on a non-generic implementation", ); + if !attrs.no_field_bound { + // Add field bounds to all bindings except the manually + // excluded. This ensures the correctness of the clone() / + // move based implementation. + add_field_bound(binding); + } + } + } + + let to_body = if moves { + quote! { self } + } else { + quote! { std::clone::Clone::clone(self) } + }; + + let from_body = if moves { + quote! { from } + } else { + quote! { std::clone::Clone::clone(from) } + }; + + (to_body, from_body) + } else { + let to_body = cg::fmap_match(&input, bind_style, |binding| { + let attrs = binding_attrs(&binding); + assert!( + !attrs.no_field_bound, + "It doesn't make sense on a generic implementation" + ); + if attrs.field_bound { + add_field_bound(&binding); } call_to(&binding) }); + let from_body = cg::fmap_match(&input, bind_style, |binding| call_from(&binding)); + let self_ = if moves { + quote! { self } + } else { + quote! { *self } + }; + let from_ = if moves { + quote! { from } + } else { + quote! { *from } + }; + + let to_body = quote! { + match #self_ { + #to_body + } + }; + + let from_body = quote! { + match #from_ { + #from_body + } + }; + (to_body, from_body) }; @@ -101,53 +159,45 @@ pub fn derive(input: DeriveInput) -> TokenStream { let trait_impl = |from_body, to_body| { quote! { #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - #from_body - } + fn from_computed_value(from: &Self::ComputedValue) -> Self { + #from_body } #[allow(unused_variables)] #[inline] fn to_computed_value(&self, context: &crate::values::computed::Context) -> Self::ComputedValue { - match *self { - #to_body - } + #to_body } } }; - let non_generic_implementation = || { - Some(quote! { - type ComputedValue = Self; - - #[inline] - fn to_computed_value(&self, _: &crate::values::computed::Context) -> Self::ComputedValue { - std::clone::Clone::clone(self) - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - std::clone::Clone::clone(computed) - } - }) - }; - derive_to_value( input, parse_quote!(crate::values::computed::ToComputedValue), parse_quote!(ComputedValue), BindStyle::Ref, - |binding| cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast()).field_bound, + |binding| { + let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast()); + ToValueAttrs { + field_bound: attrs.field_bound, + no_field_bound: attrs.no_field_bound, + } + }, |binding| quote!(crate::values::computed::ToComputedValue::from_computed_value(#binding)), |binding| quote!(crate::values::computed::ToComputedValue::to_computed_value(#binding, context)), trait_impl, - non_generic_implementation, ) } +#[derive(Default)] +pub struct ToValueAttrs { + pub field_bound: bool, + pub no_field_bound: bool, +} + #[darling(attributes(compute), default)] #[derive(Default, FromField)] struct ComputedValueAttrs { field_bound: bool, + no_field_bound: bool, } diff --git a/components/style_derive/to_resolved_value.rs b/components/style_derive/to_resolved_value.rs index 040cda954a8..f7ba2645e89 100644 --- a/components/style_derive/to_resolved_value.rs +++ b/components/style_derive/to_resolved_value.rs @@ -12,10 +12,8 @@ pub fn derive(input: DeriveInput) -> TokenStream { let trait_impl = |from_body, to_body| { quote! { #[inline] - fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { - match resolved { - #from_body - } + fn from_resolved_value(from: Self::ResolvedValue) -> Self { + #from_body } #[inline] @@ -23,42 +21,26 @@ pub fn derive(input: DeriveInput) -> TokenStream { self, context: &crate::values::resolved::Context, ) -> Self::ResolvedValue { - match self { - #to_body - } + #to_body } } }; - let non_generic_implementation = || { - Some(quote! { - type ResolvedValue = Self; - - #[inline] - fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { - resolved - } - - #[inline] - fn to_resolved_value( - self, - context: &crate::values::resolved::Context, - ) -> Self { - self - } - }) - }; - to_computed_value::derive_to_value( input, parse_quote!(crate::values::resolved::ToResolvedValue), parse_quote!(ResolvedValue), BindStyle::Move, - |binding| cg::parse_field_attrs::<ResolvedValueAttrs>(&binding.ast()).field_bound, + |binding| { + let attrs = cg::parse_field_attrs::<ResolvedValueAttrs>(&binding.ast()); + to_computed_value::ToValueAttrs { + field_bound: attrs.field_bound, + no_field_bound: attrs.no_field_bound, + } + }, |binding| quote!(crate::values::resolved::ToResolvedValue::from_resolved_value(#binding)), |binding| quote!(crate::values::resolved::ToResolvedValue::to_resolved_value(#binding, context)), trait_impl, - non_generic_implementation, ) } @@ -66,4 +48,5 @@ pub fn derive(input: DeriveInput) -> TokenStream { #[derive(Default, FromField)] struct ResolvedValueAttrs { field_bound: bool, + no_field_bound: bool, } diff --git a/components/style_traits/arc_slice.rs b/components/style_traits/arc_slice.rs index f5d0c56e7fc..8d55beff3c5 100644 --- a/components/style_traits/arc_slice.rs +++ b/components/style_traits/arc_slice.rs @@ -26,7 +26,7 @@ const ARC_SLICE_CANARY: u64 = 0xf3f3f3f3f3f3f3f3; /// cbindgen:derive-eq=false /// cbindgen:derive-neq=false #[repr(C)] -#[derive(Clone, Debug, Eq, PartialEq, ToShmem)] +#[derive(Debug, Eq, PartialEq, ToShmem)] pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u64, T>); impl<T> Deref for ArcSlice<T> { @@ -39,6 +39,12 @@ impl<T> Deref for ArcSlice<T> { } } +impl<T> Clone for ArcSlice<T> { + fn clone(&self) -> Self { + ArcSlice(self.0.clone()) + } +} + lazy_static! { // ThinArc doesn't support alignments greater than align_of::<u64>. static ref EMPTY_ARC_SLICE: ArcSlice<u64> = { diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs index 516e7b6efba..be2d275b654 100644 --- a/components/style_traits/lib.rs +++ b/components/style_traits/lib.rs @@ -137,6 +137,8 @@ pub enum StyleParseErrorKind<'i> { UnexpectedNamespaceRule, /// @import must be before any rule but @charset UnexpectedImportRule, + /// @import rules are disallowed in the parser. + DisallowedImportRule, /// Unexpected @charset rule encountered. UnexpectedCharsetRule, /// Unsupported @ rule @@ -149,7 +151,6 @@ pub enum StyleParseErrorKind<'i> { ValueError(ValueParseErrorKind<'i>), /// An error was encountered while parsing a selector SelectorError(SelectorParseErrorKind<'i>), - /// The property declaration was for an unknown property. UnknownProperty(CowRcStr<'i>), /// The property declaration was for a disabled experimental property. |