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

style: Sync changes from mozilla-central.

See individual commits for details.

https://bugzilla.mozilla.org/show_bug.cgi?id=1630676
This commit is contained in:
bors-servo 2020-04-16 16:35:43 -04:00 committed by GitHub
commit 2829945963
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
118 changed files with 3141 additions and 3196 deletions

View file

@ -60,10 +60,10 @@ use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect};
use style::properties::{style_structs, ComputedValues}; use style::properties::{style_structs, ComputedValues};
use style::servo::restyle_damage::ServoRestyleDamage; use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::effects::SimpleShadow; 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::computed::{ClipRectOrAuto, Gradient, LengthOrAuto};
use style::values::generics::background::BackgroundSize; 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::specified::ui::CursorKind;
use style::values::RGBA; use style::values::RGBA;
use style_traits::ToCss; use style_traits::ToCss;
@ -732,12 +732,8 @@ impl Fragment {
// http://www.w3.org/TR/CSS21/colors.html#background // http://www.w3.org/TR/CSS21/colors.html#background
let background = style.get_background(); let background = style.get_background();
for (i, background_image) in background.background_image.0.iter().enumerate().rev() { 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 { match *background_image {
Image::None => {},
Image::Gradient(ref gradient) => { Image::Gradient(ref gradient) => {
self.build_display_list_for_background_gradient( self.build_display_list_for_background_gradient(
state, state,
@ -975,15 +971,15 @@ impl Fragment {
display_list_section, display_list_section,
); );
let display_item = match gradient.kind { let display_item = match gradient {
GradientKind::Linear(angle_or_corner) => { Gradient::Linear {
let (gradient, stops) = gradient::linear( ref direction,
style, ref items,
placement.tile_size, ref repeating,
&gradient.items[..], compat_mode: _,
angle_or_corner, } => {
gradient.repeating, let (gradient, stops) =
); gradient::linear(style, placement.tile_size, items, *direction, *repeating);
let item = webrender_api::GradientDisplayItem { let item = webrender_api::GradientDisplayItem {
gradient, gradient,
bounds: placement.bounds.to_f32_px(), bounds: placement.bounds.to_f32_px(),
@ -993,14 +989,20 @@ impl Fragment {
}; };
DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops)) 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( let (gradient, stops) = gradient::radial(
style, style,
placement.tile_size, placement.tile_size,
&gradient.items[..], items,
shape, shape,
center, position,
gradient.repeating, *repeating,
); );
let item = webrender_api::RadialGradientDisplayItem { let item = webrender_api::RadialGradientDisplayItem {
gradient, gradient,
@ -1011,6 +1013,7 @@ impl Fragment {
}; };
DisplayItem::RadialGradient(CommonDisplayItem::with_data(base, item, stops)) DisplayItem::RadialGradient(CommonDisplayItem::with_data(base, item, stops))
}, },
Gradient::Conic { .. } => unimplemented!(),
}; };
state.add_display_item(display_item); state.add_display_item(display_item);
}); });
@ -1122,22 +1125,20 @@ impl Fragment {
let border_radius = border::radii(bounds, border_style_struct); let border_radius = border::radii(bounds, border_style_struct);
let border_widths = border.to_physical(style.writing_mode); let border_widths = border.to_physical(style.writing_mode);
if let ImageLayer::Image(ref image) = border_style_struct.border_image_source {
if self if self
.build_display_list_for_border_image( .build_display_list_for_border_image(
state, state,
style, style,
base.clone(), base.clone(),
bounds, bounds,
image, &border_style_struct.border_image_source,
border_widths, border_widths,
) )
.is_some() .is_some()
{ {
return; return;
} }
// Fallback to rendering a solid border.
}
if border_widths == SideOffsets2D::zero() { if border_widths == SideOffsets2D::zero() {
return; return;
} }
@ -1224,30 +1225,37 @@ impl Fragment {
height = image.height; height = image.height;
NinePatchBorderSource::Image(image.key?) NinePatchBorderSource::Image(image.key?)
}, },
Image::Gradient(ref gradient) => match gradient.kind { Image::Gradient(ref gradient) => match **gradient {
GradientKind::Linear(angle_or_corner) => { Gradient::Linear {
let (wr_gradient, linear_stops) = gradient::linear( ref direction,
style, ref items,
border_image_area, ref repeating,
&gradient.items[..], compat_mode: _,
angle_or_corner, } => {
gradient.repeating, let (wr_gradient, linear_stops) =
); gradient::linear(style, border_image_area, items, *direction, *repeating);
stops = linear_stops; stops = linear_stops;
NinePatchBorderSource::Gradient(wr_gradient) 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( let (wr_gradient, radial_stops) = gradient::radial(
style, style,
border_image_area, border_image_area,
&gradient.items[..], items,
shape, shape,
center, position,
gradient.repeating, *repeating,
); );
stops = radial_stops; stops = radial_stops;
NinePatchBorderSource::RadialGradient(wr_gradient) NinePatchBorderSource::RadialGradient(wr_gradient)
}, },
Gradient::Conic { .. } => unimplemented!(),
}, },
_ => return None, _ => return None,
}; };

View file

@ -7,8 +7,8 @@ use app_units::Au;
use euclid::default::{Point2D, Size2D, Vector2D}; use euclid::default::{Point2D, Size2D, Vector2D};
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::image::{EndingShape, LineDirection}; use style::values::computed::image::{EndingShape, LineDirection};
use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position}; use style::values::computed::{Angle, Color, LengthPercentage, Percentage, Position};
use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent}; use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent};
use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient}; use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient};
/// A helper data structure for gradients. /// A helper data structure for gradients.
@ -78,7 +78,7 @@ fn ellipse_size_keyword(
fn convert_gradient_stops( fn convert_gradient_stops(
style: &ComputedValues, style: &ComputedValues,
gradient_items: &[GradientItem], gradient_items: &[GradientItem<Color, LengthPercentage>],
total_length: Au, total_length: Au,
) -> GradientBuilder { ) -> GradientBuilder {
// Determine the position of each stop per CSS-IMAGES § 3.4. // 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( pub fn linear(
style: &ComputedValues, style: &ComputedValues,
size: Size2D<Au>, size: Size2D<Au>,
stops: &[GradientItem], stops: &[GradientItem<Color, LengthPercentage>],
direction: LineDirection, direction: LineDirection,
repeating: bool, repeating: bool,
) -> (Gradient, Vec<GradientStop>) { ) -> (Gradient, Vec<GradientStop>) {
@ -303,7 +303,7 @@ pub fn linear(
pub fn radial( pub fn radial(
style: &ComputedValues, style: &ComputedValues,
size: Size2D<Au>, size: Size2D<Au>,
stops: &[GradientItem], stops: &[GradientItem<Color, LengthPercentage>],
shape: &EndingShape, shape: &EndingShape,
center: &Position, center: &Position,
repeating: bool, repeating: bool,

View file

@ -4,9 +4,8 @@
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::image::{EndingShape, Gradient, LineDirection}; use style::values::computed::image::{EndingShape, Gradient, LineDirection};
use style::values::computed::{GradientItem, Length, Position}; use style::values::computed::{Color, Length, LengthPercentage, Position};
use style::values::generics::image::GenericGradientKind as Kind; use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent};
use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent};
use webrender_api::{self as wr, units}; use webrender_api::{self as wr, units};
pub(super) fn build( pub(super) fn build(
@ -15,36 +14,51 @@ pub(super) fn build(
layer: &super::background::BackgroundLayer, layer: &super::background::BackgroundLayer,
builder: &mut super::DisplayListBuilder, builder: &mut super::DisplayListBuilder,
) { ) {
let extend_mode = if gradient.repeating { match gradient {
Gradient::Linear {
ref items,
ref direction,
ref repeating,
compat_mode: _,
} => build_linear(
style,
items,
direction,
if *repeating {
wr::ExtendMode::Repeat wr::ExtendMode::Repeat
} else { } else {
wr::ExtendMode::Clamp wr::ExtendMode::Clamp
}; },
match &gradient.kind {
Kind::Linear(line_direction) => build_linear(
style,
&gradient.items,
line_direction,
extend_mode,
&layer, &layer,
builder, builder,
), ),
Kind::Radial(ending_shape, center) => build_radial( Gradient::Radial {
ref shape,
ref position,
ref items,
ref repeating,
compat_mode: _,
} => build_radial(
style, style,
&gradient.items, items,
ending_shape, shape,
center, position,
extend_mode, if *repeating {
wr::ExtendMode::Repeat
} else {
wr::ExtendMode::Clamp
},
&layer, &layer,
builder, builder,
), ),
Gradient::Conic { .. } => unimplemented!(),
} }
} }
/// https://drafts.csswg.org/css-images-3/#linear-gradients /// https://drafts.csswg.org/css-images-3/#linear-gradients
pub(super) fn build_linear( pub(super) fn build_linear(
style: &ComputedValues, style: &ComputedValues,
items: &[GradientItem], items: &[GradientItem<Color, LengthPercentage>],
line_direction: &LineDirection, line_direction: &LineDirection,
extend_mode: wr::ExtendMode, extend_mode: wr::ExtendMode,
layer: &super::background::BackgroundLayer, layer: &super::background::BackgroundLayer,
@ -144,7 +158,7 @@ pub(super) fn build_linear(
/// https://drafts.csswg.org/css-images-3/#radial-gradients /// https://drafts.csswg.org/css-images-3/#radial-gradients
pub(super) fn build_radial( pub(super) fn build_radial(
style: &ComputedValues, style: &ComputedValues,
items: &[GradientItem], items: &[GradientItem<Color, LengthPercentage>],
shape: &EndingShape, shape: &EndingShape,
center: &Position, center: &Position,
extend_mode: wr::ExtendMode, extend_mode: wr::ExtendMode,
@ -244,7 +258,7 @@ pub(super) fn build_radial(
/// https://drafts.csswg.org/css-images-4/#color-stop-fixup /// https://drafts.csswg.org/css-images-4/#color-stop-fixup
fn fixup_stops( fn fixup_stops(
style: &ComputedValues, style: &ComputedValues,
items: &[GradientItem], items: &[GradientItem<Color, LengthPercentage>],
gradient_line_length: Length, gradient_line_length: Length,
) -> Vec<wr::GradientStop> { ) -> Vec<wr::GradientStop> {
// Remove color transititon hints, which are not supported yet. // Remove color transititon hints, which are not supported yet.

View file

@ -333,7 +333,7 @@ impl<'a> BuilderForBoxFragment<'a> {
} }
fn build_background(&mut self, builder: &mut DisplayListBuilder) { 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 b = self.fragment.style.get_background();
let background_color = self.fragment.style.resolve_color(b.background_color); let background_color = self.fragment.style.resolve_color(b.background_color);
if background_color.alpha > 0 { if background_color.alpha > 0 {
@ -345,23 +345,21 @@ impl<'a> BuilderForBoxFragment<'a> {
builder.wr.push_rect(&common, rgba(background_color)) builder.wr.push_rect(&common, rgba(background_color))
} }
// Reverse because the property is top layer first, we want to paint bottom layer first. // 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() { for (index, image) in b.background_image.0.iter().enumerate().rev() {
match layer { match image {
ImageLayer::None => {}, Image::None => {},
ImageLayer::Image(image) => match image { Image::Gradient(ref gradient) => {
Image::Gradient(gradient) => {
let intrinsic = IntrinsicSizes { let intrinsic = IntrinsicSizes {
width: None, width: None,
height: None, height: None,
ratio: None, ratio: None,
}; };
if let Some(layer) = if let Some(layer) = &background::layout_layer(self, builder, index, intrinsic)
&background::layout_layer(self, builder, index, intrinsic)
{ {
gradient::build(&self.fragment.style, gradient, layer, builder) gradient::build(&self.fragment.style, &gradient, layer, builder)
} }
}, },
Image::Url(image_url) => { Image::Url(ref image_url) => {
// FIXME: images wont always have in intrinsic width or height // FIXME: images wont always have in intrinsic width or height
// when support for SVG is added. // when support for SVG is added.
// Or a WebRender `ImageKey`, for that matter. // Or a WebRender `ImageKey`, for that matter.
@ -393,9 +391,7 @@ impl<'a> BuilderForBoxFragment<'a> {
ratio: Some(width as f32 / height as f32), ratio: Some(width as f32 / height as f32),
}; };
if let Some(layer) = if let Some(layer) = background::layout_layer(self, builder, index, intrinsic) {
background::layout_layer(self, builder, index, intrinsic)
{
let image_rendering = let image_rendering =
image_rendering(self.fragment.style.clone_image_rendering()); image_rendering(self.fragment.style.clone_image_rendering());
if layer.repeat { if layer.repeat {
@ -422,8 +418,7 @@ impl<'a> BuilderForBoxFragment<'a> {
} }
}, },
// Gecko-only value, represented as a (boxed) empty enum on non-Gecko. // Gecko-only value, represented as a (boxed) empty enum on non-Gecko.
Image::Rect(rect) => match **rect {}, Image::Rect(ref rect) => match **rect {},
},
} }
} }
} }

View file

@ -914,10 +914,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
None None
} }
@ -1441,11 +1437,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::exported_part called");
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::imported_part called"); debug!("ServoThreadSafeLayoutElement::imported_part called");
None None

View file

@ -922,10 +922,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
None None
} }
@ -1447,11 +1443,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::exported_part called");
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::imported_part called"); debug!("ServoThreadSafeLayoutElement::imported_part called");
None None

View file

@ -17,7 +17,9 @@ use crate::stylesheet_loader::StylesheetLoader;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::Locked; use style::shared_lock::Locked;
use style::stylesheets::{CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError}; use style::stylesheets::{
AllowImportRules, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError,
};
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe_no_jsmanaged_fields!(RulesSource); unsafe_no_jsmanaged_fields!(RulesSource);
@ -116,6 +118,7 @@ impl CSSRuleList {
index, index,
nested, nested,
Some(&loader), Some(&loader),
AllowImportRules::Yes,
) )
})?; })?;

View file

@ -682,10 +682,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
hints.push(from_declaration( hints.push(from_declaration(
shared_lock, shared_lock,
PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue( PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue(
vec![specified::ImageLayer::Image(specified::Image::for_cascade( vec![specified::Image::for_cascade(url.into())].into(),
url.into(),
))]
.into(),
)), )),
)); ));
} }
@ -3164,10 +3161,6 @@ impl<'a> SelectorsElement for DomRoot<Element> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
None None
} }

View file

@ -26,7 +26,7 @@ use servo_arc::Arc;
use std::cell::Cell; use std::cell::Cell;
use style::media_queries::MediaList; use style::media_queries::MediaList;
use style::parser::ParserContext as CssParserContext; use style::parser::ParserContext as CssParserContext;
use style::stylesheets::{CssRuleType, Origin, Stylesheet}; use style::stylesheets::{AllowImportRules, CssRuleType, Origin, Stylesheet};
use style_traits::ParsingMode; use style_traits::ParsingMode;
#[dom_struct] #[dom_struct]
@ -119,6 +119,7 @@ impl HTMLStyleElement {
css_error_reporter, css_error_reporter,
doc.quirks_mode(), doc.quirks_mode(),
self.line_number as u32, self.line_number as u32,
AllowImportRules::Yes,
); );
let sheet = Arc::new(sheet); let sheet = Arc::new(sheet);

View file

@ -676,9 +676,11 @@ where
None => return false, None => return false,
}; };
let current_host = context.shared.current_host;
if current_host != Some(host.opaque()) {
loop { loop {
let outer_host = host.containing_shadow_host(); let outer_host = host.containing_shadow_host();
if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host { if outer_host.as_ref().map(|h| h.opaque()) == current_host {
break; break;
} }
let outer_host = match outer_host { let outer_host = match outer_host {
@ -690,6 +692,7 @@ where
hosts.push(host); hosts.push(host);
host = outer_host; host = outer_host;
} }
}
// Translate the part into the right scope. // Translate the part into the right scope.
parts.iter().all(|part| { parts.iter().all(|part| {

View file

@ -2013,27 +2013,48 @@ where
input.skip_whitespace(); input.skip_whitespace();
let mut empty = true; let mut empty = true;
if !parse_type_selector(parser, input, builder)? { 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 {
empty = false; empty = false;
} }
let mut state = SelectorParsingState::empty(); let mut state = SelectorParsingState::empty();
loop { loop {
let parse_result = match parse_one_simple_selector(parser, input, state)? { let result = match parse_one_simple_selector(parser, input, state)? {
None => break, None => break,
Some(result) => result, 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; empty = false;
match parse_result { match result {
SimpleSelectorParseResult::SimpleSelector(s) => { SimpleSelectorParseResult::SimpleSelector(s) => {
builder.push_simple_selector(s); builder.push_simple_selector(s);
}, },

View file

@ -117,13 +117,6 @@ pub trait Element: Sized + Clone + Debug {
case_sensitivity: CaseSensitivity, case_sensitivity: CaseSensitivity,
) -> bool; ) -> 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 /// Returns the mapping from the `exportparts` attribute in the reverse
/// direction, that is, in an outer-tree -> inner-tree direction. /// direction, that is, in an outer-tree -> inner-tree direction.
fn imported_part( fn imported_part(

View file

@ -28,11 +28,11 @@ mod build_gecko {
} }
lazy_static! { 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) { let candidates = if cfg!(windows) {
["python2.7.exe", "python27.exe", "python.exe"] ["python3.exe"]
} else { } else {
["python2.7", "python2", "python"] ["python3"]
}; };
for &name in &candidates { for &name in &candidates {
if Command::new(name) if Command::new(name)
@ -45,7 +45,7 @@ lazy_static! {
} }
} }
panic!( 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(", ") candidates.join(", ")
) )
}); });

View file

@ -408,7 +408,9 @@ impl ToCss for System {
} }
/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol> /// <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)] #[repr(u8)]
pub enum Symbol { pub enum Symbol {
/// <string> /// <string>
@ -554,7 +556,9 @@ impl Parse for Fallback {
} }
/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols> /// <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)] #[repr(C)]
pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>); pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);

View file

@ -579,7 +579,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
match result { match result {
Ok(new_value) => Arc::new(new_value), Ok(new_value) => Arc::new(new_value),
Err(..) => { Err(..) => {
map.remove(name); // Don't touch the map, this has the same effect as
// making it compute to the inherited one.
return; return;
}, },
} }
@ -653,16 +654,22 @@ impl<'a> CustomPropertiesBuilder<'a> {
None => return self.inherited.cloned(), None => return self.inherited.cloned(),
}; };
if self.may_have_cycles { 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)) 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. /// 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 // The cycle dependencies removal in this function is a variant
// of Tarjan's algorithm. It is mostly based on the pseudo-code // of Tarjan's algorithm. It is mostly based on the pseudo-code
// listed in // listed in
@ -698,6 +705,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
/// all unfinished strong connected components. /// all unfinished strong connected components.
stack: SmallVec<[usize; 5]>, stack: SmallVec<[usize; 5]>,
map: &'a mut CustomPropertiesMap, 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. /// to resolve the environment to substitute `env()` variables.
device: &'a Device, device: &'a Device,
} }
@ -831,18 +841,26 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
return None; return None;
} }
// Now we have shown that this variable is not in a loop, and // Now we have shown that this variable is not in a loop, and all of its
// all of its dependencies should have been resolved. We can // dependencies should have been resolved. We can start substitution
// start substitution now. // now.
let result = substitute_references_in_value(&value, &context.map, &context.device); let result = substitute_references_in_value(&value, &context.map, &context.device);
match result { match result {
Ok(computed_value) => { Ok(computed_value) => {
context.map.insert(name, Arc::new(computed_value)); context.map.insert(name, Arc::new(computed_value));
}, },
Err(..) => { Err(..) => {
// 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); context.map.remove(&name);
}, },
};
},
} }
// All resolved, so return the signal value. // All resolved, so return the signal value.
@ -859,6 +877,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
stack: SmallVec::new(), stack: SmallVec::new(),
var_info: SmallVec::new(), var_info: SmallVec::new(),
map: custom_properties_map, map: custom_properties_map,
inherited,
device, device,
}; };
traverse(name, &mut context); traverse(name, &mut context);

View file

@ -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. /// Whether a given element may generate a pseudo-element.
/// ///
/// This is useful to avoid computing, for example, pseudo styles for /// This is useful to avoid computing, for example, pseudo styles for

View file

@ -89,7 +89,7 @@ pub fn traverse_dom<E, D>(
// ThreadLocalStyleContext on the main thread. If the main thread // ThreadLocalStyleContext on the main thread. If the main thread
// ThreadLocalStyleContext has not released its TLS borrow by that point, // ThreadLocalStyleContext has not released its TLS borrow by that point,
// we'll panic on double-borrow. // 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 tlc = ThreadLocalStyleContext::new(traversal.shared_context());
let mut context = StyleContext { let mut context = StyleContext {
shared: traversal.shared_context(), shared: traversal.shared_context(),
@ -129,7 +129,7 @@ pub fn traverse_dom<E, D>(
// depth for all the children. // depth for all the children.
if pool.is_some() && discovered.len() > WORK_UNIT_MAX { if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
let pool = pool.unwrap(); 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 root_opaque = root.as_node().opaque();
let drain = discovered.drain(..); let drain = discovered.drain(..);
pool.install(|| { pool.install(|| {
@ -151,10 +151,12 @@ pub fn traverse_dom<E, D>(
scope, scope,
pool, pool,
traversal, traversal,
maybe_tls.as_ref().unwrap(), &tls,
); );
}); });
}); });
tls_slots = Some(tls.into_slots());
break; break;
} }
nodes_remaining_at_current_depth = discovered.len(); nodes_remaining_at_current_depth = discovered.len();
@ -164,9 +166,9 @@ pub fn traverse_dom<E, D>(
// Collect statistics from thread-locals if requested. // Collect statistics from thread-locals if requested.
if dump_stats || report_stats { if dump_stats || report_stats {
let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default()); let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default());
let parallel = maybe_tls.is_some(); let parallel = tls_slots.is_some();
if let Some(tls) = maybe_tls { if let Some(ref mut tls) = tls_slots {
for mut slot in tls.into_slots().into_vec() { for slot in tls.iter_mut() {
if let Some(cx) = slot.get_mut() { if let Some(cx) = slot.get_mut() {
aggregate += cx.statistics.clone(); aggregate += cx.statistics.clone();
} }

View file

@ -137,6 +137,10 @@ bitflags! {
const IN_AUTOFILL_STATE = 1 << 50; const IN_AUTOFILL_STATE = 1 << 50;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_AUTOFILL_PREVIEW_STATE = 1 << 51; 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;
} }
} }

View file

@ -10,7 +10,7 @@ use crate::context::QuirksMode;
use crate::error_reporting::ParseErrorReporter; use crate::error_reporting::ParseErrorReporter;
use crate::media_queries::MediaList; use crate::media_queries::MediaList;
use crate::shared_lock::SharedRwLock; 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 cssparser::{stylesheet_encoding, EncodingSupport};
use servo_arc::Arc; use servo_arc::Arc;
use std::borrow::Cow; use std::borrow::Cow;
@ -78,6 +78,7 @@ impl Stylesheet {
error_reporter, error_reporter,
quirks_mode, quirks_mode,
0, 0,
AllowImportRules::Yes,
) )
} }
@ -100,6 +101,7 @@ impl Stylesheet {
stylesheet_loader, stylesheet_loader,
error_reporter, error_reporter,
0, 0,
AllowImportRules::Yes,
) )
} }
} }

View file

@ -10,244 +10,9 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::{nsresult, Matrix4x4Components};
use crate::gecko_bindings::structs::{self, Matrix4x4Components};
use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
use crate::stylesheets::RulesMutateError; use crate::stylesheets::RulesMutateError;
use crate::values::computed::transform::Matrix3D; 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 { impl From<RulesMutateError> for nsresult {
fn from(other: RulesMutateError) -> Self { 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 { impl<'a> From<&'a Matrix4x4Components> for Matrix3D {
fn from(m: &'a Matrix4x4Components) -> Matrix3D { fn from(m: &'a Matrix4x4Components) -> Matrix3D {
Matrix3D { Matrix3D {

View file

@ -310,6 +310,17 @@ impl Device {
/// Returns safe area insets /// Returns safe area insets
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> { 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)
} }
} }

View file

@ -30,77 +30,78 @@ macro_rules! apply_non_ts_list {
($apply_macro:ident) => { ($apply_macro:ident) => {
$apply_macro! { $apply_macro! {
[ [
("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("link", Link, link, IN_UNVISITED_STATE, _), ("link", Link, IN_UNVISITED_STATE, _),
("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _), ("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _),
("visited", Visited, visited, IN_VISITED_STATE, _), ("visited", Visited, IN_VISITED_STATE, _),
("active", Active, active, IN_ACTIVE_STATE, _), ("active", Active, IN_ACTIVE_STATE, _),
("checked", Checked, checked, IN_CHECKED_STATE, _), ("checked", Checked, IN_CHECKED_STATE, _),
("defined", Defined, defined, IN_DEFINED_STATE, _), ("defined", Defined, IN_DEFINED_STATE, _),
("disabled", Disabled, disabled, IN_DISABLED_STATE, _), ("disabled", Disabled, IN_DISABLED_STATE, _),
("enabled", Enabled, enabled, IN_ENABLED_STATE, _), ("enabled", Enabled, IN_ENABLED_STATE, _),
("focus", Focus, focus, IN_FOCUS_STATE, _), ("focus", Focus, IN_FOCUS_STATE, _),
("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _), ("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _),
("hover", Hover, hover, IN_HOVER_STATE, _), ("focus-visible", FocusVisible, IN_FOCUS_VISIBLE_STATE, _),
("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _), ("hover", Hover, IN_HOVER_STATE, _),
("target", Target, target, IN_TARGET_STATE, _), ("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _),
("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _), ("target", Target, IN_TARGET_STATE, _),
("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _),
("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _), ("-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). // TODO(emilio): This is inconsistently named (the capital R).
("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _), ("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _),
("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _), ("-moz-broken", MozBroken, IN_BROKEN_STATE, _),
("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _), ("-moz-loading", MozLoading, IN_LOADING_STATE, _),
("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-suppressed", 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-has-dir-attr", 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-ltr", 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-rtl", 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-dir-attr-like-auto", 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", 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-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-clicktoplay", 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-updatable", 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-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-disabled", 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-blocked", 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-handler-crashed", 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-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),
("required", Required, required, IN_REQUIRED_STATE, _), ("required", Required, IN_REQUIRED_STATE, _),
("optional", Optional, optional, IN_OPTIONAL_STATE, _), ("optional", Optional, IN_OPTIONAL_STATE, _),
("valid", Valid, valid, IN_VALID_STATE, _), ("valid", Valid, IN_VALID_STATE, _),
("invalid", Invalid, invalid, IN_INVALID_STATE, _), ("invalid", Invalid, IN_INVALID_STATE, _),
("in-range", InRange, inRange, IN_INRANGE_STATE, _), ("in-range", InRange, IN_INRANGE_STATE, _),
("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _), ("out-of-range", OutOfRange, IN_OUTOFRANGE_STATE, _),
("default", Default, defaultPseudo, IN_DEFAULT_STATE, _), ("default", Default, IN_DEFAULT_STATE, _),
("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _), ("placeholder-shown", PlaceholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
("-moz-read-only", MozReadOnly, mozReadOnly, IN_MOZ_READONLY_STATE, _), ("-moz-read-only", MozReadOnly, IN_MOZ_READONLY_STATE, _),
("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _), ("-moz-read-write", MozReadWrite, IN_MOZ_READWRITE_STATE, _),
("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _), ("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _), ("-moz-ui-valid", MozUIValid, IN_MOZ_UI_VALID_STATE, _),
("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _), ("-moz-ui-invalid", MozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _), ("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _),
("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _), ("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_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-first-node", MozFirstNode, _, _),
("-moz-last-node", MozLastNode, lastNode, _, _), ("-moz-last-node", MozLastNode, _, _),
("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _), ("-moz-only-whitespace", MozOnlyWhitespace, _, _),
("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, mozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, mozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-is-html", MozIsHTML, mozIsHTML, _, _), ("-moz-is-html", MozIsHTML, _, _),
("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _), ("-moz-placeholder", MozPlaceholder, _, _),
("-moz-lwtheme", MozLWTheme, mozLWTheme, _, _), ("-moz-lwtheme", MozLWTheme, _, _),
("-moz-lwtheme-brighttext", MozLWThemeBrightText, mozLWThemeBrightText, _, _), ("-moz-lwtheme-brighttext", MozLWThemeBrightText, _, _),
("-moz-lwtheme-darktext", MozLWThemeDarkText, mozLWThemeDarkText, _, _), ("-moz-lwtheme-darktext", MozLWThemeDarkText, _, _),
("-moz-window-inactive", MozWindowInactive, mozWindowInactive, _, _), ("-moz-window-inactive", MozWindowInactive, _, _),
] ]
} }
} }

View file

@ -134,12 +134,6 @@ impl PseudoElement {
*self == PseudoElement::FirstLine *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. /// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
#[inline] #[inline]
pub fn is_color_swatch(&self) -> bool { pub fn is_color_swatch(&self) -> bool {

View file

@ -94,7 +94,7 @@ class FileAvoidWrite(BytesIO):
self.name = filename self.name = filename
def write(self, buf): def write(self, buf):
if isinstance(buf, unicode): if isinstance(buf, str):
buf = buf.encode('utf-8') buf = buf.encode('utf-8')
BytesIO.write(self, buf) BytesIO.write(self, buf)

View file

@ -42,7 +42,7 @@ bitflags! {
pub type Lang = Atom; pub type Lang = Atom;
macro_rules! pseudo_class_name { 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. /// Our representation of a non tree-structural pseudo-class.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
pub enum NonTSPseudoClass { pub enum NonTSPseudoClass {
@ -72,7 +72,7 @@ impl ToCss for NonTSPseudoClass {
W: fmt::Write, W: fmt::Write,
{ {
macro_rules! pseudo_class_serialize { 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 { match *self {
$(NonTSPseudoClass::$name => concat!(":", $css),)* $(NonTSPseudoClass::$name => concat!(":", $css),)*
NonTSPseudoClass::Lang(ref s) => { NonTSPseudoClass::Lang(ref s) => {
@ -134,7 +134,7 @@ impl NonTSPseudoClass {
/// in a particular state. /// in a particular state.
pub fn parse_non_functional(name: &str) -> Option<Self> { pub fn parse_non_functional(name: &str) -> Option<Self> {
macro_rules! pseudo_class_parse { 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, match_ignore_ascii_case! { &name,
$($css => Some(NonTSPseudoClass::$name),)* $($css => Some(NonTSPseudoClass::$name),)*
"-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen), "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
@ -156,7 +156,7 @@ impl NonTSPseudoClass {
}; };
} }
macro_rules! pseudo_class_check_is_enabled_in { 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 { match *self {
$(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$name => check_flag!($flags),)*
NonTSPseudoClass::MozLocaleDir(_) | NonTSPseudoClass::MozLocaleDir(_) |
@ -172,6 +172,9 @@ impl NonTSPseudoClass {
/// Returns whether the pseudo-class is enabled in content sheets. /// Returns whether the pseudo-class is enabled in content sheets.
#[inline] #[inline]
fn is_enabled_in_content(&self) -> bool { 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) !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
} }
@ -186,7 +189,7 @@ impl NonTSPseudoClass {
}; };
} }
macro_rules! pseudo_class_state { 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 { match *self {
$(NonTSPseudoClass::$name => flag!($state),)* $(NonTSPseudoClass::$name => flag!($state),)*
NonTSPseudoClass::Dir(..) | NonTSPseudoClass::Dir(..) |

View file

@ -193,11 +193,6 @@ impl ElementSnapshot for GeckoElementSnapshot {
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
} }
#[inline]
fn exported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::exported_part(&*self.mAttrs, name)
}
#[inline] #[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> { fn imported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::imported_part(&*self.mAttrs, name) snapshot_helpers::imported_part(&*self.mAttrs, name)

View file

@ -83,16 +83,26 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
} }
#[inline(always)] #[inline(always)]
pub(super) fn exported_part( pub(super) fn each_exported_part(
attrs: &[structs::AttrArray_InternalAttr], attrs: &[structs::AttrArray_InternalAttr],
name: &Atom, name: &Atom,
) -> Option<Atom> { mut callback: impl FnMut(&Atom),
let attr = find_attr(attrs, &atom!("exportparts"))?; ) {
let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) }; let attr = match find_attr(attrs, &atom!("exportparts")) {
if atom.is_null() { Some(attr) => attr,
return None; 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)] #[inline(always)]

View file

@ -72,7 +72,7 @@ use crate::values::computed::Length;
use crate::values::specified::length::FontBaseSize; use crate::values::specified::length::FontBaseSize;
use crate::CaseSensitivityExt; use crate::CaseSensitivityExt;
use app_units::Au; use app_units::Au;
use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::attr::{CaseSensitivity, NamespaceConstraint};
use selectors::matching::VisitedHandlingMode; use selectors::matching::VisitedHandlingMode;
@ -557,8 +557,9 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
} }
impl<'le> GeckoElement<'le> { impl<'le> GeckoElement<'le> {
/// Gets the raw `ElementData` refcell for the element.
#[inline(always)] #[inline(always)]
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> { pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
unsafe { self.0.mServoData.get().as_ref() } 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) 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) fn each_part<F>(&self, callback: F)
where where
F: FnMut(&Atom), F: FnMut(&Atom),
@ -1383,21 +1392,6 @@ impl<'le> TElement for GeckoElement<'le> {
panic!("Atomic child count not implemented in Gecko"); 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> { unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
if !self.has_data() { if !self.has_data() {
debug!("Creating ElementData for {:?}", self); debug!("Creating ElementData for {:?}", self);
@ -1634,6 +1628,22 @@ impl<'le> TElement for GeckoElement<'le> {
.any(|property| !transitions_to_keep.contains(*property)) .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] #[inline]
fn lang_attr(&self) -> Option<AttrValue> { fn lang_attr(&self) -> Option<AttrValue> {
let ptr = unsafe { bindings::Gecko_LangValue(self.0) }; let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
@ -2064,6 +2074,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::MozReadOnly | NonTSPseudoClass::MozReadOnly |
NonTSPseudoClass::MozReadWrite | NonTSPseudoClass::MozReadWrite |
NonTSPseudoClass::FocusWithin | NonTSPseudoClass::FocusWithin |
NonTSPseudoClass::FocusVisible |
NonTSPseudoClass::MozDragOver | NonTSPseudoClass::MozDragOver |
NonTSPseudoClass::MozDevtoolsHighlighted | NonTSPseudoClass::MozDevtoolsHighlighted |
NonTSPseudoClass::MozStyleeditorTransitioning | NonTSPseudoClass::MozStyleeditorTransitioning |
@ -2225,11 +2236,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
} }
#[inline]
fn exported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::exported_part(self.attrs(), name)
}
#[inline] #[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> { fn imported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::imported_part(self.attrs(), name) snapshot_helpers::imported_part(self.attrs(), name)

View file

@ -24,7 +24,18 @@ macro_rules! ns {
} }
/// A Gecko namespace is just a wrapped atom. /// 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)] #[repr(transparent)]
pub struct Namespace(pub Atom); pub struct Namespace(pub Atom);

View file

@ -62,9 +62,6 @@ pub trait ElementSnapshot: Sized {
/// called if `has_attrs()` returns true. /// called if `has_attrs()` returns true.
fn is_part(&self, name: &Atom) -> bool; fn is_part(&self, name: &Atom) -> bool;
/// See Element::exported_part.
fn exported_part(&self, name: &Atom) -> Option<Atom>;
/// See Element::imported_part. /// See Element::imported_part.
fn imported_part(&self, name: &Atom) -> Option<Atom>; 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> { fn imported_part(&self, name: &Atom) -> Option<Atom> {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name), Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),

View file

@ -8,7 +8,7 @@ import re
import sys import sys
BASE = os.path.dirname(__file__.replace('\\', '/')) 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` sys.path.insert(0, BASE) # For importing `data.py`
from mako import exceptions from mako import exceptions
@ -130,7 +130,7 @@ def main():
def abort(message): def abort(message):
sys.stderr.write(message + b"\n") print(message, file=sys.stderr)
sys.exit(1) sys.exit(1)
@ -146,18 +146,18 @@ def render(filename, **context):
strict_undefined=True) strict_undefined=True)
# Uncomment to debug generated Python code: # Uncomment to debug generated Python code:
# write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code) # write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
return template.render(**context).encode("utf8") return template.render(**context)
except Exception: except Exception:
# Uncomment to see a traceback in generated Python code: # Uncomment to see a traceback in generated Python code:
# raise # raise
abort(exceptions.text_error_template().render().encode("utf8")) abort(exceptions.text_error_template().render())
def write(directory, filename, content): def write(directory, filename, content):
if not os.path.exists(directory): if not os.path.exists(directory):
os.makedirs(directory) os.makedirs(directory)
full_path = os.path.join(directory, filename) 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) python_addr = RE_PYTHON_ADDR.search(content)
if python_addr: if python_addr:

View file

@ -13,7 +13,7 @@ use crate::media_queries::Device;
use crate::properties::{ComputedValues, StyleBuilder}; use crate::properties::{ComputedValues, StyleBuilder};
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword}; use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; 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_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::StrongRuleNode; use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
@ -337,89 +337,80 @@ where
context.builder.build() context.builder.build()
} }
/// How should a declaration behave when ignoring document colors? /// For ignored colors mode, we sometimes want to do something equivalent to
enum DeclarationApplication { /// "revert-or-initial", where we `revert` for a given origin, but then apply a
/// We should apply the declaration. /// given initial value if nothing in other origins did override it.
Apply, ///
/// We should ignore the declaration. /// This is a bit of a clunky way of achieving this.
Ignore, type DeclarationsToApplyUnlessOverriden = SmallVec::<[PropertyDeclaration; 2]>;
/// We should apply the following declaration, only if any other declaration
/// hasn't set it before.
ApplyUnlessOverriden(PropertyDeclaration),
}
fn application_when_ignoring_colors( fn tweak_when_ignoring_colors(
builder: &StyleBuilder, builder: &StyleBuilder,
longhand_id: LonghandId, longhand_id: LonghandId,
origin: Origin, origin: Origin,
declaration: &PropertyDeclaration, declaration: &mut Cow<PropertyDeclaration>,
) -> DeclarationApplication { declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
) {
if !longhand_id.ignored_when_document_colors_disabled() { if !longhand_id.ignored_when_document_colors_disabled() {
return DeclarationApplication::Apply; return;
} }
let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent); let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
if is_ua_or_user_rule { if is_ua_or_user_rule {
return DeclarationApplication::Apply; return;
} }
// Don't override background-color on ::-moz-color-swatch. It is set as an // 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 // author style (via the style attribute), but it's pretty important for it
// to show up for obvious reasons :) // to show up for obvious reasons :)
if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor { 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 // A few special-cases ahead.
// anything other than a fully transparent color, convert it into the match **declaration {
// Device's default background color. // We honor color and background-color: transparent, and
// Also: for now, we treat background-image a bit differently, too. // "revert-or-initial" otherwise.
// 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 {
PropertyDeclaration::BackgroundColor(ref color) => { PropertyDeclaration::BackgroundColor(ref color) => {
if color.is_transparent() { if !color.is_transparent() {
return DeclarationApplication::Apply;
}
let color = builder.device.default_background_color(); let color = builder.device.default_background_color();
DeclarationApplication::ApplyUnlessOverriden( declarations_to_apply_unless_overriden.push(
PropertyDeclaration::BackgroundColor(color.into()) PropertyDeclaration::BackgroundColor(color.into())
) )
} }
PropertyDeclaration::Color(ref color) => {
if color.0.is_transparent() {
return DeclarationApplication::Apply;
} }
if builder.get_parent_inherited_text().clone_color().alpha != 0 { PropertyDeclaration::Color(ref color) => {
return DeclarationApplication::Ignore; // otherwise.
if color.0.is_transparent() {
return;
} }
let color = builder.device.default_color(); let color = builder.device.default_color();
DeclarationApplication::ApplyUnlessOverriden( declarations_to_apply_unless_overriden.push(
PropertyDeclaration::Color(specified::ColorPropertyValue(color.into())) PropertyDeclaration::Color(specified::ColorPropertyValue(color.into()))
) )
}, },
// In the future, if/when we remove the backplate pref, we can remove this // We honor url background-images if backplating.
// special case along with the 'ignored_when_colors_disabled=True' mako line
// for the "background-image" property.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
PropertyDeclaration::BackgroundImage(..) => { PropertyDeclaration::BackgroundImage(ref bkg) => {
use crate::values::generics::image::Image;
if static_prefs::pref!("browser.display.permit_backplate") { if static_prefs::pref!("browser.display.permit_backplate") {
DeclarationApplication::Apply if bkg.0.iter().all(|image| matches!(*image, Image::Url(..))) {
} else { return;
DeclarationApplication::Ignore }
} }
}, },
_ => DeclarationApplication::Ignore, _ => {},
} }
*declaration.to_mut() = PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
} }
struct Cascade<'a, 'b: 'a> { struct Cascade<'a, 'b: 'a> {
context: &'a mut computed::Context<'b>, context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>, cascade_mode: CascadeMode<'a>,
seen: LonghandIdSet, seen: LonghandIdSet,
author_specified: LonghandIdSet,
reverted: PerOrigin<LonghandIdSet>, reverted: PerOrigin<LonghandIdSet>,
} }
@ -429,6 +420,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
context, context,
cascade_mode, cascade_mode,
seen: LonghandIdSet::default(), seen: LonghandIdSet::default(),
author_specified: LonghandIdSet::default(),
reverted: Default::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 ignore_colors = !self.context.builder.device.use_document_colors();
let mut declarations_to_apply_unless_overriden = let mut declarations_to_apply_unless_overriden =
SmallVec::<[PropertyDeclaration; 2]>::new(); DeclarationsToApplyUnlessOverriden::new();
for (declaration, origin) in declarations { for (declaration, origin) in declarations {
let declaration_id = declaration.id(); let declaration_id = declaration.id();
@ -533,26 +525,23 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue; 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 // When document colors are disabled, do special handling of
// properties that are marked as ignored in that mode. // properties that are marked as ignored in that mode.
if ignore_colors { if ignore_colors {
let application = application_when_ignoring_colors( tweak_when_ignoring_colors(
&self.context.builder, &self.context.builder,
longhand_id, longhand_id,
origin, 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(); 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); 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| { let unset = css_wide_keyword.map_or(false, |css_wide_keyword| {
match 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() { if let Some(svg) = builder.get_svg_if_mutated() {
svg.fill_arrays(); 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")] #[cfg(feature = "servo")]
@ -711,12 +711,26 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
None => return false, 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, Some(style) => style,
None => return false, 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 true
} }

View file

@ -67,6 +67,23 @@ bitflags! {
/// Whether this style is the style of the document element. /// Whether this style is the style of the document element.
const IS_ROOT_ELEMENT_STYLE = 1 << 11; 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. /// Flags that are unconditionally propagated to descendants.
#[inline] #[inline]
fn inherited_flags() -> Self { fn inherited_flags() -> Self {
ComputedValueFlags::IS_RELEVANT_LINK_VISITED | Self::IS_RELEVANT_LINK_VISITED |
ComputedValueFlags::CAN_BE_FRAGMENTED | Self::CAN_BE_FRAGMENTED |
ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE | Self::IS_IN_PSEUDO_ELEMENT_SUBTREE |
ComputedValueFlags::HAS_TEXT_DECORATION_LINES Self::HAS_TEXT_DECORATION_LINES |
Self::IS_IN_OPACITY_ZERO_SUBTREE
} }
/// Flags that may be propagated to descendants. /// Flags that may be propagated to descendants.

View file

@ -382,6 +382,7 @@ class Longhand(object):
"ScrollSnapStrictness", "ScrollSnapStrictness",
"ScrollSnapType", "ScrollSnapType",
"TextAlign", "TextAlign",
"TextAlignLast",
"TextDecorationLine", "TextDecorationLine",
"TextEmphasisPosition", "TextEmphasisPosition",
"TextTransform", "TextTransform",
@ -602,7 +603,7 @@ class PropertiesData(object):
longhand = Longhand(self.current_style_struct, name, **kwargs) longhand = Longhand(self.current_style_struct, name, **kwargs)
self.add_prefixed_aliases(longhand) 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.longhand_aliases += longhand.alias
self.current_style_struct.longhands.append(longhand) self.current_style_struct.longhands.append(longhand)
self.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] sub_properties = [self.longhands_by_name[s] for s in sub_properties]
shorthand = Shorthand(name, sub_properties, *args, **kwargs) shorthand = Shorthand(name, sub_properties, *args, **kwargs)
self.add_prefixed_aliases(shorthand) 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.shorthand_aliases += shorthand.alias
self.shorthands.append(shorthand) self.shorthands.append(shorthand)
self.shorthands_by_name[name] = shorthand self.shorthands_by_name[name] = shorthand
@ -669,17 +670,17 @@ def _remove_common_first_line_and_first_letter_properties(props, engine):
class PropertyRestrictions: class PropertyRestrictions:
@staticmethod @staticmethod
def logical_group(data, group): 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 @staticmethod
def shorthand(data, shorthand): def shorthand(data, shorthand):
if shorthand not in data.shorthands_by_name: if shorthand not in data.shorthands_by_name:
return [] 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 @staticmethod
def spec(data, spec_path): 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 # https://drafts.csswg.org/css-pseudo/#first-letter-styling
@staticmethod @staticmethod

View file

@ -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}; use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endfor % endfor
use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle; 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_CopyFontFamilyFrom;
use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; 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;
use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::nsCSSPropertyID;
use crate::gecko_bindings::structs::mozilla::PseudoStyleType; use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
use crate::gecko::data::PerDocumentStyleData;
use crate::gecko::values::round_border_to_device_pixels; use crate::gecko::values::round_border_to_device_pixels;
use crate::logical_geometry::WritingMode; use crate::logical_geometry::WritingMode;
use crate::media_queries::Device; use crate::media_queries::Device;
@ -43,11 +41,9 @@ use std::mem::{forget, MaybeUninit};
use std::{cmp, ops, ptr}; use std::{cmp, ops, ptr};
use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
use crate::values::computed::{Percentage, TransitionProperty}; use crate::values::computed::{Percentage, TransitionProperty};
use crate::values::computed::url::ComputedImageUrl;
use crate::values::computed::BorderStyle; use crate::values::computed::BorderStyle;
use crate::values::computed::font::FontSize; use crate::values::computed::font::FontSize;
use crate::values::generics::column::ColumnCount; use crate::values::generics::column::ColumnCount;
use crate::values::generics::image::ImageLayer;
pub mod style_structs { pub mod style_structs {
@ -392,112 +388,6 @@ def set_gecko_property(ffi_name, expr):
} }
</%def> </%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, <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None,
round_to_pixels=False)"> round_to_pixels=False)">
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -647,12 +537,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
</%def> </%def>
<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name=None)"> <%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)">
<%
if gecko_ffi_name is None:
gecko_ffi_name = "m" + to_camel_case(ident)
%>
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
self.gecko.${gecko_ffi_name} = From::from(v) 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. # Types used with predefined_type()-defined properties that we can auto-generate.
predefined_types = { predefined_types = {
"MozScriptMinSize": impl_absolute_length, "MozScriptMinSize": impl_absolute_length,
"SVGLength": impl_svg_length,
"SVGOpacity": impl_svg_opacity,
"SVGWidth": impl_svg_length,
} }
def longhand_method(longhand): def longhand_method(longhand):
@ -778,8 +660,7 @@ fn static_assert() {
for x in CORNERS]) %> for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Border" <%self:impl_trait style_struct_name="Border"
skip_longhands="${skip_border_longhands} border-image-source skip_longhands="${skip_border_longhands} border-image-repeat">
border-image-repeat">
% for side in SIDES: % for side in SIDES:
pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) { pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
self.gecko.mBorderStyle[${side.index}] = v; self.gecko.mBorderStyle[${side.index}] = v;
@ -848,35 +729,6 @@ fn static_assert() {
corner) %> corner) %>
% endfor % 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"] 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) %> <% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
<%self:impl_trait style_struct_name="Position" <%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands} grid-auto-flow"> skip_longhands="${skip_position_longhands}">
% for side in SIDES: % for side in SIDES:
<% impl_split_style_coord(side.ident, "mOffset", side.index) %> <% impl_split_style_coord(side.ident, "mOffset", side.index) %>
% endfor % endfor
${impl_simple_type_with_conversion("grid_auto_flow")}
pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) { pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY); debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
self.gecko.mJustifyItems.computed = v; self.gecko.mJustifyItems.computed = v;
@ -1005,15 +856,13 @@ fn static_assert() {
} }
</%self:impl_trait> </%self:impl_trait>
<% <% skip_font_longhands = """font-family font-size font-size-adjust font-weight
skip_font_longhands = """font-family font-size font-size-adjust font-weight font-style font-stretch font-synthesis -x-lang
font-style font-stretch -moz-script-level font-variant-alternates font-variant-east-asian
font-synthesis -x-lang font-variant-alternates font-variant-ligatures font-variant-numeric
font-variant-east-asian font-variant-ligatures font-language-override font-feature-settings
font-variant-numeric font-language-override font-variation-settings -moz-min-font-size-ratio
font-feature-settings font-variation-settings -x-text-zoom""" %>
-moz-min-font-size-ratio -x-text-zoom"""
%>
<%self:impl_trait style_struct_name="Font" <%self:impl_trait style_struct_name="Font"
skip_longhands="${skip_font_longhands}"> skip_longhands="${skip_font_longhands}">
@ -1275,9 +1124,7 @@ fn static_assert() {
longhands::_x_text_zoom::computed_value::T(self.gecko.mAllowZoom) 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_language_override", "mFont.languageOverride") %>
${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")} ${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_east_asian", "mFont.variantEastAsian")}
${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")} ${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)} ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)}
</%def> </%def>
<%def name="impl_transition_timing_function()">
${impl_animation_or_transition_timing_function('transition')}
</%def>
<%def name="impl_animation_count(ident, gecko_ffi_name)"> <%def name="impl_animation_count(ident, gecko_ffi_name)">
${impl_animation_or_transition_count('animation', 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)} ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)}
</%def> </%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')"> <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn set_animation_${ident}<I>(&mut self, v: I) pub fn set_animation_${ident}<I>(&mut self, v: I)
@ -1466,7 +1306,7 @@ fn static_assert() {
animation-iteration-count animation-timing-function animation-iteration-count animation-timing-function
clear transition-duration transition-delay clear transition-duration transition-delay
transition-timing-function transition-property 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}"> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
#[inline] #[inline]
pub fn set_display(&mut self, v: longhands::display::computed_value::T) { 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('delay', 'Delay')}
${impl_transition_time_value('duration', 'Duration')} ${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 { pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
// https://drafts.csswg.org/css-transitions/#transition-combined-duration // https://drafts.csswg.org/css-transitions/#transition-combined-duration
@ -1723,10 +1563,7 @@ fn static_assert() {
${impl_animation_count('iteration_count', 'IterationCount')} ${impl_animation_count('iteration_count', 'IterationCount')}
${impl_copy_animation_value('iteration_count', 'IterationCount')} ${impl_copy_animation_value('iteration_count', 'IterationCount')}
${impl_animation_or_transition_timing_function('animation')}
${impl_animation_timing_function()}
<% impl_shape_source("shape_outside", "mShapeOutside") %>
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) { 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() for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
.zip(other.gecko.${image_layers_field}.mLayers.iter()) .zip(other.gecko.${image_layers_field}.mLayers.iter())
.take(count as usize) { .take(count as usize) {
Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage); layer.mImage = other.mImage.clone();
} }
self.gecko.${image_layers_field}.mImageCount = count; self.gecko.${image_layers_field}.mImageCount = count;
} }
@ -2029,22 +1866,17 @@ fn static_assert() {
let images = images.into_iter(); let images = images.into_iter();
unsafe { unsafe {
// Prevent leaking of the last elements we did set Gecko_EnsureImageLayersLength(
for image in &mut self.gecko.${image_layers_field}.mLayers { &mut self.gecko.${image_layers_field},
Gecko_SetNullImageValue(&mut image.mImage) images.len(),
} LayerType::${shorthand.title()},
// XXXManishearth clear mSourceURI for masks );
Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, images.len(),
LayerType::${shorthand.title()});
} }
self.gecko.${image_layers_field}.mImageCount = images.len() as u32; self.gecko.${image_layers_field}.mImageCount = images.len() as u32;
for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} for (image, geckoimage) in images.zip(self.gecko.${image_layers_field}
.mLayers.iter_mut()) { .mLayers.iter_mut()) {
if let ImageLayer::Image(image) = image { geckoimage.mImage = image;
geckoimage.mImage.set(image)
}
} }
} }
@ -2052,12 +1884,8 @@ fn static_assert() {
longhands::${shorthand}_image::computed_value::List( longhands::${shorthand}_image::computed_value::List(
self.gecko.${image_layers_field}.mLayers.iter() self.gecko.${image_layers_field}.mLayers.iter()
.take(self.gecko.${image_layers_field}.mImageCount as usize) .take(self.gecko.${image_layers_field}.mImageCount as usize)
.map(|ref layer| { .map(|layer| layer.mImage.clone())
match unsafe { layer.mImage.into_image() } { .collect()
Some(image) => ImageLayer::Image(image),
None => ImageLayer::None,
}
}).collect()
) )
} }
@ -2189,18 +2017,9 @@ fn static_assert() {
<%self:impl_trait style_struct_name="InheritedText" <%self:impl_trait style_struct_name="InheritedText"
skip_longhands="text-align -webkit-text-stroke-width text-emphasis-position"> skip_longhands="-webkit-text-stroke-width">
<% 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")}
${impl_non_negative_length('_webkit_text_stroke_width', ${impl_non_negative_length('_webkit_text_stroke_width',
'mWebkitTextStrokeWidth')} 'mWebkitTextStrokeWidth')}
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter"> <%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter">
@ -2244,204 +2063,21 @@ fn static_assert() {
} }
</%self:impl_trait> </%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 = """ <% skip_svg_longhands = """
mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image 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" <%self:impl_trait style_struct_name="SVG"
skip_longhands="${skip_svg_longhands}"> skip_longhands="${skip_svg_longhands}">
<% impl_common_image_layer_properties("mask") %> <% impl_common_image_layer_properties("mask") %>
<% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %> <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
<% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %> <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
<% impl_shape_source("clip_path", "mClipPath") %>
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="InheritedSVG" <%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> </%self:impl_trait>
<%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor"> <%self:impl_trait style_struct_name="InheritedUI">
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> </%self:impl_trait>
<%self:impl_trait style_struct_name="Column" <%self:impl_trait style_struct_name="Column"
@ -2483,8 +2119,7 @@ clip-path
} }
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="UI" skip_longhands="-moz-force-broken-image-icon"> <%self:impl_trait style_struct_name="UI">
${impl_simple_type_with_conversion("_moz_force_broken_image_icon", "mForceBrokenImageIcon")}
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="XUL"> <%self:impl_trait style_struct_name="XUL">
@ -2494,3 +2129,40 @@ clip-path
${declare_style_struct(style_struct)} ${declare_style_struct(style_struct)}
${impl_style_struct(style_struct)} ${impl_style_struct(style_struct)}
% endfor % 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
}
}

View file

@ -680,7 +680,7 @@
% endfor % endfor
let mut bits = ${type}::empty(); 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 { if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
bits |= ${servo_bit}; bits |= ${servo_bit};
} }
@ -696,7 +696,7 @@
let mut bits: ${kw_type} = 0; let mut bits: ${kw_type} = 0;
// FIXME: if we ensure that the Servo bitflags storage is the same // FIXME: if we ensure that the Servo bitflags storage is the same
// as Gecko's one, we can just copy it. // 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}) { if self.contains(${servo_bit}) {
bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type}; bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
} }
@ -707,7 +707,8 @@
</%def> </%def>
<%def name="single_keyword(name, values, vector=False, <%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 [ keyword_kwargs = {a: kwargs.pop(a, None) for a in [
'gecko_constant_prefix', '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)"> <%def name="variants(variants, include_aliases)">
% for variant in variants: % for variant in variants:
% if include_aliases: % if include_aliases:
<% <%
aliases = [] aliases = []
for alias, v in keyword.aliases_for(engine).iteritems(): for alias, v in keyword.aliases_for(engine).items():
if variant == v: if variant == v:
aliases.append(alias) aliases.append(alias)
%> %>
% if aliases: % if aliases:
#[parse(aliases = "${','.join(aliases)}")] #[parse(aliases = "${','.join(sorted(aliases))}")]
% endif % endif
% endif % endif
${to_camel_case(variant)}, ${to_camel_case(variant)},
@ -773,10 +775,20 @@
} }
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { 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])} computed_value::T::${to_camel_case(values.split()[0])}
} }
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { 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])} SpecifiedValue::${to_camel_case(values.split()[0])}
} }
#[inline] #[inline]
@ -805,7 +817,8 @@
% else: % else:
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
${inner_body(Keyword(name, values, **keyword_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: % if caller:
${caller.body()} ${caller.body()}
% endif % endif

View file

@ -21,10 +21,10 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"background-image", "background-image",
"ImageLayer", "Image",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
initial_value="computed::ImageLayer::none()", initial_value="computed::Image::None",
initial_specified_value="specified::ImageLayer::none()", initial_specified_value="specified::Image::None",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
vector="True", vector="True",
animation_value_type="discrete", animation_value_type="discrete",
@ -35,8 +35,8 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"background-position-" + axis, "background-position-" + axis,
"position::" + direction + "Position", "position::" + direction + "Position",
"computed::LengthPercentage::zero_percent()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
initial_value="computed::LengthPercentage::zero()",
initial_specified_value="SpecifiedValue::initial_specified_value()", initial_specified_value="SpecifiedValue::initial_specified_value()",
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis, spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
@ -107,7 +107,7 @@ ${helpers.single_keyword(
"""normal multiply screen overlay darken lighten color-dodge """normal multiply screen overlay darken lighten color-dodge
color-burn hard-light soft-light difference exclusion hue color-burn hard-light soft-light difference exclusion hue
saturation color luminosity""", saturation color luminosity""",
gecko_constant_prefix="NS_STYLE_BLEND", gecko_enum_prefix="StyleBlend",
vector=True, vector=True,
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",

View file

@ -106,10 +106,10 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"border-image-source", "border-image-source",
"ImageLayer", "Image",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
initial_value="computed::ImageLayer::none()", initial_value="computed::Image::None",
initial_specified_value="specified::ImageLayer::none()", initial_specified_value="specified::Image::None",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
vector=False, vector=False,
animation_value_type="discrete", animation_value_type="discrete",

View file

@ -573,6 +573,7 @@ ${helpers.single_keyword(
"backface-visibility", "backface-visibility",
"visible hidden", "visible hidden",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
gecko_enum_prefix="StyleBackfaceVisibility",
spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property", spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
extra_prefixes=transform_extra_prefixes, extra_prefixes=transform_extra_prefixes,
animation_value_type="discrete", animation_value_type="discrete",
@ -633,7 +634,7 @@ ${helpers.predefined_type(
"Appearance", "Appearance",
"computed::Appearance::None", "computed::Appearance::None",
engines="gecko", 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)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
animation_value_type="discrete", animation_value_type="discrete",
gecko_ffi_name="mAppearance", gecko_ffi_name="mAppearance",
@ -679,10 +680,10 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"shape-outside", "shape-outside",
"basic_shape::FloatAreaShape", "basic_shape::ShapeOutside",
"generics::basic_shape::ShapeSource::None", "generics::basic_shape::ShapeOutside::None",
engines="gecko", engines="gecko",
animation_value_type="basic_shape::FloatAreaShape", animation_value_type="basic_shape::ShapeOutside",
spec="https://drafts.csswg.org/css-shapes/#shape-outside-property", spec="https://drafts.csswg.org/css-shapes/#shape-outside-property",
)} )}

View file

@ -83,7 +83,7 @@ ${helpers.single_keyword(
color-burn hard-light soft-light difference exclusion hue color-burn hard-light soft-light difference exclusion hue
saturation color luminosity""", saturation color luminosity""",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
gecko_constant_prefix="NS_STYLE_BLEND", gecko_enum_prefix="StyleBlend",
animation_value_type="discrete", animation_value_type="discrete",
flags="CREATES_STACKING_CONTEXT", flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode", spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",

View file

@ -230,7 +230,7 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-script-level", "-moz-script-level",
"MozScriptLevel", "MozScriptLevel",
0, "0",
engines="gecko", engines="gecko",
animation_value_type="none", animation_value_type="none",
enabled_in="ua", enabled_in="ua",

View file

@ -86,6 +86,7 @@ ${helpers.single_keyword(
${helpers.single_keyword( ${helpers.single_keyword(
"image-orientation", "image-orientation",
"none from-image", "none from-image",
gecko_pref_controlled_initial_value="layout.css.image-orientation.initial-from-image=from-image",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleImageOrientation", gecko_enum_prefix="StyleImageOrientation",
animation_value_type="discrete", animation_value_type="discrete",

View file

@ -36,15 +36,16 @@ ${helpers.single_keyword(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty", spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty",
gecko_enum_prefix="StyleColorInterpolation",
)} )}
${helpers.single_keyword( ${helpers.single_keyword(
"color-interpolation-filters", "color-interpolation-filters",
"linearrgb auto srgb", "linearrgb auto srgb",
engines="gecko", engines="gecko",
gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty", spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty",
gecko_enum_prefix="StyleColorInterpolation",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -141,11 +141,12 @@ ${helpers.predefined_type(
% endif % endif
</%helpers:single_keyword> </%helpers:single_keyword>
${helpers.single_keyword( ${helpers.predefined_type(
"text-align-last", "text-align-last",
"auto start end left right center justify", "TextAlignLast",
"computed::text::TextAlignLast::Auto",
needs_context=False,
engines="gecko", engines="gecko",
gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last", spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
)} )}
@ -244,7 +245,7 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"text-emphasis-style", "text-emphasis-style",
"TextEmphasisStyle", "TextEmphasisStyle",
None, "computed::TextEmphasisStyle::None",
engines="gecko", engines="gecko",
initial_specified_value="SpecifiedValue::None", initial_specified_value="SpecifiedValue::None",
animation_value_type="discrete", animation_value_type="discrete",
@ -367,13 +368,12 @@ ${helpers.single_keyword(
servo_restyle_damage="rebuild_and_reflow", 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( ${helpers.single_keyword(
"-moz-control-character-visibility", "-moz-control-character-visibility",
"hidden visible", "hidden visible",
engines="gecko", 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", animation_value_type="none",
gecko_ffi_name="mControlCharacterVisibility", gecko_ffi_name="mControlCharacterVisibility",
spec="Nonstandard", spec="Nonstandard",

View file

@ -333,7 +333,7 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"object-position", "object-position",
"Position", "Position",
"computed::Position::zero()", "computed::Position::center()",
engines="gecko", engines="gecko",
boxed=True, boxed=True,
spec="https://drafts.csswg.org/css-images-3/#the-object-position", spec="https://drafts.csswg.org/css-images-3/#the-object-position",
@ -375,7 +375,7 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"grid-auto-flow", "grid-auto-flow",
"GridAutoFlow", "GridAutoFlow",
"computed::GridAutoFlow::row()", "computed::GridAutoFlow::ROW",
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow", spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",

View file

@ -10,6 +10,7 @@ ${helpers.single_keyword(
"vector-effect", "vector-effect",
"none non-scaling-stroke", "none non-scaling-stroke",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleVectorEffect",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty", spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
)} )}
@ -76,10 +77,10 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"clip-path", "clip-path",
"basic_shape::ClippingShape", "basic_shape::ClipPath",
"generics::basic_shape::ShapeSource::None", "generics::basic_shape::ClipPath::None",
engines="gecko", engines="gecko",
animation_value_type="basic_shape::ClippingShape", animation_value_type="basic_shape::ClipPath",
flags="CREATES_STACKING_CONTEXT", flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)} )}
@ -110,7 +111,7 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"mask-position-" + axis, "mask-position-" + axis,
"position::" + direction + "Position", "position::" + direction + "Position",
"computed::LengthPercentage::zero()", "computed::LengthPercentage::zero_percent()",
engines="gecko", engines="gecko",
extra_prefixes="webkit", extra_prefixes="webkit",
initial_specified_value="specified::PositionComponent::Center", initial_specified_value="specified::PositionComponent::Center",
@ -164,6 +165,7 @@ ${helpers.single_keyword(
"mask-composite", "mask-composite",
"add subtract intersect exclude", "add subtract intersect exclude",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleMaskComposite",
vector=True, vector=True,
extra_prefixes="webkit", extra_prefixes="webkit",
animation_value_type="discrete", animation_value_type="discrete",
@ -172,10 +174,10 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"mask-image", "mask-image",
"ImageLayer", "Image",
engines="gecko", engines="gecko",
initial_value="computed::ImageLayer::none()", initial_value="computed::Image::None",
initial_specified_value="specified::ImageLayer::none()", initial_specified_value="specified::Image::None",
parse_method="parse_with_cors_anonymous", parse_method="parse_with_cors_anonymous",
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
vector=True, vector=True,

View file

@ -56,7 +56,7 @@ ${helpers.single_keyword(
${helpers.single_keyword( ${helpers.single_keyword(
"-moz-window-shadow", "-moz-window-shadow",
"none default menu tooltip sheet", "default none menu tooltip sheet",
engines="gecko", engines="gecko",
gecko_ffi_name="mWindowShadow", gecko_ffi_name="mWindowShadow",
gecko_enum_prefix="StyleWindowShadow", gecko_enum_prefix="StyleWindowShadow",

View file

@ -64,10 +64,12 @@ ${helpers.single_keyword(
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)", 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( ${helpers.predefined_type(
"-moz-box-ordinal-group", "-moz-box-ordinal-group",
"Integer", "Integer",
"0", "1",
engines="gecko", engines="gecko",
parse_method="parse_non_negative", parse_method="parse_non_negative",
alias="-webkit-box-ordinal-group", alias="-webkit-box-ordinal-group",

View file

@ -312,7 +312,7 @@ impl Clone for PropertyDeclaration {
trait AssertCopy { fn assert() {} } trait AssertCopy { fn assert() {} }
trait AssertNotCopy { fn assert() {} } trait AssertNotCopy { fn assert() {} }
impl<T: Copy> AssertCopy for Helper<T> {} 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}> {} impl AssertNotCopy for Helper<${ty}> {}
Helper::<${ty}>::assert(); Helper::<${ty}>::assert();
% endfor % endfor
@ -729,10 +729,10 @@ impl NonCustomPropertyIdSet {
<%def name="static_non_custom_property_id_set(name, is_member)"> <%def name="static_non_custom_property_id_set(name, is_member)">
static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet { 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()): for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
if is_member(property): 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)}] 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)"> <%def name="static_longhand_id_set(name, is_member)">
static ${name}: LonghandIdSet = LonghandIdSet { 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): for i, property in enumerate(data.longhands):
if is_member(property): 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)}] storage: [${", ".join("0x%x" % word for word in storage)}]
}; };
</%def> </%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 /// A set of longhand properties
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)] #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
pub struct LonghandIdSet { pub struct LonghandIdSet {
@ -837,6 +883,30 @@ impl LonghandIdSet {
&HAS_NO_EFFECT_ON_SCROLLBARS &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. /// Iterate over the current longhand id set.
pub fn iter(&self) -> LonghandIdSetIterator { pub fn iter(&self) -> LonghandIdSetIterator {
LonghandIdSetIterator { longhands: self, cur: 0, } 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. /// 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)] #[repr(u16)]
pub enum LonghandId { pub enum LonghandId {
% for i, property in enumerate(data.longhands): % for i, property in enumerate(data.longhands):
@ -1089,6 +1114,7 @@ impl LonghandId {
// could potentially do so, which would speed up serialization // could potentially do so, which would speed up serialization
// algorithms and what not, I guess. // algorithms and what not, I guess.
<% <%
from functools import cmp_to_key
longhand_to_shorthand_map = {} longhand_to_shorthand_map = {}
num_sub_properties = {} num_sub_properties = {}
for shorthand in data.shorthands: for shorthand in data.shorthands:
@ -1099,6 +1125,9 @@ impl LonghandId {
longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case) 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): def preferred_order(x, y):
# Since we want properties in order from most subproperties to least, # Since we want properties in order from most subproperties to least,
# reverse the arguments to cmp from the expected order. # 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: # Sort the lists of shorthand properties according to preferred order:
# https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
for shorthand_list in longhand_to_shorthand_map.itervalues(): for shorthand_list in longhand_to_shorthand_map.values():
shorthand_list.sort(cmp=preferred_order) shorthand_list.sort(key=cmp_to_key(preferred_order))
%> %>
// based on lookup results for each longhand, create result arrays // based on lookup results for each longhand, create result arrays
@ -1353,7 +1382,7 @@ where
} }
/// An identifier for a given shorthand property. /// 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)] #[repr(u16)]
pub enum ShorthandId { pub enum ShorthandId {
% for i, property in enumerate(data.shorthands): % for i, property in enumerate(data.shorthands):
@ -1590,10 +1619,7 @@ impl UnparsedValue {
} else { } else {
CSSWideKeyword::Initial CSSWideKeyword::Initial
}; };
PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { PropertyDeclaration::css_wide_keyword(longhand_id, keyword)
id: longhand_id,
keyword,
})
}; };
let css = match crate::custom_properties::substitute( let css = match crate::custom_properties::substitute(
@ -1630,10 +1656,7 @@ impl UnparsedValue {
let mut input = Parser::new(&mut input); let mut input = Parser::new(&mut input);
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
if let Ok(keyword) = input.try(CSSWideKeyword::parse) { if let Ok(keyword) = input.try(CSSWideKeyword::parse) {
return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { return PropertyDeclaration::css_wide_keyword(longhand_id, keyword);
id: longhand_id,
keyword,
});
} }
let declaration = input.parse_entirely(|input| { 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. /// Returns a CSS-wide keyword if the declaration's value is one.
#[inline] #[inline]
pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> { pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
@ -2367,9 +2396,7 @@ impl PropertyDeclaration {
PropertyId::Longhand(id) => { PropertyId::Longhand(id) => {
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
input.try(CSSWideKeyword::parse).map(|keyword| { input.try(CSSWideKeyword::parse).map(|keyword| {
PropertyDeclaration::CSSWideKeyword( PropertyDeclaration::css_wide_keyword(id, keyword)
WideKeywordDeclaration { id, keyword },
)
}).or_else(|()| { }).or_else(|()| {
input.look_for_var_or_env_functions(); input.look_for_var_or_env_functions();
input.parse_entirely(|input| id.parse_value(context, input)) input.parse_entirely(|input| id.parse_value(context, input))
@ -2403,12 +2430,7 @@ impl PropertyDeclaration {
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
} else { } else {
for longhand in id.longhands() { for longhand in id.longhands() {
declarations.push(PropertyDeclaration::CSSWideKeyword( declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword));
WideKeywordDeclaration {
id: longhand,
keyword,
},
))
} }
} }
} else { } else {
@ -2550,12 +2572,7 @@ impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
match *self.all_shorthand { match *self.all_shorthand {
AllShorthand::NotSet => None, AllShorthand::NotSet => None,
AllShorthand::CSSWideKeyword(ref keyword) => { AllShorthand::CSSWideKeyword(ref keyword) => {
Some(PropertyDeclaration::CSSWideKeyword( Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword))
WideKeywordDeclaration {
id: self.longhands.next()?,
keyword: *keyword
}
))
} }
AllShorthand::WithVariables(ref unparsed) => { AllShorthand::WithVariables(ref unparsed) => {
Some(PropertyDeclaration::WithVariables( Some(PropertyDeclaration::WithVariables(

View file

@ -70,11 +70,10 @@ pub fn parse_border<'i, 't>(
let mut width = None; let mut width = None;
let mut any = false; let mut any = false;
loop { loop {
if color.is_none() { if width.is_none() {
if let Ok(value) = input.try(|i| Color::parse(context, i)) { if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) {
color = Some(value); width = Some(value);
any = true; any = true;
continue
} }
} }
if style.is_none() { if style.is_none() {
@ -84,9 +83,9 @@ pub fn parse_border<'i, 't>(
continue continue
} }
} }
if width.is_none() { if color.is_none() {
if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) { if let Ok(value) = input.try(|i| Color::parse(context, i)) {
width = Some(value); color = Some(value);
any = true; any = true;
continue continue
} }

View file

@ -549,7 +549,7 @@
use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow}; use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
use crate::values::generics::grid::GridTemplateComponent; use crate::values::generics::grid::GridTemplateComponent;
use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks}; 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>( pub fn parse_value<'i, 't>(
context: &ParserContext, context: &ParserContext,
@ -566,28 +566,28 @@
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
is_row: bool, is_row: bool,
) -> Result<GridAutoFlow, ParseError<'i>> { ) -> Result<GridAutoFlow, ParseError<'i>> {
let mut auto_flow = None; let mut track = None;
let mut dense = false; let mut dense = GridAutoFlow::empty();
for _ in 0..2 { for _ in 0..2 {
if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() { if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() {
auto_flow = if is_row { track = if is_row {
Some(AutoFlow::Row) Some(GridAutoFlow::ROW)
} else { } else {
Some(AutoFlow::Column) Some(GridAutoFlow::COLUMN)
}; };
} else if input.try(|i| i.expect_ident_matching("dense")).is_ok() { } else if input.try(|i| i.expect_ident_matching("dense")).is_ok() {
dense = true; dense = GridAutoFlow::DENSE
} else { } else {
break break
} }
} }
auto_flow.map(|flow| { if track.is_some() {
GridAutoFlow { Ok(track.unwrap() | dense)
autoflow: flow, } else {
dense: dense, Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
}).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) { 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); 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. // It should fail to serialize if other branch of the if condition's values are set.
if !self.grid_auto_rows.is_initial() || if !self.grid_auto_rows.is_initial() ||
!self.grid_template_columns.is_initial() { !self.grid_template_columns.is_initial() {
@ -653,7 +653,7 @@
self.grid_template_rows.to_css(dest)?; self.grid_template_rows.to_css(dest)?;
dest.write_str(" / auto-flow")?; dest.write_str(" / auto-flow")?;
if self.grid_auto_flow.dense { if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
dest.write_str(" dense")?; dest.write_str(" dense")?;
} }
@ -676,7 +676,7 @@
} }
dest.write_str("auto-flow")?; dest.write_str("auto-flow")?;
if self.grid_auto_flow.dense { if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
dest.write_str(" dense")?; dest.write_str(" dense")?;
} }

View file

@ -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 /// An object that we use with all the intermediate state needed for the
/// cascade. /// cascade.
/// ///
@ -82,6 +77,7 @@ where
flags_setter: &'a mut F, flags_setter: &'a mut F,
matches_user_and_author_rules: bool, matches_user_and_author_rules: bool,
matches_document_author_rules: bool, matches_document_author_rules: bool,
in_sort_scope: bool,
} }
impl<'a, 'b: 'a, E, F: 'a> RuleCollector<'a, 'b, E, F> impl<'a, 'b: 'a, E, F: 'a> RuleCollector<'a, 'b, E, F>
@ -134,9 +130,36 @@ where
rules, rules,
matches_user_and_author_rules, matches_user_and_author_rules,
matches_document_author_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) { fn collect_stylist_rules(&mut self, origin: Origin) {
let cascade_level = match origin { let cascade_level = match origin {
Origin::UserAgent => CascadeLevel::UANormal, Origin::UserAgent => CascadeLevel::UANormal,
@ -150,7 +173,9 @@ where
None => return, 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) { fn collect_user_agent_rules(&mut self) {
@ -189,39 +214,30 @@ where
} }
} }
fn collect_rules_in_shadow_tree( #[inline]
&mut self, fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel) {
shadow_host: E, debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
map: &SelectorMap<Rule>, SelectorMap::get_matching_rules(
cascade_level: CascadeLevel, self.element,
) { part_rules,
debug_assert!(shadow_host.shadow_root().is_some()); &mut self.rules,
self.collect_rules_internal(Some(shadow_host), map, cascade_level); &mut self.context,
&mut self.flags_setter,
cascade_level,
);
} }
#[inline] #[inline]
fn collect_rules_internal( fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel) {
&mut self, debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
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( map.get_all_matching_rules(
element, self.element,
rule_hash_target, self.rule_hash_target,
rules, &mut self.rules,
context, &mut self.context,
flags_setter, &mut self.flags_setter,
cascade_level, cascade_level,
); );
});
sort_rules_from(rules, start);
} }
/// Collects the rules for the ::slotted pseudo-element and the :host /// Collects the rules for the ::slotted pseudo-element and the :host
@ -258,17 +274,16 @@ where
None => continue, None => continue,
}; };
self.collect_rules_in_shadow_tree( self.in_shadow_tree(shadow.host(), |collector| {
shadow.host(), let cascade_level = CascadeLevel::AuthorNormal {
slotted_rules,
CascadeLevel::AuthorNormal {
shadow_cascade_order, 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 { if !self.matches_user_and_author_rules {
return; return;
} }
@ -281,11 +296,34 @@ where
self.matches_document_author_rules = false; self.matches_document_author_rules = false;
let cascade_data = containing_shadow.style_data(); let cascade_data = match containing_shadow.style_data() {
let host = containing_shadow.host(); Some(c) => c,
if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) { None => return,
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal()); };
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. /// Collects the rules for the :host pseudo-class.
@ -311,13 +349,12 @@ where
}; };
let rule_hash_target = self.rule_hash_target; let rule_hash_target = self.rule_hash_target;
self.collect_rules_in_shadow_tree( self.in_shadow_tree(rule_hash_target, |collector| {
rule_hash_target, let cascade_level = CascadeLevel::AuthorNormal {
host_rules,
CascadeLevel::AuthorNormal {
shadow_cascade_order, shadow_cascade_order,
}, };
); collector.collect_rules_in_map(host_rules, cascade_level);
});
} }
fn collect_document_author_rules(&mut self) { fn collect_document_author_rules(&mut self) {
@ -328,7 +365,7 @@ where
self.collect_stylist_rules(Origin::Author); 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() { if !self.rule_hash_target.has_part_attr() {
return; return;
} }
@ -363,28 +400,16 @@ where
if let Some(part_rules) = part_rules { if let Some(part_rules) = part_rules {
let containing_host = outer_shadow.map(|s| s.host()); 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 { let cascade_level = CascadeLevel::AuthorNormal {
shadow_cascade_order, shadow_cascade_order,
}; };
let start = rules.len(); self.in_tree(containing_host, |collector| {
self.context.with_shadow_host(containing_host, |context| {
for p in &parts { for p in &parts {
if let Some(part_rules) = part_rules.get(p) { if let Some(part_rules) = part_rules.get(p) {
SelectorMap::get_matching_rules( collector.collect_rules_in_list(part_rules, cascade_level);
element,
&part_rules,
rules,
context,
flags_setter,
cascade_level,
);
} }
} }
}); });
sort_rules_from(rules, start);
shadow_cascade_order.inc(); shadow_cascade_order.inc();
} }
@ -393,15 +418,14 @@ where
None => break, // Nowhere to export to. None => break, // Nowhere to export to.
}; };
parts.retain(|part| { let mut new_parts = SmallVec::new();
let exported_part = match inner_shadow_host.exported_part(part) { for part in &parts {
Some(part) => part, inner_shadow_host.each_exported_part(part, |exported_part| {
None => return false, new_parts.push(exported_part.clone());
};
std::mem::replace(part, exported_part);
true
}); });
} }
parts = new_parts;
}
} }
fn collect_style_attribute(&mut self) { fn collect_style_attribute(&mut self) {
@ -461,10 +485,10 @@ where
return; return;
} }
self.collect_host_and_slotted_rules(); 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_document_author_rules();
self.collect_style_attribute(); self.collect_style_attribute();
self.collect_part_rules(); self.collect_part_rules_from_outer_trees();
self.collect_animation_rules(); self.collect_animation_rules();
} }
} }

View file

@ -7,8 +7,6 @@
//! The rule tree. //! The rule tree.
use crate::applicable_declarations::ApplicableDeclarationList; use crate::applicable_declarations::ApplicableDeclarationList;
#[cfg(feature = "gecko")]
use crate::gecko::selector_parser::PseudoElement;
use crate::hash::{self, FxHashMap}; use crate::hash::{self, FxHashMap};
use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
@ -136,29 +134,6 @@ impl StyleSource {
let _ = write!(writer, " -> {:?}", self.read(guard).declarations()); 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 /// Read the style source guard, and obtain thus read access to the
/// underlying property declaration block. /// underlying property declaration block.
#[inline] #[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. /// Returns true if there is either animation or transition level rule.
pub fn has_animation_or_transition_rules(&self) -> bool { pub fn has_animation_or_transition_rules(&self) -> bool {
self.self_and_ancestors() self.self_and_ancestors()
@ -1742,47 +1525,6 @@ impl Drop for StrongRuleNode {
return; 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() { if node.parent.is_none() {
debug!("Dropping root node!"); debug!("Dropping root node!");
// The free list should be null by this point // The free list should be null by this point

View file

@ -709,10 +709,6 @@ impl ElementSnapshot for ServoElementSnapshot {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &Atom) -> Option<Atom> {
None None
} }

View file

@ -227,8 +227,8 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
fn set_bits(&mut self) { fn set_bits(&mut self) {
let display = self.style.get_box().clone_display(); let display = self.style.get_box().clone_display();
if !display.is_contents() && if !display.is_contents() {
!self if !self
.style .style
.get_text() .get_text()
.clone_text_decoration_line() .clone_text_decoration_line()
@ -238,6 +238,12 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
.add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); .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() { if self.style.is_pseudo_element() {
self.style self.style
.add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE); .add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
@ -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 /// If a <fieldset> has grid/flex display type, we need to inherit
/// this type into its ::-moz-fieldset-content anonymous box. /// this type into its ::-moz-fieldset-content anonymous box.
/// ///
@ -496,9 +531,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// normal cascading process. /// normal cascading process.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) { fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
match self.style.pseudo { use crate::selector_parser::PseudoElement;
Some(ref p) if p.is_fieldset_content() => {},
_ => return, if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
return;
} }
debug_assert_eq!(self.style.get_box().clone_display(), Display::Block); 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_prohibited_display_contents(element);
self.adjust_for_fieldset_content(layout_parent_style); 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.adjust_for_top_layer();
self.blockify_if_necessary(layout_parent_style, element); self.blockify_if_necessary(layout_parent_style, element);

View file

@ -294,7 +294,9 @@ where
// Removing sheets makes us tear down the whole cascade and invalidation // 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. // 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 // 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. // See bug 1434756.
if sheet.committed { if sheet.committed {
self.set_data_validity_at_least(DataValidity::FullyInvalid); self.set_data_validity_at_least(DataValidity::FullyInvalid);

View file

@ -58,8 +58,8 @@ pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};
pub use self::rules_iterator::{AllRules, EffectiveRules}; pub use self::rules_iterator::{AllRules, EffectiveRules};
pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator}; pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
pub use self::style_rule::StyleRule; pub use self::style_rule::StyleRule;
pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
pub use self::stylesheet::{SanitizationData, SanitizationKind};
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
pub use self::supports_rule::SupportsRule; pub use self::supports_rule::SupportsRule;
pub use self::viewport_rule::ViewportRule; pub use self::viewport_rule::ViewportRule;
@ -369,6 +369,7 @@ impl CssRule {
shared_lock: &SharedRwLock, shared_lock: &SharedRwLock,
state: State, state: State,
loader: Option<&dyn StylesheetLoader>, loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<Self, RulesMutateError> { ) -> Result<Self, RulesMutateError> {
let url_data = parent_stylesheet_contents.url_data.read(); let url_data = parent_stylesheet_contents.url_data.read();
let context = ParserContext::new( let context = ParserContext::new(
@ -395,6 +396,7 @@ impl CssRule {
dom_error: None, dom_error: None,
namespaces: &mut *guard, namespaces: &mut *guard,
insert_rule_context: Some(insert_rule_context), insert_rule_context: Some(insert_rule_context),
allow_import_rules,
}; };
parse_one_rule(&mut input, &mut rule_parser) parse_one_rule(&mut input, &mut rule_parser)

View file

@ -10,7 +10,7 @@ use crate::str::CssStringWriter;
use crate::stylesheets::loader::StylesheetLoader; use crate::stylesheets::loader::StylesheetLoader;
use crate::stylesheets::rule_parser::{InsertRuleContext, State}; use crate::stylesheets::rule_parser::{InsertRuleContext, State};
use crate::stylesheets::stylesheet::StylesheetContents; use crate::stylesheets::stylesheet::StylesheetContents;
use crate::stylesheets::{CssRule, RulesMutateError}; use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
use servo_arc::{Arc, RawOffsetArc}; use servo_arc::{Arc, RawOffsetArc};
@ -128,6 +128,7 @@ pub trait CssRulesHelpers {
index: usize, index: usize,
nested: bool, nested: bool,
loader: Option<&dyn StylesheetLoader>, loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError>; ) -> Result<CssRule, RulesMutateError>;
} }
@ -140,6 +141,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
index: usize, index: usize,
nested: bool, nested: bool,
loader: Option<&dyn StylesheetLoader>, loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError> { ) -> Result<CssRule, RulesMutateError> {
let new_rule = { let new_rule = {
let read_guard = lock.read(); let read_guard = lock.read();
@ -176,6 +178,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
lock, lock,
state, state,
loader, loader,
allow_import_rules,
)? )?
}; };

View file

@ -19,6 +19,7 @@ use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::stylesheet::Namespaces; use crate::stylesheets::stylesheet::Namespaces;
use crate::stylesheets::supports_rule::SupportsCondition; use crate::stylesheets::supports_rule::SupportsCondition;
use crate::stylesheets::viewport_rule; use crate::stylesheets::viewport_rule;
use crate::stylesheets::AllowImportRules;
use crate::stylesheets::{CorsMode, DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; use crate::stylesheets::{CorsMode, DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
use crate::stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader}; use crate::stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader};
use crate::stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule}; 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 /// This won't contain any namespaces, and only nested parsers created with
/// `ParserContext::new_with_rule_type` will. /// `ParserContext::new_with_rule_type` will.
pub context: ParserContext<'a>, pub context: ParserContext<'a>,
/// The current state of the parser. /// The current stajkj/te of the parser.
pub state: State, pub state: State,
/// Whether we have tried to parse was invalid due to being in the wrong /// 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 /// 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, pub namespaces: &'a mut Namespaces,
/// The info we need insert a rule in a list. /// The info we need insert a rule in a list.
pub insert_rule_context: Option<InsertRuleContext<'a>>, pub insert_rule_context: Option<InsertRuleContext<'a>>,
/// Whether @import rules will be allowed.
pub allow_import_rules: AllowImportRules,
} }
impl<'b> TopLevelRuleParser<'b> { impl<'b> TopLevelRuleParser<'b> {
@ -189,6 +192,10 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) 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 // FIXME(emilio): We should always be able to have a loader
// around! See bug 1533783. // around! See bug 1533783.
if self.loader.is_none() { 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 media = Arc::new(self.shared_lock.wrap(media));
let prelude = AtRuleNonBlockPrelude::Import(url, media); let prelude = AtRuleNonBlockPrelude::Import(url, media);
return Ok(AtRuleType::WithoutBlock(prelude)); return Ok(AtRuleType::WithoutBlock(prelude));
}, },
"namespace" => { "namespace" => {

View file

@ -81,6 +81,7 @@ impl StylesheetContents {
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
line_number_offset: u32, line_number_offset: u32,
use_counters: Option<&UseCounters>, use_counters: Option<&UseCounters>,
allow_import_rules: AllowImportRules,
sanitization_data: Option<&mut SanitizationData>, sanitization_data: Option<&mut SanitizationData>,
) -> Self { ) -> Self {
let namespaces = RwLock::new(Namespaces::default()); let namespaces = RwLock::new(Namespaces::default());
@ -95,6 +96,7 @@ impl StylesheetContents {
quirks_mode, quirks_mode,
line_number_offset, line_number_offset,
use_counters, use_counters,
allow_import_rules,
sanitization_data, sanitization_data,
); );
@ -355,6 +357,16 @@ pub enum SanitizationKind {
NoConditionalRules, 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 { impl SanitizationKind {
fn allows(self, rule: &CssRule) -> bool { fn allows(self, rule: &CssRule) -> bool {
debug_assert_ne!(self, SanitizationKind::None); debug_assert_ne!(self, SanitizationKind::None);
@ -415,6 +427,7 @@ impl Stylesheet {
stylesheet_loader: Option<&dyn StylesheetLoader>, stylesheet_loader: Option<&dyn StylesheetLoader>,
error_reporter: Option<&dyn ParseErrorReporter>, error_reporter: Option<&dyn ParseErrorReporter>,
line_number_offset: u32, line_number_offset: u32,
allow_import_rules: AllowImportRules,
) { ) {
let namespaces = RwLock::new(Namespaces::default()); let namespaces = RwLock::new(Namespaces::default());
@ -430,6 +443,7 @@ impl Stylesheet {
existing.contents.quirks_mode, existing.contents.quirks_mode,
line_number_offset, line_number_offset,
/* use_counters = */ None, /* use_counters = */ None,
allow_import_rules,
/* sanitization_data = */ None, /* sanitization_data = */ None,
); );
@ -457,6 +471,7 @@ impl Stylesheet {
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
line_number_offset: u32, line_number_offset: u32,
use_counters: Option<&UseCounters>, use_counters: Option<&UseCounters>,
allow_import_rules: AllowImportRules,
mut sanitization_data: Option<&mut SanitizationData>, mut sanitization_data: Option<&mut SanitizationData>,
) -> (Vec<CssRule>, Option<String>, Option<String>) { ) -> (Vec<CssRule>, Option<String>, Option<String>) {
let mut rules = Vec::new(); let mut rules = Vec::new();
@ -481,6 +496,7 @@ impl Stylesheet {
dom_error: None, dom_error: None,
insert_rule_context: None, insert_rule_context: None,
namespaces, namespaces,
allow_import_rules,
}; };
{ {
@ -537,6 +553,7 @@ impl Stylesheet {
error_reporter: Option<&dyn ParseErrorReporter>, error_reporter: Option<&dyn ParseErrorReporter>,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
line_number_offset: u32, line_number_offset: u32,
allow_import_rules: AllowImportRules,
) -> Self { ) -> Self {
// FIXME: Consider adding use counters to Servo? // FIXME: Consider adding use counters to Servo?
let contents = StylesheetContents::from_str( let contents = StylesheetContents::from_str(
@ -549,6 +566,7 @@ impl Stylesheet {
quirks_mode, quirks_mode,
line_number_offset, line_number_offset,
/* use_counters = */ None, /* use_counters = */ None,
allow_import_rules,
/* sanitized_output = */ None, /* sanitized_output = */ None,
); );

View file

@ -684,7 +684,7 @@ fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
where where
E: TElement, E: TElement,
{ {
use crate::values::generics::image::{GenericImageLayer, Image}; use crate::values::generics::image::Image;
use style_traits::ToCss; use style_traits::ToCss;
// We speculatively evaluate any paint worklets during styling. // We speculatively evaluate any paint worklets during styling.
@ -694,9 +694,7 @@ where
if let Some(ref values) = data.styles.primary { if let Some(ref values) = data.styles.primary {
for image in &values.get_background().background_image.0 { for image in &values.get_background().background_image.0 {
let (name, arguments) = match *image { let (name, arguments) = match *image {
GenericImageLayer::Image(Image::PaintWorklet(ref worklet)) => { Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
(&worklet.name, &worklet.arguments)
},
_ => continue, _ => continue,
}; };
let painter = match context.shared.registered_speculative_painters.get(name) { let painter = match context.shared.registered_speculative_painters.get(name) {

View file

@ -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,
))
}
}

View file

@ -23,7 +23,6 @@ pub mod color;
pub mod effects; pub mod effects;
mod font; mod font;
mod grid; mod grid;
mod length;
mod svg; mod svg;
pub mod transform; pub mod transform;
@ -109,9 +108,6 @@ pub fn animate_multiplicative_factor(
/// If a variant is annotated with `#[animation(error)]`, the corresponding /// If a variant is annotated with `#[animation(error)]`, the corresponding
/// `match` arm returns an error. /// `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 /// Trait bounds for type parameter `Foo` can be opted out of with
/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
/// fields can be opted into with `#[animation(field_bound)]` on the field. /// 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> impl<T> ToAnimatedZero for crate::OwnedSlice<T>
where where
T: ToAnimatedZero, T: ToAnimatedZero,

View file

@ -14,11 +14,11 @@ use crate::values::generics::basic_shape as generic;
/// A computed alias for FillRule. /// A computed alias for FillRule.
pub use crate::values::generics::basic_shape::FillRule; pub use crate::values::generics::basic_shape::FillRule;
/// A computed clipping shape. /// A computed `clip-path` value.
pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>; pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
/// A computed float area shape. /// A computed `shape-outside` value.
pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>; pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
/// A computed basic shape. /// A computed basic shape.
pub type BasicShape = generic::GenericBasicShape< pub type BasicShape = generic::GenericBasicShape<

View file

@ -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))] #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf))]
/// Specifies a prioritized list of font family names or generic family names. /// Specifies a prioritized list of font family names or generic family names.
pub struct FontFamily { 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))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
/// The name of a font family of choice /// The name of a font family of choice
pub struct FamilyName { 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))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
/// Font family names must either be given quoted as strings, /// Font family names must either be given quoted as strings,
/// or unquoted as a sequence of one or more identifiers. /// or unquoted as a sequence of one or more identifiers.
@ -285,7 +289,9 @@ pub enum FontFamilyNameSyntax {
Identifiers, 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))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
/// A set of faces that vary in weight, width or slope. /// A set of faces that vary in weight, width or slope.
pub enum SingleFontFamily { pub enum SingleFontFamily {
@ -301,15 +307,28 @@ pub enum SingleFontFamily {
/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
/// sSingleGenerics are updated as well. /// sSingleGenerics are updated as well.
#[derive( #[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))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(u8)] #[repr(u8)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum GenericFontFamily { pub enum GenericFontFamily {
/// No generic family specified, only for internal usage. /// No generic family specified, only for internal usage.
///
/// NOTE(emilio): Gecko code relies on this variant being zero.
#[css(skip)] #[css(skip)]
None, None = 0,
Serif, Serif,
SansSerif, SansSerif,
#[parse(aliases = "-moz-fixed")] #[parse(aliases = "-moz-fixed")]
@ -350,19 +369,22 @@ impl SingleFontFamily {
}; };
let mut value = first_ident.as_ref().to_owned(); let mut value = first_ident.as_ref().to_owned();
let mut serialize_quoted = value.contains(' ');
// These keywords are not allowed by themselves. // These keywords are not allowed by themselves.
// The only way this value can be valid with with another keyword. // The only way this value can be valid with with another keyword.
if reserved { if reserved {
let ident = input.expect_ident()?; let ident = input.expect_ident()?;
serialize_quoted = serialize_quoted || ident.contains(' ');
value.push(' '); value.push(' ');
value.push_str(&ident); value.push_str(&ident);
} }
while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
serialize_quoted = serialize_quoted || ident.contains(' ');
value.push(' '); value.push(' ');
value.push_str(&ident); 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. // For font family names which contains special white spaces, e.g.
// `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
// as identifiers correctly. Just mark them quoted so we don't need // as identifiers correctly. Just mark them quoted so we don't need
@ -422,16 +444,22 @@ impl SingleFontFamily {
} }
#[cfg(feature = "servo")] #[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 /// A list of SingleFontFamily
pub struct FontFamilyList(Box<[SingleFontFamily]>); pub struct FontFamilyList(Box<[SingleFontFamily]>);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[derive(Clone, Debug)] #[derive(Clone, Debug, ToComputedValue, ToResolvedValue)]
/// A list of SingleFontFamily /// A list of SingleFontFamily
pub enum FontFamilyList { pub enum FontFamilyList {
/// A strong reference to a Gecko SharedFontList object. /// 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. /// A font-family generic ID.
Generic(GenericFontFamily), Generic(GenericFontFamily),
} }
@ -675,7 +703,7 @@ pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
/// (see http://www.microsoft.com/typography/otspec/languagetags.htm). /// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)]
#[repr(C)] #[repr(C)]
pub struct FontLanguageOverride(u32); pub struct FontLanguageOverride(pub u32);
impl FontLanguageOverride { impl FontLanguageOverride {
#[inline] #[inline]
@ -686,10 +714,7 @@ impl FontLanguageOverride {
/// Returns this value as a `&str`, backed by `storage`. /// Returns this value as a `&str`, backed by `storage`.
#[inline] #[inline]
pub fn to_str(self, storage: &mut [u8; 4]) -> &str { pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
if self.0 == 0 {
return "normal";
}
*storage = u32::to_be_bytes(self.0); *storage = u32::to_be_bytes(self.0);
// Safe because we ensure it's ASCII during computing // Safe because we ensure it's ASCII during computing
let slice = if cfg!(debug_assertions) { let slice = if cfg!(debug_assertions) {
@ -730,10 +755,22 @@ impl ToCss for FontLanguageOverride {
where where
W: fmt::Write, W: fmt::Write,
{ {
if self.0 == 0 {
return dest.write_str("normal");
}
self.to_str(&mut [0; 4]).to_css(dest) 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")] #[cfg(feature = "gecko")]
impl From<FontLanguageOverride> for u32 { impl From<FontLanguageOverride> for u32 {
fn from(v: FontLanguageOverride) -> u32 { fn from(v: FontLanguageOverride) -> u32 {

View file

@ -13,7 +13,8 @@ use crate::values::computed::url::ComputedImageUrl;
use crate::values::computed::NumberOrPercentage; use crate::values::computed::NumberOrPercentage;
use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{Angle, Color, Context};
use crate::values::computed::{ 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::generics::image::{self as generic, GradientCompatMode};
use crate::values::specified::image::LineDirection as SpecifiedLineDirection; use crate::values::specified::image::LineDirection as SpecifiedLineDirection;
@ -22,9 +23,6 @@ use std::f32::consts::PI;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
/// A computed image layer.
pub type ImageLayer = generic::GenericImageLayer<Image>;
/// Computed values for an image according to CSS-IMAGES. /// Computed values for an image according to CSS-IMAGES.
/// <https://drafts.csswg.org/css-images/#image-values> /// <https://drafts.csswg.org/css-images/#image-values>
pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl>; pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl>;
@ -37,6 +35,8 @@ pub type Gradient = generic::GenericGradient<
NonNegativeLength, NonNegativeLength,
NonNegativeLengthPercentage, NonNegativeLengthPercentage,
Position, Position,
Angle,
AngleOrPercentage,
Color, Color,
>; >;
@ -57,15 +57,9 @@ pub enum LineDirection {
Corner(HorizontalPositionKeyword, VerticalPositionKeyword), 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(...)`. /// Computed values for `-moz-image-rect(...)`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>; pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>;
/// Empty enum on non-gecko /// Empty enum on non-gecko
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]

View file

@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat};
use crate::Zero; use crate::Zero;
use app_units::Au; use app_units::Au;
use std::fmt::{self, Write}; 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}; use style_traits::{CSSPixel, CssWriter, ToCss};
pub use super::image::Image; pub use super::image::Image;
@ -203,6 +203,7 @@ impl Size {
Serialize, Serialize,
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue,
ToResolvedValue, ToResolvedValue,
ToShmem, 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 { impl Mul<CSSFloat> for CSSPixelLength {
type Output = Self; type Output = Self;

View file

@ -25,15 +25,16 @@
//! our expectations. //! our expectations.
use super::{Context, Length, Percentage, ToComputedValue}; 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::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::NonNegative; use crate::values::generics::{calc, NonNegative};
use crate::values::specified::length::FontBaseSize; use crate::values::specified::length::FontBaseSize;
use crate::values::{specified, CSSFloat}; use crate::values::{specified, CSSFloat};
use crate::Zero; use crate::Zero;
use app_units::Au; use app_units::Au;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::values::specified::AllowedNumericType; use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -162,13 +163,20 @@ impl MallocSizeOf for LengthPercentage {
} }
/// An unpacked `<length-percentage>` that borrows the `calc()` variant. /// An unpacked `<length-percentage>` that borrows the `calc()` variant.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq, ToCss)]
enum Unpacked<'a> { enum Unpacked<'a> {
Calc(&'a CalcLengthPercentage), Calc(&'a CalcLengthPercentage),
Length(Length), Length(Length),
Percentage(Percentage), 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 /// An unpacked `<length-percentage>` that owns the `calc()` variant, for
/// serialization purposes. /// serialization purposes.
#[derive(Deserialize, PartialEq, Serialize)] #[derive(Deserialize, PartialEq, Serialize)]
@ -185,6 +193,22 @@ impl LengthPercentage {
Self::new_length(Length::new(1.)) 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. /// Constructs a length value.
#[inline] #[inline]
pub fn new_length(length: Length) -> Self { pub fn new_length(length: Length) -> Self {
@ -211,25 +235,46 @@ impl LengthPercentage {
percent 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. /// Constructs a `calc()` value.
#[inline] #[inline]
pub fn new_calc( pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
length: Length, node.simplify_and_sort();
percentage: Option<Percentage>,
clamping_mode: AllowedNumericType, match node {
) -> Self { CalcNode::Leaf(l) => {
let percentage = match percentage { return match l {
Some(p) => p, CalcLengthPercentageLeaf::Length(l) => {
None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))), Self::new_length(Length::new(clamping_mode.clamp(l.px())))
}; },
if length.is_zero() { CalcLengthPercentageLeaf::Percentage(p) => {
return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0))); Self::new_percent(Percentage(clamping_mode.clamp(p.0)))
},
} }
Self::new_calc_unchecked(Box::new(CalcLengthPercentage { },
length, _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
percentage,
clamping_mode, clamping_mode,
})) node,
})),
}
} }
/// Private version of new_calc() that constructs a calc() variant without /// Private version of new_calc() that constructs a calc() variant without
@ -262,7 +307,18 @@ impl LengthPercentage {
LengthPercentageUnion::TAG_CALC => Tag::Calc, LengthPercentageUnion::TAG_CALC => Tag::Calc,
LengthPercentageUnion::TAG_LENGTH => Tag::Length, LengthPercentageUnion::TAG_LENGTH => Tag::Length,
LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage, 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() { match self.unpack() {
Unpacked::Length(l) => l.px() == 0.0, Unpacked::Length(l) => l.px() == 0.0,
Unpacked::Percentage(p) => p.0 == 0.0, Unpacked::Percentage(p) => p.0 == 0.0,
Unpacked::Calc(ref c) => { Unpacked::Calc(..) => false,
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,
} }
} }
@ -407,25 +413,8 @@ impl LengthPercentage {
#[inline] #[inline]
pub fn to_percentage(&self) -> Option<Percentage> { pub fn to_percentage(&self) -> Option<Percentage> {
match self.unpack() { match self.unpack() {
Unpacked::Length(..) => None,
Unpacked::Percentage(p) => Some(p), Unpacked::Percentage(p) => Some(p),
Unpacked::Calc(ref c) => { Unpacked::Length(..) | Unpacked::Calc(..) => None,
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)
},
} }
} }
@ -452,19 +441,22 @@ impl LengthPercentage {
/// the height property), they apply whenever a calc() expression contains /// the height property), they apply whenever a calc() expression contains
/// percentages. /// percentages.
pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> { pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
if self.has_percentage() { if let Unpacked::Length(l) = self.unpack() {
return Some(self.resolve(container_len?)); return Some(l);
} }
Some(self.length()) Some(self.resolve(container_len?))
} }
/// Returns the clamped non-negative values. /// Returns the clamped non-negative values.
#[inline] #[inline]
pub fn clamp_to_non_negative(&self) -> Self { pub fn clamp_to_non_negative(mut self) -> Self {
match self.unpack() { match self.unpack_mut() {
Unpacked::Length(l) => Self::new_length(l.clamp_to_non_negative()), UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),
Unpacked::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()), UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
Unpacked::Calc(c) => c.clamp_to_non_negative(), UnpackedMut::Calc(ref mut c) => {
c.clamping_mode = AllowedNumericType::NonNegative;
self
},
} }
} }
} }
@ -549,7 +541,7 @@ impl ToCss for LengthPercentage {
where where
W: Write, 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 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),
}
impl CalcLengthPercentageLeaf {
fn is_zero_length(&self) -> bool {
match *self {
Self::Length(ref l) => l.is_zero(),
Self::Percentage(..) => false,
}
}
}
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 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.,
}
}
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(())
}
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,
}
}
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. /// The representation of a calc() function with mixed lengths and percentages.
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)] #[derive(
Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss,
)]
#[repr(C)] #[repr(C)]
pub struct CalcLengthPercentage { pub struct CalcLengthPercentage {
length: Length,
percentage: Percentage,
#[animation(constant)] #[animation(constant)]
#[css(skip)]
clamping_mode: AllowedNumericType, clamping_mode: AllowedNumericType,
node: CalcNode,
} }
impl CalcLengthPercentage { 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()))
}
/// Resolves the percentage. /// Resolves the percentage.
#[inline] #[inline]
pub fn resolve(&self, basis: Length) -> Length { fn resolve(&self, basis: Length) -> Length {
let length = self.length.px() + basis.px() * self.percentage.0; // unwrap() is fine because the conversion below is infallible.
Length::new(self.clamping_mode.clamp(length)) let px = self
} .node
.resolve(|l| {
/// Returns the length, without clamping. Ok(match *l {
#[inline] CalcLengthPercentageLeaf::Length(l) => l.px(),
fn unclamped_length(&self) -> Length { CalcLengthPercentageLeaf::Percentage(ref p) => basis.px() * p.0,
self.length })
} })
.unwrap();
/// Returns the clamped non-negative values. Length::new(self.clamping_mode.clamp(px))
#[inline]
fn clamp_to_non_negative(&self) -> LengthPercentage {
LengthPercentage::new_calc(
self.length,
Some(self.percentage),
AllowedNumericType::NonNegative,
)
} }
} }
@ -641,7 +725,7 @@ impl CalcLengthPercentage {
// maybe. // maybe.
impl PartialEq for CalcLengthPercentage { impl PartialEq for CalcLengthPercentage {
fn eq(&self, other: &Self) -> bool { 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 where
F: Fn(Length) -> Length, F: Fn(Length) -> Length,
{ {
use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength}; use crate::values::specified::calc::Leaf;
use std::f32; use crate::values::specified::length::NoCalcLength;
let mut length = 0.; 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")
},
});
if let Some(absolute) = self.absolute { LengthPercentage::new_calc(node, self.clamping_mode)
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();
}
}
LengthPercentage::new_calc(
Length::new(length.min(f32::MAX).max(f32::MIN)),
self.percentage,
self.clamping_mode,
)
} }
/// Compute font-size or line-height taking into account text-zoom if necessary. /// 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, /// Compute the value into pixel length as CSSFloat without context,
/// so it returns Err(()) if there is any non-absolute unit. /// so it returns Err(()) if there is any non-absolute unit.
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> { pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
if self.vw.is_some() || use crate::values::specified::calc::Leaf;
self.vh.is_some() || use crate::values::specified::length::NoCalcLength;
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 { // Simplification should've turned this into an absolute length,
Some(abs) => Ok(abs.to_px()), // otherwise it wouldn't have been able to.
None => { match self.node {
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self); calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
Err(()) _ => Err(()),
},
} }
} }
@ -740,17 +792,51 @@ impl specified::CalcLengthPercentage {
#[inline] #[inline]
fn from_computed_value(computed: &CalcLengthPercentage) -> Self { 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 { specified::CalcLengthPercentage {
clamping_mode: computed.clamping_mode, clamping_mode: computed.clamping_mode,
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)), node: computed.node.map_leaves(|l| match l {
percentage: Some(computed.percentage), CalcLengthPercentageLeaf::Length(ref l) => {
..Default::default() 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. /// A wrapper of LengthPercentage, whose value must be >= 0.
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>; pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;

View file

@ -21,10 +21,9 @@ use crate::media_queries::Device;
use crate::properties; use crate::properties;
use crate::properties::{ComputedValues, LonghandId, StyleBuilder}; use crate::properties::{ComputedValues, LonghandId, StyleBuilder};
use crate::rule_cache::RuleCacheConditions; use crate::rule_cache::RuleCacheConditions;
use crate::Atom; use crate::{ArcSlice, Atom};
#[cfg(feature = "servo")]
use crate::Prefix;
use euclid::default::Size2D; use euclid::default::Size2D;
use servo_arc::Arc;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp; use std::cmp;
use std::f32; 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::{FontVariantAlternates, FontWeight};
pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontVariantEastAsian, FontVariationSettings};
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; 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::{CSSPixelLength, ExtremumLength, NonNegativeLength};
pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; 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::TextUnderlinePosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing}; 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::text::{TextDecorationLength, TextDecorationSkipInk};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; 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!(());
trivial_to_computed_value!(bool); trivial_to_computed_value!(bool);
trivial_to_computed_value!(f32); trivial_to_computed_value!(f32);
@ -460,10 +499,13 @@ trivial_to_computed_value!(u32);
trivial_to_computed_value!(usize); trivial_to_computed_value!(usize);
trivial_to_computed_value!(Atom); trivial_to_computed_value!(Atom);
#[cfg(feature = "servo")] #[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!(String);
trivial_to_computed_value!(Box<str>); trivial_to_computed_value!(Box<str>);
trivial_to_computed_value!(crate::OwnedStr); trivial_to_computed_value!(crate::OwnedStr);
trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(

View file

@ -9,6 +9,7 @@
use crate::values::computed::{Integer, LengthPercentage, Percentage}; use crate::values::computed::{Integer, LengthPercentage, Percentage};
use crate::values::generics::position::Position as GenericPosition; 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::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex; use crate::values::generics::position::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas}; 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. /// A computed value for the `z-index` property.
pub type ZIndex = GenericZIndex<Integer>; pub type ZIndex = GenericZIndex<Integer>;

View file

@ -31,7 +31,7 @@ impl SVGPaint {
} }
/// <length> | <percentage> | <number> | context-value /// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<LengthPercentage>; pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
impl SVGLength { impl SVGLength {
/// `0px` /// `0px`
@ -41,7 +41,7 @@ impl SVGLength {
} }
/// An non-negative wrapper of SVGLength. /// An non-negative wrapper of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>; pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
impl SVGWidth { impl SVGWidth {
/// `1px`. /// `1px`.

View file

@ -18,8 +18,7 @@ use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::TextAlignKeyword as TextAlign; pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition};
pub use crate::values::specified::TextUnderlinePosition;
pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak}; pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak};
pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; pub use crate::values::specified::{TextDecorationSkipInk, TextTransform};
@ -30,6 +29,9 @@ pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
/// Implements type for `text-decoration-thickness` property. /// Implements type for `text-decoration-thickness` property.
pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>; pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
/// The computed value of `text-align`.
pub type TextAlign = specified::TextAlignKeyword;
/// A computed value for the `letter-spacing` property. /// A computed value for the `letter-spacing` property.
#[repr(transparent)] #[repr(transparent)]
#[derive( #[derive(

View file

@ -13,10 +13,10 @@ pub use crate::values::specified::ui::CursorKind;
pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect}; pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
/// A computed value for the `cursor` property. /// 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`. /// 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. /// A computed value for `scrollbar-color` property.
pub type ScrollbarColor = generics::GenericScrollbarColor<Color>; pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;

View file

@ -19,9 +19,6 @@ use std::ops::Add;
/// If a variant is annotated with `#[animation(error)]`, the corresponding /// If a variant is annotated with `#[animation(error)]`, the corresponding
/// `match` arm returns an error. /// `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 /// Trait bounds for type parameter `Foo` can be opted out of with
/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
/// fields can be opted into with `#[distance(field_bound)]` on the field. /// 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> impl<T> ComputeSquaredDistance for Option<T>
where where
T: ComputeSquaredDistance, T: ComputeSquaredDistance,

View file

@ -15,18 +15,17 @@ use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; 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> /// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(
Animate, Animate,
Clone, Clone,
ComputeSquaredDistance,
Copy, Copy,
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
Parse,
SpecifiedValueInfo, SpecifiedValueInfo,
ToAnimatedValue, ToAnimatedValue,
ToComputedValue, ToComputedValue,
@ -34,15 +33,27 @@ pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, U
ToResolvedValue, ToResolvedValue,
ToShmem, 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, FillBox,
StrokeBox, StrokeBox,
ViewBox, ViewBox,
ShapeBox(ShapeBox), ShapeBox(ShapeBox),
} }
/// A float area shape, for `shape-outside`. impl Default for ShapeGeometryBox {
pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>; fn default() -> Self {
Self::ElementDependent
}
}
/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
#[allow(missing_docs)] #[allow(missing_docs)]
@ -51,6 +62,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I
Animate, Animate,
Clone, Clone,
Copy, Copy,
ComputeSquaredDistance,
Debug, Debug,
Eq, Eq,
MallocSizeOf, MallocSizeOf,
@ -63,6 +75,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(u8)]
pub enum ShapeBox { pub enum ShapeBox {
MarginBox, MarginBox,
BorderBox, BorderBox,
@ -70,12 +83,19 @@ pub enum ShapeBox {
ContentBox, 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)] #[allow(missing_docs)]
#[animation(no_bound(ImageOrUrl))] #[animation(no_bound(U))]
#[derive( #[derive(
Animate, Animate,
Clone, Clone,
ComputeSquaredDistance,
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
@ -86,18 +106,54 @@ pub enum ShapeBox {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> { #[repr(u8)]
#[animation(error)] pub enum GenericClipPath<BasicShape, U> {
ImageOrUrl(ImageOrUrl),
Shape(Box<BasicShape>, Option<ReferenceBox>),
#[animation(error)]
Box(ReferenceBox),
#[css(function)]
Path(Path),
#[animation(error)] #[animation(error)]
None, None,
#[animation(error)]
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)] #[allow(missing_docs)]
#[derive( #[derive(
Animate, Animate,
@ -252,7 +308,7 @@ pub use self::GenericShapeRadius as ShapeRadius;
#[repr(C)] #[repr(C)]
pub struct GenericPolygon<LengthPercentage> { pub struct GenericPolygon<LengthPercentage> {
/// The filling rule for a polygon. /// The filling rule for a polygon.
#[css(skip_if = "fill_is_default")] #[css(skip_if = "is_default")]
pub fill: FillRule, pub fill: FillRule,
/// A collection of (x, y) coordinates to draw the polygon. /// A collection of (x, y) coordinates to draw the polygon.
#[css(iterable)] #[css(iterable)]
@ -311,6 +367,7 @@ pub enum FillRule {
#[derive( #[derive(
Animate, Animate,
Clone, Clone,
ComputeSquaredDistance,
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
@ -321,39 +378,23 @@ pub enum FillRule {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)]
pub struct Path { pub struct Path {
/// The filling rule for the svg path. /// The filling rule for the svg path.
#[css(skip_if = "fill_is_default")] #[css(skip_if = "is_default")]
#[animation(constant)] #[animation(constant)]
pub fill: FillRule, pub fill: FillRule,
/// The svg path data. /// The svg path data.
pub path: SVGPathData, pub path: SVGPathData,
} }
// FIXME(nox): Implement ComputeSquaredDistance for T types and stop impl<B, U> ToAnimatedZero for ClipPath<B, U> {
// using PartialEq here, this will let us derive this impl. fn to_animated_zero(&self) -> Result<Self, ()> {
impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U> Err(())
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, T, U> ToAnimatedZero for ShapeSource<B, T, U> { impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
fn to_animated_zero(&self) -> Result<Self, ()> { fn to_animated_zero(&self) -> Result<Self, ()> {
Err(()) Err(())
} }
@ -488,6 +529,6 @@ impl Default for FillRule {
} }
#[inline] #[inline]
fn fill_is_default(fill: &FillRule) -> bool { fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
*fill == FillRule::default() *fill == Default::default()
} }

View file

@ -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(&center) {
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(&center) {
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)
}
}

View file

@ -655,8 +655,10 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
pub struct LineNameList { pub struct LineNameList {
/// The optional `<line-name-list>` /// The optional `<line-name-list>`
pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>, pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
/// Indicates the line name that requires `auto-fill`, if in bounds. /// Indicates the starting line names that requires `auto-fill`, if in bounds.
pub fill_idx: usize, pub fill_start: usize,
/// Indicates the number of line names in the auto-fill
pub fill_len: usize,
} }
impl Parse for LineNameList { impl Parse for LineNameList {
@ -666,7 +668,7 @@ impl Parse for LineNameList {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
input.expect_ident_matching("subgrid")?; input.expect_ident_matching("subgrid")?;
let mut line_names = vec![]; let mut line_names = vec![];
let mut fill_idx = None; let mut fill_data = None;
loop { loop {
let repeat_parse_result = input.try(|input| { let repeat_parse_result = input.try(|input| {
@ -682,8 +684,7 @@ impl Parse for LineNameList {
Ok((names_list, count)) Ok((names_list, count))
}) })
}); });
if let Ok((names_list, count)) = repeat_parse_result {
if let Ok((mut names_list, count)) = repeat_parse_result {
match count { match count {
// FIXME(emilio): we shouldn't expand repeat() at // FIXME(emilio): we shouldn't expand repeat() at
// parse time for subgrid. (bug 1583429) // parse time for subgrid. (bug 1583429)
@ -694,19 +695,11 @@ impl Parse for LineNameList {
.cycle() .cycle()
.take(num.value() as usize * names_list.len()), .take(num.value() as usize * names_list.len()),
), ),
RepeatCount::AutoFill if fill_idx.is_none() => { RepeatCount::AutoFill if fill_data.is_none() => {
// `repeat(autof-fill, ..)` should have just one line name. let fill_idx = line_names.len();
// FIXME(bug 1341507) the above comment is wrong per: let fill_len = names_list.len();
// https://drafts.csswg.org/css-grid-2/#typedef-name-repeat fill_data = Some((fill_idx, fill_len));
if names_list.len() != 1 { line_names.extend(names_list.into_iter());
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);
}, },
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
@ -721,9 +714,12 @@ impl Parse for LineNameList {
line_names.truncate(MAX_GRID_LINE as usize); line_names.truncate(MAX_GRID_LINE as usize);
} }
let (fill_start, fill_len) = fill_data.unwrap_or((usize::MAX, 0));
Ok(LineNameList { Ok(LineNameList {
names: line_names.into(), 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, W: Write,
{ {
dest.write_str("subgrid")?; 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() { for (i, names) in self.names.iter().enumerate() {
if i == fill_idx { if i == fill_start {
dest.write_str(" repeat(auto-fill,")?; dest.write_str(" repeat(auto-fill,")?;
} }
@ -751,7 +748,7 @@ impl ToCss for LineNameList {
} }
dest.write_str("]")?; dest.write_str("]")?;
if i == fill_idx { if i == fill_start + fill_len - 1 {
dest.write_str(")")?; dest.write_str(")")?;
} }
} }

View file

@ -9,55 +9,28 @@
use crate::custom_properties; use crate::custom_properties;
use crate::values::serialize_atom_identifier; use crate::values::serialize_atom_identifier;
use crate::Atom; use crate::Atom;
use crate::Zero;
use servo_arc::Arc; use servo_arc::Arc;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
use values::generics::position::PositionComponent;
/// An <image> | <none> (for background-image, for example). /// An `<image> | none` value.
#[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].
/// ///
/// [image]: https://drafts.csswg.org/css-images/#image-values /// https://drafts.csswg.org/css-images/#image-values
#[derive( #[derive(
Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
)] )]
#[repr(C, u8)] #[repr(C, u8)]
pub enum GenericImage<Gradient, MozImageRect, ImageUrl> { pub enum GenericImage<G, MozImageRect, ImageUrl> {
/// `none` variant.
None,
/// A `<url()>` image. /// A `<url()>` image.
Url(ImageUrl), Url(ImageUrl),
/// A `<gradient>` image. Gradients are rather large, and not nearly as /// 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. /// 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. /// A `-moz-image-rect` image. Also fairly large and rare.
// not cfged out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used` // not cfged out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used`
// Instead we make MozImageRect an empty enum // Instead we make MozImageRect an empty enum
@ -80,27 +53,51 @@ pub use self::GenericImage as Image;
/// <https://drafts.csswg.org/css-images/#gradients> /// <https://drafts.csswg.org/css-images/#gradients>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(C)] #[repr(C)]
pub struct GenericGradient< pub enum GenericGradient<
LineDirection, LineDirection,
LengthPercentage, LengthPercentage,
NonNegativeLength, NonNegativeLength,
NonNegativeLengthPercentage, NonNegativeLengthPercentage,
Position, Position,
Angle,
AngleOrPercentage,
Color, Color,
> { > {
/// Gradients can be linear or radial. /// A linear gradient.
pub kind: GenericGradientKind< Linear {
LineDirection, /// Line direction
NonNegativeLength, direction: LineDirection,
NonNegativeLengthPercentage,
Position,
>,
/// The color stops and interpolation hints. /// The color stops and interpolation hints.
pub items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>, items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
/// True if this is a repeating gradient. /// True if this is a repeating gradient.
pub repeating: bool, repeating: bool,
/// Compatibility mode. /// Compatibility mode.
pub compat_mode: GradientCompatMode, 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; pub use self::GenericGradient as Gradient;
@ -117,26 +114,6 @@ pub enum GradientCompatMode {
Moz, 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. /// A radial gradient's ending shape.
#[derive( #[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, 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, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)] )]
#[repr(C, u8)] #[repr(C, u8)]
pub enum GenericGradientItem<Color, LengthPercentage> { pub enum GenericGradientItem<Color, T> {
/// A simple color stop, without position. /// A simple color stop, without position.
SimpleColorStop(Color), SimpleColorStop(Color),
/// A complex color stop, with a position. /// A complex color stop, with a position.
@ -217,10 +194,10 @@ pub enum GenericGradientItem<Color, LengthPercentage> {
/// The color for the stop. /// The color for the stop.
color: Color, color: Color,
/// The position for the stop. /// The position for the stop.
position: LengthPercentage, position: T,
}, },
/// An interpolation hint. /// An interpolation hint.
InterpolationHint(LengthPercentage), InterpolationHint(T),
} }
pub use self::GenericGradientItem as GradientItem; pub use self::GenericGradientItem as GradientItem;
@ -230,17 +207,17 @@ pub use self::GenericGradientItem as GradientItem;
#[derive( #[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)] )]
pub struct ColorStop<Color, LengthPercentage> { pub struct ColorStop<Color, T> {
/// The color of this stop. /// The color of this stop.
pub color: Color, pub color: Color,
/// The position of this stop. /// 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`. /// Convert the color stop into an appropriate `GradientItem`.
#[inline] #[inline]
pub fn into_item(self) -> GradientItem<Color, LengthPercentage> { pub fn into_item(self) -> GradientItem<Color, T> {
match self.position { match self.position {
Some(position) => GradientItem::ComplexColorStop { Some(position) => GradientItem::ComplexColorStop {
color: self.color, color: self.color,
@ -261,6 +238,8 @@ pub struct PaintWorklet {
/// The arguments for the worklet. /// The arguments for the worklet.
/// TODO: store a parsed representation of the arguments. /// TODO: store a parsed representation of the arguments.
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
#[compute(no_field_bound)]
#[resolve(no_field_bound)]
pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>, pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
} }
@ -285,7 +264,7 @@ impl ToCss for PaintWorklet {
/// ///
/// `-moz-image-rect(<uri>, top, right, bottom, left);` /// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)] #[allow(missing_docs)]
#[css(comma, function)] #[css(comma, function = "-moz-image-rect")]
#[derive( #[derive(
Clone, Clone,
Debug, Debug,
@ -297,7 +276,8 @@ impl ToCss for PaintWorklet {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> { #[repr(C)]
pub struct GenericMozImageRect<NumberOrPercentage, MozImageRectUrl> {
pub url: MozImageRectUrl, pub url: MozImageRectUrl,
pub top: NumberOrPercentage, pub top: NumberOrPercentage,
pub right: NumberOrPercentage, pub right: NumberOrPercentage,
@ -305,6 +285,8 @@ pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
pub left: NumberOrPercentage, pub left: NumberOrPercentage,
} }
pub use self::GenericMozImageRect as MozImageRect;
impl<G, R, U> fmt::Debug for Image<G, R, U> impl<G, R, U> fmt::Debug for Image<G, R, U>
where where
G: ToCss, G: ToCss,
@ -327,6 +309,7 @@ where
W: Write, W: Write,
{ {
match *self { match *self {
Image::None => dest.write_str("none"),
Image::Url(ref url) => url.to_css(dest), Image::Url(ref url) => url.to_css(dest),
Image::Gradient(ref gradient) => gradient.to_css(dest), Image::Gradient(ref gradient) => gradient.to_css(dest),
Image::Rect(ref rect) => rect.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 where
D: LineDirection, D: LineDirection,
LP: ToCss, LP: ToCss,
NL: ToCss, NL: ToCss,
NLP: ToCss, NLP: ToCss,
P: ToCss, P: PositionComponent + ToCss,
A: ToCss,
AoP: ToCss,
C: ToCss, C: ToCss,
{ {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
W: Write, 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::WebKit => dest.write_str("-webkit-")?,
GradientCompatMode::Moz => dest.write_str("-moz-")?, GradientCompatMode::Moz => dest.write_str("-moz-")?,
_ => {}, _ => {},
} }
if self.repeating { if repeating {
dest.write_str("repeating-")?; dest.write_str("repeating-")?;
} }
dest.write_str(self.kind.label())?;
dest.write_str("-gradient(")?; match *self {
let mut skip_comma = match self.kind { Gradient::Linear {
GradientKind::Linear(ref direction) if direction.points_downwards(self.compat_mode) => { ref direction,
true ref items,
}, compat_mode,
GradientKind::Linear(ref direction) => { ..
direction.to_css(dest, self.compat_mode)?; } => {
dest.write_str("linear-gradient(")?;
let mut skip_comma = if !direction.points_downwards(compat_mode) {
direction.to_css(dest, compat_mode)?;
false false
},
GradientKind::Radial(ref shape, ref position) => {
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 {
if !omit_shape {
shape.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("at ")?;
position.to_css(dest)?;
} else { } else {
position.to_css(dest)?; true
if !omit_shape {
dest.write_str(", ")?;
shape.to_css(dest)?;
}
}
false
},
}; };
for item in &*self.items { for item in &**items {
if !skip_comma { if !skip_comma {
dest.write_str(", ")?; dest.write_str(", ")?;
} }
skip_comma = false; skip_comma = false;
item.to_css(dest)?; item.to_css(dest)?;
} }
},
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,
};
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)?;
}
let mut skip_comma = omit_angle && omit_position;
for item in &**items {
if !skip_comma {
dest.write_str(", ")?;
}
skip_comma = false;
item.to_css(dest)?;
}
},
}
dest.write_str(")") 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. /// The direction of a linear gradient.
pub trait LineDirection { pub trait LineDirection {
/// Whether this direction points towards, and thus can be omitted. /// Whether this direction points towards, and thus can be omitted.

View file

@ -19,6 +19,7 @@ pub mod basic_shape;
pub mod border; pub mod border;
#[path = "box.rs"] #[path = "box.rs"]
pub mod box_; pub mod box_;
pub mod calc;
pub mod color; pub mod color;
pub mod column; pub mod column;
pub mod counters; pub mod counters;

View file

@ -31,6 +31,17 @@ pub struct GenericPosition<H, V> {
pub vertical: 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; pub use self::GenericPosition as Position;
impl<H, V> Position<H, V> { 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>`. /// A generic type for representing an `Auto | <position>`.
/// This is used by <offset-anchor> for now. /// This is used by <offset-anchor> for now.
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property /// https://drafts.fxtf.org/motion-1/#offset-anchor-property

View file

@ -152,7 +152,8 @@ impl<C: Parse, U: Parse> Parse for SVGPaint<C, U> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub enum SVGLength<L> { #[repr(C, u8)]
pub enum GenericSVGLength<L> {
/// `<length> | <percentage> | <number>` /// `<length> | <percentage> | <number>`
LengthPercentage(L), LengthPercentage(L),
/// `context-value` /// `context-value`
@ -160,6 +161,8 @@ pub enum SVGLength<L> {
ContextValue, ContextValue,
} }
pub use self::GenericSVGLength as SVGLength;
/// Generic value for stroke-dasharray. /// Generic value for stroke-dasharray.
#[derive( #[derive(
Clone, Clone,

View file

@ -354,7 +354,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage {
match *self { match *self {
Length(len) => len.to_computed_pixel_length_without_context(), Length(len) => len.to_computed_pixel_length_without_context(),
Calc(ref calc) => calc.to_computed_pixel_length_without_context(), Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
_ => Err(()), Percentage(..) => Err(()),
} }
} }
} }

View file

@ -21,19 +21,22 @@ use values::specified::ui::CursorKind;
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub struct Cursor<Image> { #[repr(C)]
pub struct GenericCursor<Image> {
/// The parsed images for the cursor. /// The parsed images for the cursor.
pub images: Box<[Image]>, pub images: crate::OwnedSlice<Image>,
/// The kind of the cursor [default | help | ...]. /// The kind of the cursor [default | help | ...].
pub keyword: CursorKind, pub keyword: CursorKind,
} }
pub use self::GenericCursor as Cursor;
impl<Image> Cursor<Image> { impl<Image> Cursor<Image> {
/// Set `cursor` to `auto` /// Set `cursor` to `auto`
#[inline] #[inline]
pub fn auto() -> Self { pub fn auto() -> Self {
Self { Self {
images: vec![].into_boxed_slice(), images: Default::default(),
keyword: CursorKind::Auto, keyword: CursorKind::Auto,
} }
} }
@ -63,24 +66,31 @@ impl<Image: ToCss> ToCss for Cursor<Image> {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
pub struct CursorImage<ImageUrl, Number> { #[repr(C)]
pub struct GenericCursorImage<ImageUrl, Number> {
/// The url to parse images from. /// The url to parse images from.
pub url: ImageUrl, pub url: ImageUrl,
/// The <x> and <y> coordinates. /// Whether the image has a hotspot or not.
pub hotspot: Option<(Number, Number)>, 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> { impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
W: Write, W: Write,
{ {
self.url.to_css(dest)?; self.url.to_css(dest)?;
if let Some((ref x, ref y)) = self.hotspot { if self.has_hotspot {
dest.write_str(" ")?; dest.write_str(" ")?;
x.to_css(dest)?; self.hotspot_x.to_css(dest)?;
dest.write_str(" ")?; dest.write_str(" ")?;
y.to_css(dest)?; self.hotspot_y.to_css(dest)?;
} }
Ok(()) Ok(())
} }

View file

@ -6,7 +6,9 @@
//! there are used values. //! there are used values.
use crate::properties::ComputedValues; use crate::properties::ComputedValues;
use crate::ArcSlice;
use cssparser; use cssparser;
use servo_arc::Arc;
use smallvec::SmallVec; use smallvec::SmallVec;
mod color; mod color;
@ -77,8 +79,11 @@ trivial_to_resolved_value!(computed::url::ComputedUrl);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
trivial_to_resolved_value!(computed::url::ComputedImageUrl); trivial_to_resolved_value!(computed::url::ComputedImageUrl);
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
trivial_to_resolved_value!(html5ever::Namespace);
#[cfg(feature = "servo")]
trivial_to_resolved_value!(html5ever::Prefix); trivial_to_resolved_value!(html5ever::Prefix);
trivial_to_resolved_value!(computed::LengthPercentage); trivial_to_resolved_value!(computed::LengthPercentage);
trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType);
impl<A, B> ToResolvedValue for (A, B) impl<A, B> ToResolvedValue for (A, B)
where where
@ -214,3 +219,43 @@ where
Self::from(Box::from_resolved_value(resolved.into_box())) 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
}
}

View file

@ -556,7 +556,7 @@ impl SpecifiedValueInfo for AlignItems {
/// Value of the `justify-items` property /// Value of the `justify-items` property
/// ///
/// <https://drafts.csswg.org/css-align/#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)] #[repr(C)]
pub struct JustifyItems(pub AlignFlags); pub struct JustifyItems(pub AlignFlags);

View file

@ -9,8 +9,7 @@
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::generics::basic_shape as generic; use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord}; use crate::values::generics::basic_shape::{Path, PolygonCoord};
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::specified::border::BorderRadius; use crate::values::specified::border::BorderRadius;
use crate::values::specified::image::Image; use crate::values::specified::image::Image;
@ -25,14 +24,14 @@ use style_traits::{ParseError, StyleParseErrorKind};
/// A specified alias for FillRule. /// A specified alias for FillRule.
pub use crate::values::generics::basic_shape::FillRule; pub use crate::values::generics::basic_shape::FillRule;
/// A specified clipping shape. /// A specified `clip-path` value.
pub type ClippingShape = generic::ClippingShape<BasicShape, SpecifiedUrl>; pub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;
/// A specified float area shape. /// A specified `shape-outside` value.
pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>; pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
/// A specified basic shape. /// A specified basic shape.
pub type BasicShape = generic::BasicShape< pub type BasicShape = generic::GenericBasicShape<
HorizontalPosition, HorizontalPosition,
VerticalPosition, VerticalPosition,
LengthPercentage, LengthPercentage,
@ -65,53 +64,16 @@ fn is_clip_path_path_enabled(_: &ParserContext) -> bool {
false false
} }
impl Parse for ClippingShape { /// A helper for both clip-path and shape-outside parsing of shapes.
#[inline] fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R,
if is_clip_path_path_enabled(context) { to_reference_box: impl FnOnce(ReferenceBox) -> R,
if let Ok(p) = input.try(|i| Path::parse(context, i)) { ) -> Result<R, ParseError<'i>>
return Ok(ShapeSource::Path(p));
}
}
if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
return Ok(ShapeSource::ImageOrUrl(url));
}
Self::parse_common(context, input)
}
}
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));
}
Self::parse_common(context, input)
}
}
impl<ReferenceBox, ImageOrUrl> ShapeSource<BasicShape, ReferenceBox, ImageOrUrl>
where where
ReferenceBox: Parse, ReferenceBox: Default + Parse,
{ {
/// The internal parser for ShapeSource.
fn parse_common<'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);
}
fn parse_component<U: Parse>( fn parse_component<U: Parse>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser, input: &mut Parser,
@ -135,30 +97,58 @@ where
} }
if let Some(shp) = shape { if let Some(shp) = shape {
return Ok(ShapeSource::Shape(Box::new(shp), ref_box)); return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default()));
} }
ref_box match ref_box {
.map(ShapeSource::Box) Some(r) => Ok(to_reference_box(r)),
.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
} }
impl Parse for GeometryBox { impl Parse for ClipPath {
#[inline]
fn parse<'i, 't>( fn parse<'i, 't>(
_context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
if let Ok(shape_box) = input.try(ShapeBox::parse) { if input.try(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(GeometryBox::ShapeBox(shape_box)); return Ok(ClipPath::None);
} }
try_match_ident_ignore_ascii_case! { input, if is_clip_path_path_enabled(context) {
"fill-box" => Ok(GeometryBox::FillBox), if let Ok(p) = input.try(|i| Path::parse(context, i)) {
"stroke-box" => Ok(GeometryBox::StrokeBox), return Ok(ClipPath::Path(p));
"view-box" => Ok(GeometryBox::ViewBox),
} }
} }
if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
return Ok(ClipPath::Url(url));
}
parse_shape_or_box(context, input, ClipPath::Shape, ClipPath::Box)
}
}
impl Parse for ShapeOutside {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
// 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);
}
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)
}
} }
impl Parse for BasicShape { impl Parse for BasicShape {

View file

@ -234,7 +234,18 @@ impl Parse for BorderSpacing {
#[allow(missing_docs)] #[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive( #[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 { pub enum BorderImageRepeatKeyword {
Stretch, Stretch,

View file

@ -104,6 +104,8 @@ pub enum DisplayInside {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozGridLine, MozGridLine,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozStack,
#[cfg(feature = "gecko")]
MozDeck, MozDeck,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozPopup, MozPopup,
@ -227,6 +229,8 @@ impl Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine); pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
#[cfg(feature = "gecko")] #[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); pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup); pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
@ -616,6 +620,8 @@ impl Parse for Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine, "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
#[cfg(feature = "gecko")] #[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, "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup, "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
@ -1595,7 +1601,7 @@ pub enum Appearance {
Meterchunk, Meterchunk,
/// The "arrowed" part of the dropdown button that open up a dropdown list. /// The "arrowed" part of the dropdown button that open up a dropdown list.
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMenulistButton, MozMenulistArrowButton,
/// For HTML's <input type=number> /// For HTML's <input type=number>
NumberInput, NumberInput,
/// A horizontal progress bar. /// A horizontal progress bar.
@ -1624,7 +1630,7 @@ pub enum Appearance {
RadioLabel, RadioLabel,
/// nsRangeFrame and its subparts /// nsRangeFrame and its subparts
Range, 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 resizer background area in a status bar for the resizer widget in
/// the corner of a window. /// the corner of a window.
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
@ -1850,6 +1856,14 @@ pub enum Appearance {
Count, 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. /// A kind of break between two boxes.
/// ///
/// https://drafts.csswg.org/css-break/#break-between /// https://drafts.csswg.org/css-break/#break-between

View file

@ -7,15 +7,16 @@
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
use crate::parser::ParserContext; 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::ViewportPercentageLength;
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength}; use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
use crate::values::specified::{self, Angle, Time}; use crate::values::specified::{self, Angle, Time};
use crate::values::{CSSFloat, CSSInteger}; use crate::values::{CSSFloat, CSSInteger};
use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token}; use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cmp;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::{cmp, mem};
use style_traits::values::specified::AllowedNumericType; use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
@ -32,40 +33,9 @@ pub enum MathFunction {
Clamp, Clamp,
} }
/// This determines the order in which we serialize members of a calc() /// A leaf node inside a `Calc` expression's AST.
/// sum. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
/// pub enum Leaf {
/// 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 {
/// `<length>` /// `<length>`
Length(NoCalcLength), Length(NoCalcLength),
/// `<angle>` /// `<angle>`
@ -76,27 +46,28 @@ pub enum CalcNode {
Percentage(CSSFloat), Percentage(CSSFloat),
/// `<number>` /// `<number>`
Number(CSSFloat), Number(CSSFloat),
/// An expression of the form `x + y + ...`. Subtraction is represented by }
/// the negated expression of the right hand side.
Sum(Box<[CalcNode]>), impl ToCss for Leaf {
/// A `min()` / `max()` function. fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
MinMax(Box<[CalcNode]>, MinMaxOp), where
/// A `clamp()` function. W: Write,
Clamp { {
/// The minimum value. match *self {
min: Box<CalcNode>, Self::Length(ref l) => l.to_css(dest),
/// The central value. Self::Number(ref n) => n.to_css(dest),
center: Box<CalcNode>, Self::Percentage(p) => crate::values::serialize_percentage(p, dest),
/// The maximum value. Self::Angle(ref a) => a.to_css(dest),
max: Box<CalcNode>, Self::Time(ref t) => t.to_css(dest),
}, }
}
} }
/// An expected unit we intend to parse within a `calc()` expression. /// 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. /// This is used as a hint for the parser to fast-reject invalid expressions.
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum CalcUnit { enum CalcUnit {
/// `<number>` /// `<number>`
Number, Number,
/// `<length>` /// `<length>`
@ -117,178 +88,50 @@ pub enum CalcUnit {
/// relative lengths, and to_computed_pixel_length_without_context() handles /// 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 /// this case. Therefore, if you want to add a new field, please make sure this
/// function work properly. /// function work properly.
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct CalcLengthPercentage { pub struct CalcLengthPercentage {
#[css(skip)]
pub clamping_mode: AllowedNumericType, pub clamping_mode: AllowedNumericType,
pub absolute: Option<AbsoluteLength>, pub node: CalcNode,
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(")")
}
} }
impl SpecifiedValueInfo for CalcLengthPercentage {} impl SpecifiedValueInfo for CalcLengthPercentage {}
macro_rules! impl_generic_to_type { impl PartialOrd for Leaf {
($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;
}
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(()),
})
}};
}
impl PartialOrd for CalcNode {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
use self::CalcNode::*; use self::Leaf::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
return None;
}
match (self, other) { match (self, other) {
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other), (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
(&Percentage(ref one), &Percentage(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()), (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()), (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other), (&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 { impl generic::CalcNodeLeaf for Leaf {
fn negate(&mut self) { fn is_negative(&self) -> bool {
self.mul_by(-1.); 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) { fn mul_by(&mut self, scalar: f32) {
@ -310,44 +153,10 @@ impl CalcNode {
Self::Percentage(ref mut p) => { Self::Percentage(ref mut p) => {
*p *= scalar; *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 { match *self {
Self::Number(..) => SortKey::Number, Self::Number(..) => SortKey::Number,
Self::Percentage(..) => SortKey::Percentage, Self::Percentage(..) => SortKey::Percentage,
@ -369,7 +178,12 @@ impl CalcNode {
}, },
NoCalcLength::ServoCharacterWidth(..) => unreachable!(), 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 /// Only handles leaf nodes, it's the caller's responsibility to simplify
/// them before calling this if needed. /// them before calling this if needed.
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> { 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) { match (self, other) {
(&mut Number(ref mut one), &Number(ref other)) | (&mut Number(ref mut one), &Number(ref other)) |
@ -394,170 +212,24 @@ impl CalcNode {
(&mut Length(ref mut one), &Length(ref other)) => { (&mut Length(ref mut one), &Length(ref other)) => {
*one = one.try_sum(other)?; *one = one.try_sum(other)?;
}, },
_ => return Err(()), _ => {
match *other {
Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) => {},
}
unsafe {
debug_unreachable!();
}
},
} }
Ok(()) 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(&center) {
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. /// A calc node representation for specified values.
let max_cmp_center = match max.partial_cmp(&center) { pub type CalcNode = generic::GenericCalcNode<Leaf>;
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());
}
},
Self::Length(ref mut len) => {
if let NoCalcLength::Absolute(ref mut absolute_length) = *len {
*absolute_length = AbsoluteLength::Px(absolute_length.to_px());
}
},
Self::Percentage(..) | Self::Angle(..) | Self::Time(..) | Self::Number(..) => {
// These are leaves already, nothing to do.
},
}
}
impl CalcNode {
/// Tries to parse a single element in the expression, that is, a /// Tries to parse a single element in the expression, that is, a
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to /// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
/// `expected_unit`. /// `expected_unit`.
@ -571,7 +243,7 @@ impl CalcNode {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
match (input.next()?, expected_unit) { match (input.next()?, expected_unit) {
(&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)), (&Token::Number { value, .. }, _) => Ok(CalcNode::Leaf(Leaf::Number(value))),
( (
&Token::Dimension { &Token::Dimension {
value, ref unit, .. value, ref unit, ..
@ -583,18 +255,22 @@ impl CalcNode {
value, ref unit, .. value, ref unit, ..
}, },
CalcUnit::LengthPercentage, CalcUnit::LengthPercentage,
) => NoCalcLength::parse_dimension(context, value, unit) ) => match NoCalcLength::parse_dimension(context, value, unit) {
.map(CalcNode::Length) Ok(l) => Ok(CalcNode::Leaf(Leaf::Length(l))),
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)), Err(()) => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
},
( (
&Token::Dimension { &Token::Dimension {
value, ref unit, .. value, ref unit, ..
}, },
CalcUnit::Angle, CalcUnit::Angle,
) => { ) => {
Angle::parse_dimension(value, unit, /* from_calc = */ true) match Angle::parse_dimension(value, unit, /* from_calc = */ true) {
.map(CalcNode::Angle) Ok(a) => Ok(CalcNode::Leaf(Leaf::Angle(a))),
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) Err(()) => {
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
},
}
}, },
( (
&Token::Dimension { &Token::Dimension {
@ -602,13 +278,16 @@ impl CalcNode {
}, },
CalcUnit::Time, CalcUnit::Time,
) => { ) => {
Time::parse_dimension(value, unit, /* from_calc = */ true) match Time::parse_dimension(value, unit, /* from_calc = */ true) {
.map(CalcNode::Time) Ok(t) => Ok(CalcNode::Leaf(Leaf::Time(t))),
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) Err(()) => {
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
},
}
}, },
(&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) | (&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => { (&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| { (&Token::ParenthesisBlock, _) => input.parse_nested_block(|input| {
CalcNode::parse_argument(context, input, expected_unit) CalcNode::parse_argument(context, input, expected_unit)
@ -654,11 +333,9 @@ impl CalcNode {
// //
// Consider adding an API to cssparser to specify the // Consider adding an API to cssparser to specify the
// initial vector capacity? // initial vector capacity?
let arguments = input let arguments = input.parse_comma_separated(|input| {
.parse_comma_separated(|input| {
Self::parse_argument(context, input, expected_unit) Self::parse_argument(context, input, expected_unit)
})? })?;
.into_boxed_slice();
let op = match function { let op = match function {
MathFunction::Min => MinMaxOp::Min, MathFunction::Min => MinMaxOp::Min,
@ -666,7 +343,7 @@ impl CalcNode {
_ => unreachable!(), _ => unreachable!(),
}; };
Ok(Self::MinMax(arguments, op)) Ok(Self::MinMax(arguments.into(), op))
}, },
} }
}) })
@ -712,7 +389,7 @@ impl CalcNode {
Ok(if sum.len() == 1 { Ok(if sum.len() == 1 {
sum.drain(..).next().unwrap() sum.drain(..).next().unwrap()
} else { } else {
Self::Sum(sum.into_boxed_slice()) Self::Sum(sum.into_boxed_slice().into())
}) })
} }
@ -773,106 +450,64 @@ impl CalcNode {
Ok(node) Ok(node)
} }
/// Tries to simplify this expression into a `<length>` or `<percentage`> /// Tries to simplify this expression into a `<length>` or `<percentage>`
/// value. /// value.
fn to_length_or_percentage( fn into_length_or_percentage(
&mut self, mut self,
clamping_mode: AllowedNumericType, clamping_mode: AllowedNumericType,
) -> Result<CalcLengthPercentage, ()> { ) -> Result<CalcLengthPercentage, ()> {
let mut ret = CalcLengthPercentage { // Keep track of whether there's any invalid member of the calculation,
clamping_mode, // so as to reject the calculation properly at parse-time.
..Default::default() let mut any_invalid = false;
}; self.visit_depth_first(|node| {
self.simplify_and_sort_children(); if let CalcNode::Leaf(ref l) = *node {
self.add_length_or_percentage_to(&mut ret, 1.0)?; any_invalid |= !matches!(*l, Leaf::Percentage(..) | Leaf::Length(..));
Ok(ret)
} }
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 { if any_invalid {
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(()); return Err(());
},
CalcNode::Angle(..) | CalcNode::Time(..) | CalcNode::Number(..) => return Err(()),
} }
Ok(()) Ok(CalcLengthPercentage {
clamping_mode,
node: self,
})
} }
/// Tries to simplify this expression into a `<time>` value. /// Tries to simplify this expression into a `<time>` value.
fn to_time(&self) -> Result<Time, ()> { 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. /// Tries to simplify this expression into an `Angle` value.
fn to_angle(&self) -> Result<Angle, ()> { 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. /// Tries to simplify this expression into a `<number>` value.
fn to_number(&self) -> Result<CSSFloat, ()> { 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. /// Tries to simplify this expression into a `<percentage>` value.
fn to_percentage(&self) -> Result<CSSFloat, ()> { 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, /// Given a function name, and the location from where the token came from,
@ -926,7 +561,7 @@ impl CalcNode {
function: MathFunction, function: MathFunction,
) -> Result<CalcLengthPercentage, ParseError<'i>> { ) -> Result<CalcLengthPercentage, ParseError<'i>> {
Self::parse(context, input, function, CalcUnit::LengthPercentage)? 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)) .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
@ -949,7 +584,7 @@ impl CalcNode {
function: MathFunction, function: MathFunction,
) -> Result<CalcLengthPercentage, ParseError<'i>> { ) -> Result<CalcLengthPercentage, ParseError<'i>> {
Self::parse(context, input, function, CalcUnit::Length)? 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)) .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }

View file

@ -140,8 +140,10 @@ pub enum SystemColor {
Windowframe, Windowframe,
Windowtext, Windowtext,
MozButtondefault, MozButtondefault,
MozDefaultColor, #[parse(aliases = "-moz-default-color")]
MozDefaultBackgroundColor, Canvastext,
#[parse(aliases = "-moz-default-background-color")]
Canvas,
MozDialog, MozDialog,
MozDialogtext, MozDialogtext,
/// Used to highlight valid regions to drop something onto. /// Used to highlight valid regions to drop something onto.
@ -230,9 +232,12 @@ pub enum SystemColor {
/// colors. /// colors.
MozNativehyperlinktext, MozNativehyperlinktext,
MozHyperlinktext, #[parse(aliases = "-moz-hyperlinktext")]
MozActivehyperlinktext, Linktext,
MozVisitedhyperlinktext, #[parse(aliases = "-moz-activehyperlinktext")]
Activetext,
#[parse(aliases = "-moz-visitedhyperlinktext")]
Visitedtext,
/// Combobox widgets /// Combobox widgets
MozComboboxtext, MozComboboxtext,
@ -253,11 +258,11 @@ impl SystemColor {
let prefs = cx.device().pref_sheet_prefs(); let prefs = cx.device().pref_sheet_prefs();
convert_nscolor_to_computedcolor(match *self { convert_nscolor_to_computedcolor(match *self {
SystemColor::MozDefaultColor => prefs.mDefaultColor, SystemColor::Canvastext => prefs.mDefaultColor,
SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor, SystemColor::Canvas => prefs.mDefaultBackgroundColor,
SystemColor::MozHyperlinktext => prefs.mLinkColor, SystemColor::Linktext => prefs.mLinkColor,
SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor, SystemColor::Activetext => prefs.mActiveLinkColor,
SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor, SystemColor::Visitedtext => prefs.mVisitedLinkColor,
_ => unsafe { _ => unsafe {
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document()) bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())

View file

@ -492,7 +492,9 @@ impl ToComputedValue for FontStretch {
SpecifiedValueInfo, SpecifiedValueInfo,
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue,
ToCss, ToCss,
ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[allow(missing_docs)] #[allow(missing_docs)]
@ -534,7 +536,9 @@ impl Default for KeywordSize {
PartialEq, PartialEq,
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue,
ToCss, ToCss,
ToResolvedValue,
ToShmem, ToShmem,
)] )]
/// Additional information for keyword-derived font sizes. /// Additional information for keyword-derived font sizes.
@ -567,7 +571,7 @@ impl KeywordInfo {
/// Computes the final size for this font-size keyword, accounting for /// Computes the final size for this font-size keyword, accounting for
/// text-zoom. /// text-zoom.
fn to_computed_value(&self, context: &Context) -> CSSPixelLength { 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) 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. /// The default font size.
pub const FONT_MEDIUM_PX: i32 = 16; pub const FONT_MEDIUM_PX: i32 = 16;
#[cfg(feature = "servo")] impl KeywordSize {
impl ToComputedValue for KeywordSize {
type ComputedValue = NonNegativeLength;
#[inline] #[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); let medium = Length::new(FONT_MEDIUM_PX as f32);
// https://drafts.csswg.org/css-fonts-3/#font-size-prop // https://drafts.csswg.org/css-fonts-3/#font-size-prop
NonNegative(match *self { NonNegative(match *self {
@ -779,17 +782,9 @@ impl ToComputedValue for KeywordSize {
}) })
} }
#[inline]
fn from_computed_value(_: &NonNegativeLength) -> Self {
unreachable!()
}
}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl ToComputedValue for KeywordSize {
type ComputedValue = NonNegativeLength;
#[inline] #[inline]
fn to_computed_value(&self, cx: &Context) -> NonNegativeLength { fn to_length(&self, cx: &Context) -> NonNegativeLength {
use crate::context::QuirksMode; use crate::context::QuirksMode;
// The tables in this function are originally from // 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 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
}) })
} }
#[inline]
fn from_computed_value(_: &NonNegativeLength) -> Self {
unreachable!()
}
} }
impl FontSize { impl FontSize {

View file

@ -185,16 +185,6 @@ impl TrackRepeat<LengthPercentage, Integer> {
values.push(track_size); values.push(track_size);
names.push(current_names); 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 { } else {
if values.is_empty() { if values.is_empty() {
// expecting at least one <track-size> // expecting at least one <track-size>

View file

@ -19,7 +19,8 @@ use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPosi
use crate::values::specified::position::{Position, PositionComponent, Side}; use crate::values::specified::position::{Position, PositionComponent, Side};
use crate::values::specified::url::SpecifiedImageUrl; use crate::values::specified::url::SpecifiedImageUrl;
use crate::values::specified::{ 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::values::specified::{Number, NumberOrPercentage, Percentage};
use crate::Atom; use crate::Atom;
@ -32,24 +33,6 @@ use std::fmt::{self, Write};
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; 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. /// Specified values for an image according to CSS-IMAGES.
/// <https://drafts.csswg.org/css-images/#image-values> /// <https://drafts.csswg.org/css-images/#image-values>
pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl>; pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl>;
@ -62,9 +45,23 @@ pub type Gradient = generic::Gradient<
NonNegativeLength, NonNegativeLength,
NonNegativeLengthPercentage, NonNegativeLengthPercentage,
Position, Position,
Angle,
AngleOrPercentage,
Color, 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 { impl SpecifiedValueInfo for Gradient {
const SUPPORTED_TYPES: u8 = CssType::GRADIENT; const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
@ -85,12 +82,12 @@ impl SpecifiedValueInfo for Gradient {
"-moz-repeating-radial-gradient", "-moz-repeating-radial-gradient",
"-webkit-gradient", "-webkit-gradient",
]); ]);
}
}
/// A specified gradient kind. if conic_gradients_enabled() {
pub type GradientKind = f(&["conic-gradient", "repeating-conic-gradient"]);
generic::GradientKind<LineDirection, NonNegativeLength, NonNegativeLengthPercentage, Position>; }
}
}
/// A specified gradient line direction. /// A specified gradient line direction.
/// ///
@ -110,16 +107,10 @@ pub enum LineDirection {
/// A specified ending shape. /// A specified ending shape.
pub type EndingShape = generic::EndingShape<NonNegativeLength, NonNegativeLengthPercentage>; 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` /// Specified values for `moz-image-rect`
/// -moz-image-rect(<uri>, top, right, bottom, left); /// -moz-image-rect(<uri>, top, right, bottom, left);
#[cfg(feature = "gecko")] #[cfg(all(feature = "gecko", not(feature = "cbindgen")))]
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, SpecifiedImageUrl>; pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, SpecifiedImageUrl>;
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
#[derive( #[derive(
@ -141,6 +132,9 @@ impl Parse for Image {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Image, ParseError<'i>> { ) -> 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)) { if let Ok(url) = input.try(|input| SpecifiedImageUrl::parse(context, input)) {
return Ok(generic::Image::Url(url)); return Ok(generic::Image::Url(url));
} }
@ -211,10 +205,11 @@ impl Parse for Gradient {
enum Shape { enum Shape {
Linear, Linear,
Radial, Radial,
Conic,
} }
let func = input.expect_function()?; 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" => { "linear-gradient" => {
(Shape::Linear, false, GradientCompatMode::Modern) (Shape::Linear, false, GradientCompatMode::Modern)
}, },
@ -255,6 +250,12 @@ impl Parse for Gradient {
"-moz-repeating-radial-gradient" => { "-moz-repeating-radial-gradient" => {
(Shape::Radial, true, GradientCompatMode::Moz) (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" => { "-webkit-gradient" => {
return input.parse_nested_block(|i| { return input.parse_nested_block(|i| {
Self::parse_webkit_gradient_argument(context, i) Self::parse_webkit_gradient_argument(context, i)
@ -266,25 +267,13 @@ impl Parse for Gradient {
} }
}; };
let (kind, items) = input.parse_nested_block(|i| { Ok(input.parse_nested_block(|i| {
let shape = match shape { Ok(match shape {
Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?, Shape::Linear => Self::parse_linear(context, i, repeating, compat_mode)?,
Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?, Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?,
}; Shape::Conic => Self::parse_conic(context, i, repeating)?,
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,
}) })
})?)
} }
} }
@ -404,16 +393,21 @@ impl Gradient {
let ident = input.expect_ident_cloned()?; let ident = input.expect_ident_cloned()?;
input.expect_comma()?; input.expect_comma()?;
let (kind, reverse_stops) = match_ignore_ascii_case! { &ident, Ok(match_ignore_ascii_case! { &ident,
"linear" => { "linear" => {
let first = Point::parse(context, input)?; let first = Point::parse(context, input)?;
input.expect_comma()?; input.expect_comma()?;
let second = Point::parse(context, input)?; let second = Point::parse(context, input)?;
let direction = LineDirection::from_points(first, second); 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" => { "radial" => {
let first_point = Point::parse(context, input)?; let first_point = Point::parse(context, input)?;
@ -433,16 +427,28 @@ impl Gradient {
let rad = Circle::Radius(NonNegative(Length::from_px(radius.value))); let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));
let shape = generic::EndingShape::Circle(rad); let shape = generic::EndingShape::Circle(rad);
let position: Position = point.into(); let position: Position = point.into();
let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?;
let kind = generic::GradientKind::Radial(shape, position); generic::Gradient::Radial {
(kind, reverse_stops) shape,
position,
items,
repeating: false,
compat_mode: GradientCompatMode::Modern,
}
}, },
_ => { _ => {
let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone()); let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());
return Err(input.new_custom_error(e)); 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 let mut items = input
.try(|i| { .try(|i| {
i.expect_comma()?; i.expect_comma()?;
@ -521,46 +527,62 @@ impl Gradient {
} }
}) })
} }
Ok(items.into())
Ok(generic::Gradient { }
kind,
items: items.into(), /// Not used for -webkit-gradient syntax and conic-gradient
repeating: false, fn parse_stops<'i, 't>(
compat_mode: GradientCompatMode::Modern, 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. /// Parses a linear gradient.
/// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword. /// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
fn parse_linear<'i, 't>( fn parse_linear<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
compat_mode: &mut GradientCompatMode, repeating: bool,
mut compat_mode: GradientCompatMode,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) let direction =
{ if let Ok(d) = input.try(|i| LineDirection::parse(context, i, &mut compat_mode)) {
input.expect_comma()?; input.expect_comma()?;
d d
} else { } else {
match *compat_mode { match compat_mode {
GradientCompatMode::Modern => { GradientCompatMode::Modern => {
LineDirection::Vertical(VerticalPositionKeyword::Bottom) LineDirection::Vertical(VerticalPositionKeyword::Bottom)
}, },
_ => LineDirection::Vertical(VerticalPositionKeyword::Top), _ => LineDirection::Vertical(VerticalPositionKeyword::Top),
} }
}; };
Ok(generic::GradientKind::Linear(direction)) let items = Gradient::parse_stops(context, input)?;
Ok(Gradient::Linear {
direction,
items,
repeating,
compat_mode,
})
} }
/// Parses a radial gradient.
fn parse_radial<'i, 't>( fn parse_radial<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
compat_mode: &mut GradientCompatMode, repeating: bool,
compat_mode: GradientCompatMode,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let (shape, position) = match *compat_mode { let (shape, position) = match compat_mode {
GradientCompatMode::Modern => { 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| { let position = input.try(|i| {
i.expect_ident_matching("at")?; i.expect_ident_matching("at")?;
Position::parse(context, i) Position::parse(context, i)
@ -573,7 +595,7 @@ impl GradientKind {
if position.is_ok() { if position.is_ok() {
i.expect_comma()?; i.expect_comma()?;
} }
EndingShape::parse(context, i, *compat_mode) EndingShape::parse(context, i, compat_mode)
}); });
(shape, position.ok()) (shape, position.ok())
}, },
@ -588,7 +610,54 @@ impl GradientKind {
}); });
let position = position.unwrap_or(Position::center()); 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>( fn parse_comma_separated<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, 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>> { ) -> Result<crate::OwnedSlice<Self>, ParseError<'i>> {
let mut items = Vec::new(); let mut items = Vec::new();
let mut seen_stop = false; let mut seen_stop = false;
@ -804,20 +875,20 @@ impl GradientItem {
loop { loop {
input.parse_until_before(Delimiter::Comma, |input| { input.parse_until_before(Delimiter::Comma, |input| {
if seen_stop { 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; seen_stop = false;
items.push(generic::GradientItem::InterpolationHint(hint)); items.push(generic::GradientItem::InterpolationHint(hint));
return Ok(()); 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(); let stop_color = stop.color.clone();
items.push(stop.into_item()); items.push(stop.into_item());
items.push( items.push(
ColorStop { generic::ColorStop {
color: stop_color, color: stop_color,
position: Some(multi_position), position: Some(multi_position),
} }
@ -845,14 +916,18 @@ impl GradientItem {
} }
} }
impl Parse for ColorStop { impl<T> generic::ColorStop<Color, T> {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, 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>> { ) -> Result<Self, ParseError<'i>> {
Ok(ColorStop { Ok(generic::ColorStop {
color: Color::parse(context, input)?, color: Color::parse(context, input)?,
position: input.try(|i| LengthPercentage::parse(context, i)).ok(), position: input.try(|i| parse_position(context, i)).ok(),
}) })
} }
} }

View file

@ -16,7 +16,7 @@ use crate::values::generics::length::{
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
}; };
use crate::values::generics::NonNegative; 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::specified::NonNegativeNumber;
use crate::values::CSSFloat; use crate::values::CSSFloat;
use crate::Zero; use crate::Zero;
@ -28,8 +28,8 @@ use std::ops::{Add, Mul};
use style_traits::values::specified::AllowedNumericType; use style_traits::values::specified::AllowedNumericType;
use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind}; use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use super::image::Image;
pub use super::image::{GradientKind, Image}; pub use super::image::{EndingShape as GradientEndingShape, Gradient};
pub use crate::values::specified::calc::CalcLengthPercentage; pub use crate::values::specified::calc::CalcLengthPercentage;
/// Number of app units per pixel /// Number of app units per pixel
@ -92,7 +92,16 @@ impl FontRelativeLength {
FontRelativeLength::Em(v) | FontRelativeLength::Em(v) |
FontRelativeLength::Ex(v) | FontRelativeLength::Ex(v) |
FontRelativeLength::Ch(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::Vw(v) |
ViewportPercentageLength::Vh(v) | ViewportPercentageLength::Vh(v) |
ViewportPercentageLength::Vmin(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. /// Convert this into a pixel value.
#[inline] #[inline]
pub fn to_px(&self) -> CSSFloat { pub fn to_px(&self) -> CSSFloat {
@ -484,6 +514,16 @@ impl Mul<CSSFloat> for NoCalcLength {
} }
impl 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. /// Parse a given absolute or relative dimension.
pub fn parse_dimension( pub fn parse_dimension(
context: &ParserContext, context: &ParserContext,
@ -912,9 +952,10 @@ impl From<Percentage> for LengthPercentage {
#[inline] #[inline]
fn from(pc: Percentage) -> Self { fn from(pc: Percentage) -> Self {
if pc.is_calc() { if pc.is_calc() {
// FIXME(emilio): Hard-coding the clamping mode is suspect.
LengthPercentage::Calc(Box::new(CalcLengthPercentage { LengthPercentage::Calc(Box::new(CalcLengthPercentage {
percentage: Some(computed::Percentage(pc.get())), clamping_mode: AllowedNumericType::All,
..Default::default() node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
})) }))
} else { } else {
LengthPercentage::Percentage(computed::Percentage(pc.get())) LengthPercentage::Percentage(computed::Percentage(pc.get()))

View file

@ -55,8 +55,8 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
pub use self::font::{FontVariantAlternates, FontWeight}; pub use self::font::{FontVariantAlternates, FontWeight};
pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontVariantEastAsian, FontVariationSettings};
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{EndingShape as GradientEndingShape, Gradient};
pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect}; pub use self::image::{Image, MozImageRect};
pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth}; pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber}; pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; 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::{SVGLength, SVGOpacity, SVGPaint};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::svg_path::SVGPathData; pub use self::svg_path::SVGPathData;
pub use self::text::TextAlignLast;
pub use self::text::TextUnderlinePosition; pub use self::text::TextUnderlinePosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign}; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};

View file

@ -120,9 +120,6 @@ impl Percentage {
Token::Function(ref name) => { Token::Function(ref name) => {
let function = CalcNode::math_function(name, location)?; let function = CalcNode::math_function(name, location)?;
let value = CalcNode::parse_percentage(context, input, function)?; 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 { Ok(Percentage {
value, value,
calc_clamping_mode: Some(num_context), calc_clamping_mode: Some(num_context),

View file

@ -13,6 +13,7 @@ use crate::str::HTML_SPACE_CHARACTERS;
use crate::values::computed::LengthPercentage as ComputedLengthPercentage; use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
use crate::values::computed::{Context, Percentage, ToComputedValue}; use crate::values::computed::{Context, Percentage, ToComputedValue};
use crate::values::generics::position::Position as GenericPosition; 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::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex; use crate::values::generics::position::ZIndex as GenericZIndex;
use crate::values::specified::{AllowQuirks, Integer, LengthPercentage}; 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> { impl<S> PositionComponent<S> {
/// `0%` /// `0%`
pub fn zero() -> Self { 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() => { PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
let length = length.to_computed_value(context); 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>)`. // 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::Side(_, Some(ref length)) |
PositionComponent::Length(ref length) => length.to_computed_value(context), PositionComponent::Length(ref length) => length.to_computed_value(context),
@ -350,66 +361,25 @@ impl Side for VerticalPositionKeyword {
} }
} }
#[derive( bitflags! {
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 /// Controls how the auto-placement algorithm works
/// specifying exactly how auto-placed items get flowed into the grid /// specifying exactly how auto-placed items get flowed into the grid
pub struct GridAutoFlow { #[derive(
/// Specifiy how auto-placement algorithm fills each `row` or `column` in turn MallocSizeOf,
#[css(contextual_skip_if = "is_row_dense")] SpecifiedValueInfo,
pub autoflow: AutoFlow, ToComputedValue,
/// Specify use `dense` packing algorithm or not ToResolvedValue,
#[css(represents_keyword)] ToShmem
pub dense: bool, )]
} #[value_info(other_values = "row,column,dense")]
#[repr(C)]
impl GridAutoFlow { pub struct GridAutoFlow: u8 {
#[inline] /// 'row' - mutually exclusive with 'column'
/// Get default `grid-auto-flow` as `row` const ROW = 1 << 0;
pub fn row() -> GridAutoFlow { /// 'column' - mutually exclusive with 'row'
GridAutoFlow { const COLUMN = 1 << 1;
autoflow: AutoFlow::Row, /// 'dense'
dense: false, const DENSE = 1 << 2;
}
} }
} }
@ -419,26 +389,26 @@ impl Parse for GridAutoFlow {
_context: &ParserContext, _context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<GridAutoFlow, ParseError<'i>> { ) -> Result<GridAutoFlow, ParseError<'i>> {
let mut value = None; let mut track = None;
let mut dense = false; let mut dense = GridAutoFlow::empty();
while !input.is_exhausted() { while !input.is_exhausted() {
let location = input.current_source_location(); let location = input.current_source_location();
let ident = input.expect_ident()?; let ident = input.expect_ident()?;
let success = match_ignore_ascii_case! { &ident, let success = match_ignore_ascii_case! { &ident,
"row" if value.is_none() => { "row" if track.is_none() => {
value = Some(AutoFlow::Row); track = Some(GridAutoFlow::ROW);
true true
}, },
"column" if value.is_none() => { "column" if track.is_none() => {
value = Some(AutoFlow::Column); track = Some(GridAutoFlow::COLUMN);
true true
}, },
"dense" if !dense => { "dense" if dense.is_empty() => {
dense = true; dense = GridAutoFlow::DENSE;
true true
}, },
_ => false _ => false,
}; };
if !success { if !success {
return Err(location return Err(location
@ -446,47 +416,37 @@ impl Parse for GridAutoFlow {
} }
} }
if value.is_some() || dense { if track.is_some() || !dense.is_empty() {
Ok(GridAutoFlow { Ok(track.unwrap_or(GridAutoFlow::ROW) | dense)
autoflow: value.unwrap_or(AutoFlow::Row),
dense: dense,
})
} else { } else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
} }
} }
#[cfg(feature = "gecko")] impl ToCss for GridAutoFlow {
impl From<u8> for GridAutoFlow { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
fn from(bits: u8) -> GridAutoFlow { where
use crate::gecko_bindings::structs; W: Write,
{
GridAutoFlow { if *self == GridAutoFlow::ROW {
autoflow: if bits & structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8 != 0 { return dest.write_str("row");
AutoFlow::Row
} else {
AutoFlow::Column
},
dense: bits & structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8 != 0,
}
}
} }
#[cfg(feature = "gecko")] if *self == GridAutoFlow::COLUMN {
impl From<GridAutoFlow> for u8 { return dest.write_str("column");
fn from(v: GridAutoFlow) -> u8 {
use crate::gecko_bindings::structs;
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 v.dense {
result |= structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8;
} }
result
if *self == GridAutoFlow::ROW | GridAutoFlow::DENSE {
return dest.write_str("dense");
}
if *self == GridAutoFlow::COLUMN | GridAutoFlow::DENSE {
return dest.write_str("column dense");
}
debug_assert!(false, "Unknown or invalid grid-autoflow value");
Ok(())
} }
} }
@ -592,7 +552,7 @@ impl TemplateAreas {
Ok(TemplateAreas { Ok(TemplateAreas {
areas: areas.into(), areas: areas.into(),
strings: strings.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 /// A range of rows or columns. Using this instead of std::ops::Range for FFI
/// purposes. /// purposes.
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] #[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
pub struct UnsignedRange { pub struct UnsignedRange {
/// The start of the range. /// The start of the range.
pub start: u32, pub start: u32,
@ -650,7 +619,16 @@ pub struct UnsignedRange {
pub end: u32, pub end: u32,
} }
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] #[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)] #[repr(C)]
/// Not associated with any particular grid item, but can be referenced from the /// Not associated with any particular grid item, but can be referenced from the
/// grid-placement properties. /// grid-placement properties.

View file

@ -21,13 +21,13 @@ use style_traits::{StyleParseErrorKind, ToCss};
pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>; pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;
/// <length> | <percentage> | <number> | context-value /// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<LengthPercentage>; pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
/// A non-negative version of SVGLength. /// A non-negative version of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>; pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
/// [ <length> | <percentage> | <number> ]# | context-value /// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>; pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
/// Whether the `context-value` value is enabled. /// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]

Some files were not shown because too many files have changed in this diff Show more