mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
commit
2829945963
118 changed files with 3141 additions and 3196 deletions
|
@ -60,10 +60,10 @@ use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect};
|
|||
use style::properties::{style_structs, ComputedValues};
|
||||
use style::servo::restyle_damage::ServoRestyleDamage;
|
||||
use style::values::computed::effects::SimpleShadow;
|
||||
use style::values::computed::image::{Image, ImageLayer};
|
||||
use style::values::computed::image::Image;
|
||||
use style::values::computed::{ClipRectOrAuto, Gradient, LengthOrAuto};
|
||||
use style::values::generics::background::BackgroundSize;
|
||||
use style::values::generics::image::{GradientKind, PaintWorklet};
|
||||
use style::values::generics::image::PaintWorklet;
|
||||
use style::values::specified::ui::CursorKind;
|
||||
use style::values::RGBA;
|
||||
use style_traits::ToCss;
|
||||
|
@ -732,12 +732,8 @@ impl Fragment {
|
|||
// http://www.w3.org/TR/CSS21/colors.html#background
|
||||
let background = style.get_background();
|
||||
for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
|
||||
let background_image = match *background_image {
|
||||
ImageLayer::None => continue,
|
||||
ImageLayer::Image(ref image) => image,
|
||||
};
|
||||
|
||||
match *background_image {
|
||||
Image::None => {},
|
||||
Image::Gradient(ref gradient) => {
|
||||
self.build_display_list_for_background_gradient(
|
||||
state,
|
||||
|
@ -975,15 +971,15 @@ impl Fragment {
|
|||
display_list_section,
|
||||
);
|
||||
|
||||
let display_item = match gradient.kind {
|
||||
GradientKind::Linear(angle_or_corner) => {
|
||||
let (gradient, stops) = gradient::linear(
|
||||
style,
|
||||
placement.tile_size,
|
||||
&gradient.items[..],
|
||||
angle_or_corner,
|
||||
gradient.repeating,
|
||||
);
|
||||
let display_item = match gradient {
|
||||
Gradient::Linear {
|
||||
ref direction,
|
||||
ref items,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => {
|
||||
let (gradient, stops) =
|
||||
gradient::linear(style, placement.tile_size, items, *direction, *repeating);
|
||||
let item = webrender_api::GradientDisplayItem {
|
||||
gradient,
|
||||
bounds: placement.bounds.to_f32_px(),
|
||||
|
@ -993,14 +989,20 @@ impl Fragment {
|
|||
};
|
||||
DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops))
|
||||
},
|
||||
GradientKind::Radial(ref shape, ref center) => {
|
||||
Gradient::Radial {
|
||||
ref shape,
|
||||
ref position,
|
||||
ref items,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => {
|
||||
let (gradient, stops) = gradient::radial(
|
||||
style,
|
||||
placement.tile_size,
|
||||
&gradient.items[..],
|
||||
items,
|
||||
shape,
|
||||
center,
|
||||
gradient.repeating,
|
||||
position,
|
||||
*repeating,
|
||||
);
|
||||
let item = webrender_api::RadialGradientDisplayItem {
|
||||
gradient,
|
||||
|
@ -1011,6 +1013,7 @@ impl Fragment {
|
|||
};
|
||||
DisplayItem::RadialGradient(CommonDisplayItem::with_data(base, item, stops))
|
||||
},
|
||||
Gradient::Conic { .. } => unimplemented!(),
|
||||
};
|
||||
state.add_display_item(display_item);
|
||||
});
|
||||
|
@ -1122,22 +1125,20 @@ impl Fragment {
|
|||
let border_radius = border::radii(bounds, border_style_struct);
|
||||
let border_widths = border.to_physical(style.writing_mode);
|
||||
|
||||
if let ImageLayer::Image(ref image) = border_style_struct.border_image_source {
|
||||
if self
|
||||
.build_display_list_for_border_image(
|
||||
state,
|
||||
style,
|
||||
base.clone(),
|
||||
bounds,
|
||||
image,
|
||||
border_widths,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Fallback to rendering a solid border.
|
||||
if self
|
||||
.build_display_list_for_border_image(
|
||||
state,
|
||||
style,
|
||||
base.clone(),
|
||||
bounds,
|
||||
&border_style_struct.border_image_source,
|
||||
border_widths,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if border_widths == SideOffsets2D::zero() {
|
||||
return;
|
||||
}
|
||||
|
@ -1224,30 +1225,37 @@ impl Fragment {
|
|||
height = image.height;
|
||||
NinePatchBorderSource::Image(image.key?)
|
||||
},
|
||||
Image::Gradient(ref gradient) => match gradient.kind {
|
||||
GradientKind::Linear(angle_or_corner) => {
|
||||
let (wr_gradient, linear_stops) = gradient::linear(
|
||||
style,
|
||||
border_image_area,
|
||||
&gradient.items[..],
|
||||
angle_or_corner,
|
||||
gradient.repeating,
|
||||
);
|
||||
Image::Gradient(ref gradient) => match **gradient {
|
||||
Gradient::Linear {
|
||||
ref direction,
|
||||
ref items,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => {
|
||||
let (wr_gradient, linear_stops) =
|
||||
gradient::linear(style, border_image_area, items, *direction, *repeating);
|
||||
stops = linear_stops;
|
||||
NinePatchBorderSource::Gradient(wr_gradient)
|
||||
},
|
||||
GradientKind::Radial(ref shape, ref center) => {
|
||||
Gradient::Radial {
|
||||
ref shape,
|
||||
ref position,
|
||||
ref items,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => {
|
||||
let (wr_gradient, radial_stops) = gradient::radial(
|
||||
style,
|
||||
border_image_area,
|
||||
&gradient.items[..],
|
||||
items,
|
||||
shape,
|
||||
center,
|
||||
gradient.repeating,
|
||||
position,
|
||||
*repeating,
|
||||
);
|
||||
stops = radial_stops;
|
||||
NinePatchBorderSource::RadialGradient(wr_gradient)
|
||||
},
|
||||
Gradient::Conic { .. } => unimplemented!(),
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
|
|
@ -7,8 +7,8 @@ use app_units::Au;
|
|||
use euclid::default::{Point2D, Size2D, Vector2D};
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::image::{EndingShape, LineDirection};
|
||||
use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position};
|
||||
use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent};
|
||||
use style::values::computed::{Angle, Color, LengthPercentage, Percentage, Position};
|
||||
use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent};
|
||||
use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient};
|
||||
|
||||
/// A helper data structure for gradients.
|
||||
|
@ -78,7 +78,7 @@ fn ellipse_size_keyword(
|
|||
|
||||
fn convert_gradient_stops(
|
||||
style: &ComputedValues,
|
||||
gradient_items: &[GradientItem],
|
||||
gradient_items: &[GradientItem<Color, LengthPercentage>],
|
||||
total_length: Au,
|
||||
) -> GradientBuilder {
|
||||
// Determine the position of each stop per CSS-IMAGES § 3.4.
|
||||
|
@ -237,7 +237,7 @@ fn position_to_offset(position: &LengthPercentage, total_length: Au) -> f32 {
|
|||
pub fn linear(
|
||||
style: &ComputedValues,
|
||||
size: Size2D<Au>,
|
||||
stops: &[GradientItem],
|
||||
stops: &[GradientItem<Color, LengthPercentage>],
|
||||
direction: LineDirection,
|
||||
repeating: bool,
|
||||
) -> (Gradient, Vec<GradientStop>) {
|
||||
|
@ -303,7 +303,7 @@ pub fn linear(
|
|||
pub fn radial(
|
||||
style: &ComputedValues,
|
||||
size: Size2D<Au>,
|
||||
stops: &[GradientItem],
|
||||
stops: &[GradientItem<Color, LengthPercentage>],
|
||||
shape: &EndingShape,
|
||||
center: &Position,
|
||||
repeating: bool,
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::image::{EndingShape, Gradient, LineDirection};
|
||||
use style::values::computed::{GradientItem, Length, Position};
|
||||
use style::values::generics::image::GenericGradientKind as Kind;
|
||||
use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent};
|
||||
use style::values::computed::{Color, Length, LengthPercentage, Position};
|
||||
use style::values::generics::image::{Circle, ColorStop, Ellipse, GradientItem, ShapeExtent};
|
||||
use webrender_api::{self as wr, units};
|
||||
|
||||
pub(super) fn build(
|
||||
|
@ -15,36 +14,51 @@ pub(super) fn build(
|
|||
layer: &super::background::BackgroundLayer,
|
||||
builder: &mut super::DisplayListBuilder,
|
||||
) {
|
||||
let extend_mode = if gradient.repeating {
|
||||
wr::ExtendMode::Repeat
|
||||
} else {
|
||||
wr::ExtendMode::Clamp
|
||||
};
|
||||
match &gradient.kind {
|
||||
Kind::Linear(line_direction) => build_linear(
|
||||
match gradient {
|
||||
Gradient::Linear {
|
||||
ref items,
|
||||
ref direction,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => build_linear(
|
||||
style,
|
||||
&gradient.items,
|
||||
line_direction,
|
||||
extend_mode,
|
||||
items,
|
||||
direction,
|
||||
if *repeating {
|
||||
wr::ExtendMode::Repeat
|
||||
} else {
|
||||
wr::ExtendMode::Clamp
|
||||
},
|
||||
&layer,
|
||||
builder,
|
||||
),
|
||||
Kind::Radial(ending_shape, center) => build_radial(
|
||||
Gradient::Radial {
|
||||
ref shape,
|
||||
ref position,
|
||||
ref items,
|
||||
ref repeating,
|
||||
compat_mode: _,
|
||||
} => build_radial(
|
||||
style,
|
||||
&gradient.items,
|
||||
ending_shape,
|
||||
center,
|
||||
extend_mode,
|
||||
items,
|
||||
shape,
|
||||
position,
|
||||
if *repeating {
|
||||
wr::ExtendMode::Repeat
|
||||
} else {
|
||||
wr::ExtendMode::Clamp
|
||||
},
|
||||
&layer,
|
||||
builder,
|
||||
),
|
||||
Gradient::Conic { .. } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-images-3/#linear-gradients
|
||||
pub(super) fn build_linear(
|
||||
style: &ComputedValues,
|
||||
items: &[GradientItem],
|
||||
items: &[GradientItem<Color, LengthPercentage>],
|
||||
line_direction: &LineDirection,
|
||||
extend_mode: wr::ExtendMode,
|
||||
layer: &super::background::BackgroundLayer,
|
||||
|
@ -144,7 +158,7 @@ pub(super) fn build_linear(
|
|||
/// https://drafts.csswg.org/css-images-3/#radial-gradients
|
||||
pub(super) fn build_radial(
|
||||
style: &ComputedValues,
|
||||
items: &[GradientItem],
|
||||
items: &[GradientItem<Color, LengthPercentage>],
|
||||
shape: &EndingShape,
|
||||
center: &Position,
|
||||
extend_mode: wr::ExtendMode,
|
||||
|
@ -244,7 +258,7 @@ pub(super) fn build_radial(
|
|||
/// https://drafts.csswg.org/css-images-4/#color-stop-fixup
|
||||
fn fixup_stops(
|
||||
style: &ComputedValues,
|
||||
items: &[GradientItem],
|
||||
items: &[GradientItem<Color, LengthPercentage>],
|
||||
gradient_line_length: Length,
|
||||
) -> Vec<wr::GradientStop> {
|
||||
// Remove color transititon hints, which are not supported yet.
|
||||
|
|
|
@ -333,7 +333,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
|
||||
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
|
||||
use style::values::computed::image::{Image, ImageLayer};
|
||||
use style::values::computed::image::Image;
|
||||
let b = self.fragment.style.get_background();
|
||||
let background_color = self.fragment.style.resolve_color(b.background_color);
|
||||
if background_color.alpha > 0 {
|
||||
|
@ -345,85 +345,80 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
builder.wr.push_rect(&common, rgba(background_color))
|
||||
}
|
||||
// Reverse because the property is top layer first, we want to paint bottom layer first.
|
||||
for (index, layer) in b.background_image.0.iter().enumerate().rev() {
|
||||
match layer {
|
||||
ImageLayer::None => {},
|
||||
ImageLayer::Image(image) => match image {
|
||||
Image::Gradient(gradient) => {
|
||||
let intrinsic = IntrinsicSizes {
|
||||
width: None,
|
||||
height: None,
|
||||
ratio: None,
|
||||
};
|
||||
if let Some(layer) =
|
||||
&background::layout_layer(self, builder, index, intrinsic)
|
||||
{
|
||||
gradient::build(&self.fragment.style, gradient, layer, builder)
|
||||
}
|
||||
},
|
||||
Image::Url(image_url) => {
|
||||
// FIXME: images won’t always have in intrinsic width or height
|
||||
// when support for SVG is added.
|
||||
// Or a WebRender `ImageKey`, for that matter.
|
||||
let (width, height, key) = match image_url.url() {
|
||||
Some(url) => {
|
||||
match builder.context.get_webrender_image_for_url(
|
||||
self.fragment.tag,
|
||||
url.clone(),
|
||||
UsePlaceholder::No,
|
||||
) {
|
||||
Some(WebRenderImageInfo {
|
||||
width,
|
||||
height,
|
||||
key: Some(key),
|
||||
}) => (width, height, key),
|
||||
_ => continue,
|
||||
}
|
||||
},
|
||||
None => continue,
|
||||
};
|
||||
|
||||
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||
let dppx = 1.0;
|
||||
|
||||
let intrinsic = IntrinsicSizes {
|
||||
width: Some(Length::new(width as f32 / dppx)),
|
||||
height: Some(Length::new(height as f32 / dppx)),
|
||||
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||
ratio: Some(width as f32 / height as f32),
|
||||
};
|
||||
|
||||
if let Some(layer) =
|
||||
background::layout_layer(self, builder, index, intrinsic)
|
||||
{
|
||||
let image_rendering =
|
||||
image_rendering(self.fragment.style.clone_image_rendering());
|
||||
if layer.repeat {
|
||||
builder.wr.push_repeating_image(
|
||||
&layer.common,
|
||||
layer.bounds,
|
||||
layer.tile_size,
|
||||
layer.tile_spacing,
|
||||
image_rendering,
|
||||
wr::AlphaType::PremultipliedAlpha,
|
||||
key,
|
||||
wr::ColorF::WHITE,
|
||||
)
|
||||
} else {
|
||||
builder.wr.push_image(
|
||||
&layer.common,
|
||||
layer.bounds,
|
||||
image_rendering,
|
||||
wr::AlphaType::PremultipliedAlpha,
|
||||
key,
|
||||
wr::ColorF::WHITE,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
// Gecko-only value, represented as a (boxed) empty enum on non-Gecko.
|
||||
Image::Rect(rect) => match **rect {},
|
||||
for (index, image) in b.background_image.0.iter().enumerate().rev() {
|
||||
match image {
|
||||
Image::None => {},
|
||||
Image::Gradient(ref gradient) => {
|
||||
let intrinsic = IntrinsicSizes {
|
||||
width: None,
|
||||
height: None,
|
||||
ratio: None,
|
||||
};
|
||||
if let Some(layer) = &background::layout_layer(self, builder, index, intrinsic)
|
||||
{
|
||||
gradient::build(&self.fragment.style, &gradient, layer, builder)
|
||||
}
|
||||
},
|
||||
Image::Url(ref image_url) => {
|
||||
// FIXME: images won’t always have in intrinsic width or height
|
||||
// when support for SVG is added.
|
||||
// Or a WebRender `ImageKey`, for that matter.
|
||||
let (width, height, key) = match image_url.url() {
|
||||
Some(url) => {
|
||||
match builder.context.get_webrender_image_for_url(
|
||||
self.fragment.tag,
|
||||
url.clone(),
|
||||
UsePlaceholder::No,
|
||||
) {
|
||||
Some(WebRenderImageInfo {
|
||||
width,
|
||||
height,
|
||||
key: Some(key),
|
||||
}) => (width, height, key),
|
||||
_ => continue,
|
||||
}
|
||||
},
|
||||
None => continue,
|
||||
};
|
||||
|
||||
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||
let dppx = 1.0;
|
||||
|
||||
let intrinsic = IntrinsicSizes {
|
||||
width: Some(Length::new(width as f32 / dppx)),
|
||||
height: Some(Length::new(height as f32 / dppx)),
|
||||
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||
ratio: Some(width as f32 / height as f32),
|
||||
};
|
||||
|
||||
if let Some(layer) = background::layout_layer(self, builder, index, intrinsic) {
|
||||
let image_rendering =
|
||||
image_rendering(self.fragment.style.clone_image_rendering());
|
||||
if layer.repeat {
|
||||
builder.wr.push_repeating_image(
|
||||
&layer.common,
|
||||
layer.bounds,
|
||||
layer.tile_size,
|
||||
layer.tile_spacing,
|
||||
image_rendering,
|
||||
wr::AlphaType::PremultipliedAlpha,
|
||||
key,
|
||||
wr::ColorF::WHITE,
|
||||
)
|
||||
} else {
|
||||
builder.wr.push_image(
|
||||
&layer.common,
|
||||
layer.bounds,
|
||||
image_rendering,
|
||||
wr::AlphaType::PremultipliedAlpha,
|
||||
key,
|
||||
wr::ColorF::WHITE,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
// Gecko-only value, represented as a (boxed) empty enum on non-Gecko.
|
||||
Image::Rect(ref rect) => match **rect {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -914,10 +914,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
@ -1441,11 +1437,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::exported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::imported_part called");
|
||||
None
|
||||
|
|
|
@ -922,10 +922,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
@ -1447,11 +1443,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::exported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::imported_part called");
|
||||
None
|
||||
|
|
|
@ -17,7 +17,9 @@ use crate::stylesheet_loader::StylesheetLoader;
|
|||
use dom_struct::dom_struct;
|
||||
use servo_arc::Arc;
|
||||
use style::shared_lock::Locked;
|
||||
use style::stylesheets::{CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError};
|
||||
use style::stylesheets::{
|
||||
AllowImportRules, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError,
|
||||
};
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe_no_jsmanaged_fields!(RulesSource);
|
||||
|
@ -116,6 +118,7 @@ impl CSSRuleList {
|
|||
index,
|
||||
nested,
|
||||
Some(&loader),
|
||||
AllowImportRules::Yes,
|
||||
)
|
||||
})?;
|
||||
|
||||
|
|
|
@ -682,10 +682,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
|
|||
hints.push(from_declaration(
|
||||
shared_lock,
|
||||
PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue(
|
||||
vec![specified::ImageLayer::Image(specified::Image::for_cascade(
|
||||
url.into(),
|
||||
))]
|
||||
.into(),
|
||||
vec![specified::Image::for_cascade(url.into())].into(),
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
@ -3164,10 +3161,6 @@ impl<'a> SelectorsElement for DomRoot<Element> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ use servo_arc::Arc;
|
|||
use std::cell::Cell;
|
||||
use style::media_queries::MediaList;
|
||||
use style::parser::ParserContext as CssParserContext;
|
||||
use style::stylesheets::{CssRuleType, Origin, Stylesheet};
|
||||
use style::stylesheets::{AllowImportRules, CssRuleType, Origin, Stylesheet};
|
||||
use style_traits::ParsingMode;
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -119,6 +119,7 @@ impl HTMLStyleElement {
|
|||
css_error_reporter,
|
||||
doc.quirks_mode(),
|
||||
self.line_number as u32,
|
||||
AllowImportRules::Yes,
|
||||
);
|
||||
|
||||
let sheet = Arc::new(sheet);
|
||||
|
|
|
@ -676,19 +676,22 @@ where
|
|||
None => return false,
|
||||
};
|
||||
|
||||
loop {
|
||||
let outer_host = host.containing_shadow_host();
|
||||
if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host {
|
||||
break;
|
||||
let current_host = context.shared.current_host;
|
||||
if current_host != Some(host.opaque()) {
|
||||
loop {
|
||||
let outer_host = host.containing_shadow_host();
|
||||
if outer_host.as_ref().map(|h| h.opaque()) == current_host {
|
||||
break;
|
||||
}
|
||||
let outer_host = match outer_host {
|
||||
Some(h) => h,
|
||||
None => return false,
|
||||
};
|
||||
// TODO(emilio): if worth it, we could early return if
|
||||
// host doesn't have the exportparts attribute.
|
||||
hosts.push(host);
|
||||
host = outer_host;
|
||||
}
|
||||
let outer_host = match outer_host {
|
||||
Some(h) => h,
|
||||
None => return false,
|
||||
};
|
||||
// TODO(emilio): if worth it, we could early return if
|
||||
// host doesn't have the exportparts attribute.
|
||||
hosts.push(host);
|
||||
host = outer_host;
|
||||
}
|
||||
|
||||
// Translate the part into the right scope.
|
||||
|
|
|
@ -2013,27 +2013,48 @@ where
|
|||
input.skip_whitespace();
|
||||
|
||||
let mut empty = true;
|
||||
if !parse_type_selector(parser, input, builder)? {
|
||||
if let Some(url) = parser.default_namespace() {
|
||||
// If there was no explicit type selector, but there is a
|
||||
// default namespace, there is an implicit "<defaultns>|*" type
|
||||
// selector.
|
||||
builder.push_simple_selector(Component::DefaultNamespace(url))
|
||||
}
|
||||
} else {
|
||||
if parse_type_selector(parser, input, builder)? {
|
||||
empty = false;
|
||||
}
|
||||
|
||||
let mut state = SelectorParsingState::empty();
|
||||
loop {
|
||||
let parse_result = match parse_one_simple_selector(parser, input, state)? {
|
||||
let result = match parse_one_simple_selector(parser, input, state)? {
|
||||
None => break,
|
||||
Some(result) => result,
|
||||
};
|
||||
|
||||
if empty {
|
||||
if let Some(url) = parser.default_namespace() {
|
||||
// If there was no explicit type selector, but there is a
|
||||
// default namespace, there is an implicit "<defaultns>|*" type
|
||||
// selector. Except for :host, where we ignore it.
|
||||
//
|
||||
// https://drafts.csswg.org/css-scoping/#host-element-in-tree:
|
||||
//
|
||||
// When considered within its own shadow trees, the shadow
|
||||
// host is featureless. Only the :host, :host(), and
|
||||
// :host-context() pseudo-classes are allowed to match it.
|
||||
//
|
||||
// https://drafts.csswg.org/selectors-4/#featureless:
|
||||
//
|
||||
// A featureless element does not match any selector at all,
|
||||
// except those it is explicitly defined to match. If a
|
||||
// given selector is allowed to match a featureless element,
|
||||
// it must do so while ignoring the default namespace.
|
||||
//
|
||||
if !matches!(
|
||||
result,
|
||||
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
|
||||
) {
|
||||
builder.push_simple_selector(Component::DefaultNamespace(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
empty = false;
|
||||
|
||||
match parse_result {
|
||||
match result {
|
||||
SimpleSelectorParseResult::SimpleSelector(s) => {
|
||||
builder.push_simple_selector(s);
|
||||
},
|
||||
|
|
|
@ -117,13 +117,6 @@ pub trait Element: Sized + Clone + Debug {
|
|||
case_sensitivity: CaseSensitivity,
|
||||
) -> bool;
|
||||
|
||||
/// Returns the mapping from the `exportparts` attribute in the regular
|
||||
/// direction, that is, inner-tree -> outer-tree.
|
||||
fn exported_part(
|
||||
&self,
|
||||
name: &<Self::Impl as SelectorImpl>::PartName,
|
||||
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
|
||||
|
||||
/// Returns the mapping from the `exportparts` attribute in the reverse
|
||||
/// direction, that is, in an outer-tree -> inner-tree direction.
|
||||
fn imported_part(
|
||||
|
|
|
@ -28,11 +28,11 @@ mod build_gecko {
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PYTHON: String = env::var("PYTHON").ok().unwrap_or_else(|| {
|
||||
pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| {
|
||||
let candidates = if cfg!(windows) {
|
||||
["python2.7.exe", "python27.exe", "python.exe"]
|
||||
["python3.exe"]
|
||||
} else {
|
||||
["python2.7", "python2", "python"]
|
||||
["python3"]
|
||||
};
|
||||
for &name in &candidates {
|
||||
if Command::new(name)
|
||||
|
@ -45,7 +45,7 @@ lazy_static! {
|
|||
}
|
||||
}
|
||||
panic!(
|
||||
"Can't find python (tried {})! Try fixing PATH or setting the PYTHON env var",
|
||||
"Can't find python (tried {})! Try fixing PATH or setting the PYTHON3 env var",
|
||||
candidates.join(", ")
|
||||
)
|
||||
});
|
||||
|
|
|
@ -408,7 +408,9 @@ impl ToCss for System {
|
|||
}
|
||||
|
||||
/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum Symbol {
|
||||
/// <string>
|
||||
|
@ -554,7 +556,9 @@ impl Parse for Fallback {
|
|||
}
|
||||
|
||||
/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
|
||||
|
||||
|
|
|
@ -579,7 +579,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
match result {
|
||||
Ok(new_value) => Arc::new(new_value),
|
||||
Err(..) => {
|
||||
map.remove(name);
|
||||
// Don't touch the map, this has the same effect as
|
||||
// making it compute to the inherited one.
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
@ -653,16 +654,22 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
None => return self.inherited.cloned(),
|
||||
};
|
||||
if self.may_have_cycles {
|
||||
substitute_all(&mut map, self.device);
|
||||
let inherited = self.inherited.as_ref().map(|m| &***m);
|
||||
substitute_all(&mut map, inherited, self.device);
|
||||
}
|
||||
Some(Arc::new(map))
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve all custom properties to either substituted or invalid.
|
||||
/// Resolve all custom properties to either substituted, invalid, or unset
|
||||
/// (meaning we should use the inherited value).
|
||||
///
|
||||
/// It does cycle dependencies removal at the same time as substitution.
|
||||
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Device) {
|
||||
fn substitute_all(
|
||||
custom_properties_map: &mut CustomPropertiesMap,
|
||||
inherited: Option<&CustomPropertiesMap>,
|
||||
device: &Device,
|
||||
) {
|
||||
// The cycle dependencies removal in this function is a variant
|
||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||
// listed in
|
||||
|
@ -698,6 +705,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
|
|||
/// all unfinished strong connected components.
|
||||
stack: SmallVec<[usize; 5]>,
|
||||
map: &'a mut CustomPropertiesMap,
|
||||
/// The inherited variables. We may need to restore some if we fail
|
||||
/// substitution.
|
||||
inherited: Option<&'a CustomPropertiesMap>,
|
||||
/// to resolve the environment to substitute `env()` variables.
|
||||
device: &'a Device,
|
||||
}
|
||||
|
@ -831,17 +841,25 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
|
|||
return None;
|
||||
}
|
||||
|
||||
// Now we have shown that this variable is not in a loop, and
|
||||
// all of its dependencies should have been resolved. We can
|
||||
// start substitution now.
|
||||
// Now we have shown that this variable is not in a loop, and all of its
|
||||
// dependencies should have been resolved. We can start substitution
|
||||
// now.
|
||||
let result = substitute_references_in_value(&value, &context.map, &context.device);
|
||||
|
||||
match result {
|
||||
Ok(computed_value) => {
|
||||
context.map.insert(name, Arc::new(computed_value));
|
||||
},
|
||||
Err(..) => {
|
||||
context.map.remove(&name);
|
||||
// This is invalid, reset it to the unset (inherited) value.
|
||||
let inherited = context.inherited.and_then(|m| m.get(&name)).cloned();
|
||||
match inherited {
|
||||
Some(computed_value) => {
|
||||
context.map.insert(name, computed_value);
|
||||
},
|
||||
None => {
|
||||
context.map.remove(&name);
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -859,6 +877,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Devi
|
|||
stack: SmallVec::new(),
|
||||
var_info: SmallVec::new(),
|
||||
map: custom_properties_map,
|
||||
inherited,
|
||||
device,
|
||||
};
|
||||
traverse(name, &mut context);
|
||||
|
|
|
@ -522,6 +522,14 @@ pub trait TElement:
|
|||
{
|
||||
}
|
||||
|
||||
/// Internal iterator for the part names that this element exports for a
|
||||
/// given part name.
|
||||
fn each_exported_part<F>(&self, _name: &Atom, _callback: F)
|
||||
where
|
||||
F: FnMut(&Atom),
|
||||
{
|
||||
}
|
||||
|
||||
/// Whether a given element may generate a pseudo-element.
|
||||
///
|
||||
/// This is useful to avoid computing, for example, pseudo styles for
|
||||
|
|
|
@ -89,7 +89,7 @@ pub fn traverse_dom<E, D>(
|
|||
// ThreadLocalStyleContext on the main thread. If the main thread
|
||||
// ThreadLocalStyleContext has not released its TLS borrow by that point,
|
||||
// we'll panic on double-borrow.
|
||||
let mut maybe_tls: Option<ScopedTLS<ThreadLocalStyleContext<E>>> = None;
|
||||
let mut tls_slots = None;
|
||||
let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context());
|
||||
let mut context = StyleContext {
|
||||
shared: traversal.shared_context(),
|
||||
|
@ -129,7 +129,7 @@ pub fn traverse_dom<E, D>(
|
|||
// depth for all the children.
|
||||
if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
|
||||
let pool = pool.unwrap();
|
||||
maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool));
|
||||
let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
|
||||
let root_opaque = root.as_node().opaque();
|
||||
let drain = discovered.drain(..);
|
||||
pool.install(|| {
|
||||
|
@ -151,10 +151,12 @@ pub fn traverse_dom<E, D>(
|
|||
scope,
|
||||
pool,
|
||||
traversal,
|
||||
maybe_tls.as_ref().unwrap(),
|
||||
&tls,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
tls_slots = Some(tls.into_slots());
|
||||
break;
|
||||
}
|
||||
nodes_remaining_at_current_depth = discovered.len();
|
||||
|
@ -164,9 +166,9 @@ pub fn traverse_dom<E, D>(
|
|||
// Collect statistics from thread-locals if requested.
|
||||
if dump_stats || report_stats {
|
||||
let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default());
|
||||
let parallel = maybe_tls.is_some();
|
||||
if let Some(tls) = maybe_tls {
|
||||
for mut slot in tls.into_slots().into_vec() {
|
||||
let parallel = tls_slots.is_some();
|
||||
if let Some(ref mut tls) = tls_slots {
|
||||
for slot in tls.iter_mut() {
|
||||
if let Some(cx) = slot.get_mut() {
|
||||
aggregate += cx.statistics.clone();
|
||||
}
|
||||
|
|
|
@ -137,6 +137,10 @@ bitflags! {
|
|||
const IN_AUTOFILL_STATE = 1 << 50;
|
||||
/// Non-standard & undocumented.
|
||||
const IN_AUTOFILL_PREVIEW_STATE = 1 << 51;
|
||||
/// :focus-visible
|
||||
///
|
||||
/// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
|
||||
const IN_FOCUS_VISIBLE_STATE = 1 << 52;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::context::QuirksMode;
|
|||
use crate::error_reporting::ParseErrorReporter;
|
||||
use crate::media_queries::MediaList;
|
||||
use crate::shared_lock::SharedRwLock;
|
||||
use crate::stylesheets::{Origin, Stylesheet, StylesheetLoader, UrlExtraData};
|
||||
use crate::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetLoader, UrlExtraData};
|
||||
use cssparser::{stylesheet_encoding, EncodingSupport};
|
||||
use servo_arc::Arc;
|
||||
use std::borrow::Cow;
|
||||
|
@ -78,6 +78,7 @@ impl Stylesheet {
|
|||
error_reporter,
|
||||
quirks_mode,
|
||||
0,
|
||||
AllowImportRules::Yes,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -100,6 +101,7 @@ impl Stylesheet {
|
|||
stylesheet_loader,
|
||||
error_reporter,
|
||||
0,
|
||||
AllowImportRules::Yes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,244 +10,9 @@
|
|||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use crate::gecko_bindings::bindings;
|
||||
use crate::gecko_bindings::structs::{self, Matrix4x4Components};
|
||||
use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
|
||||
use crate::gecko_bindings::structs::{nsresult, Matrix4x4Components};
|
||||
use crate::stylesheets::RulesMutateError;
|
||||
use crate::values::computed::transform::Matrix3D;
|
||||
use crate::values::computed::{Gradient, Image, TextAlign};
|
||||
use crate::values::generics::image::GenericImage;
|
||||
use crate::values::generics::rect::Rect;
|
||||
|
||||
impl nsStyleImage {
|
||||
/// Set a given Servo `Image` value into this `nsStyleImage`.
|
||||
pub fn set(&mut self, image: Image) {
|
||||
match image {
|
||||
GenericImage::Gradient(boxed_gradient) => self.set_gradient(boxed_gradient),
|
||||
GenericImage::Url(ref url) => unsafe {
|
||||
bindings::Gecko_SetLayerImageImageValue(self, url);
|
||||
},
|
||||
GenericImage::Rect(ref image_rect) => {
|
||||
unsafe {
|
||||
bindings::Gecko_SetLayerImageImageValue(self, &image_rect.url);
|
||||
bindings::Gecko_InitializeImageCropRect(self);
|
||||
|
||||
// Set CropRect
|
||||
let ref mut rect = *self.mCropRect.mPtr;
|
||||
*rect = Rect(
|
||||
image_rect.top,
|
||||
image_rect.right,
|
||||
image_rect.bottom,
|
||||
image_rect.left,
|
||||
);
|
||||
}
|
||||
},
|
||||
GenericImage::Element(ref element) => unsafe {
|
||||
bindings::Gecko_SetImageElement(self, element.as_ptr());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_gradient(&mut self, gradient: Box<Gradient>) {
|
||||
unsafe {
|
||||
bindings::Gecko_SetGradientImageValue(self, Box::into_raw(gradient));
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts into Image.
|
||||
pub unsafe fn into_image(self: &nsStyleImage) -> Option<Image> {
|
||||
use crate::gecko_bindings::structs::nsStyleImageType;
|
||||
use crate::values::computed::MozImageRect;
|
||||
|
||||
match self.mType {
|
||||
nsStyleImageType::eStyleImageType_Null => None,
|
||||
nsStyleImageType::eStyleImageType_Image => {
|
||||
let url = self.__bindgen_anon_1.mImage.as_ref().clone();
|
||||
if self.mCropRect.mPtr.is_null() {
|
||||
Some(GenericImage::Url(url))
|
||||
} else {
|
||||
let rect = &*self.mCropRect.mPtr;
|
||||
Some(GenericImage::Rect(Box::new(MozImageRect {
|
||||
url,
|
||||
top: rect.0,
|
||||
right: rect.1,
|
||||
bottom: rect.2,
|
||||
left: rect.3,
|
||||
})))
|
||||
}
|
||||
},
|
||||
nsStyleImageType::eStyleImageType_Gradient => {
|
||||
let gradient: &Gradient = &**self.__bindgen_anon_1.mGradient.as_ref();
|
||||
Some(GenericImage::Gradient(Box::new(gradient.clone())))
|
||||
},
|
||||
nsStyleImageType::eStyleImageType_Element => {
|
||||
use crate::gecko_string_cache::Atom;
|
||||
let atom = bindings::Gecko_GetImageElement(self);
|
||||
Some(GenericImage::Element(Atom::from_raw(atom)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod basic_shape {
|
||||
//! Conversions from and to CSS shape representations.
|
||||
use crate::gecko_bindings::structs::{
|
||||
StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
|
||||
};
|
||||
use crate::values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape};
|
||||
use crate::values::computed::motion::OffsetPath;
|
||||
use crate::values::generics::basic_shape::{GeometryBox, Path, ShapeBox, ShapeSource};
|
||||
use crate::values::specified::SVGPathData;
|
||||
|
||||
impl StyleShapeSource {
|
||||
/// Convert StyleShapeSource to ShapeSource except URL and Image
|
||||
/// types.
|
||||
fn into_shape_source<ReferenceBox, ImageOrUrl>(
|
||||
&self,
|
||||
) -> Option<ShapeSource<BasicShape, ReferenceBox, ImageOrUrl>>
|
||||
where
|
||||
ReferenceBox: From<StyleGeometryBox>,
|
||||
{
|
||||
match self.mType {
|
||||
StyleShapeSourceType::None => Some(ShapeSource::None),
|
||||
StyleShapeSourceType::Box => Some(ShapeSource::Box(self.mReferenceBox.into())),
|
||||
StyleShapeSourceType::Shape => {
|
||||
let other_shape = unsafe { &*self.__bindgen_anon_1.mBasicShape.as_ref().mPtr };
|
||||
let shape = Box::new(other_shape.clone());
|
||||
let reference_box = if self.mReferenceBox == StyleGeometryBox::NoBox {
|
||||
None
|
||||
} else {
|
||||
Some(self.mReferenceBox.into())
|
||||
};
|
||||
Some(ShapeSource::Shape(shape, reference_box))
|
||||
},
|
||||
StyleShapeSourceType::Image => None,
|
||||
StyleShapeSourceType::Path => {
|
||||
let path = self.to_svg_path().expect("expect an SVGPathData");
|
||||
let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule;
|
||||
Some(ShapeSource::Path(Path { fill, path }))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a SVGPathData from StyleShapeSource if possible.
|
||||
fn to_svg_path(&self) -> Option<SVGPathData> {
|
||||
match self.mType {
|
||||
StyleShapeSourceType::Path => {
|
||||
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
||||
Some(SVGPathData(gecko_path.mPath.clone()))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StyleShapeSource> for ClippingShape {
|
||||
fn from(other: &'a StyleShapeSource) -> Self {
|
||||
match other.mType {
|
||||
StyleShapeSourceType::Image => unsafe {
|
||||
use crate::values::generics::image::Image as GenericImage;
|
||||
|
||||
let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr;
|
||||
let image = shape_image.into_image().expect("Cannot convert to Image");
|
||||
match image {
|
||||
GenericImage::Url(url) => ShapeSource::ImageOrUrl(url.0),
|
||||
_ => panic!("ClippingShape doesn't support non-url images"),
|
||||
}
|
||||
},
|
||||
_ => other
|
||||
.into_shape_source()
|
||||
.expect("Couldn't convert to StyleSource!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StyleShapeSource> for FloatAreaShape {
|
||||
fn from(other: &'a StyleShapeSource) -> Self {
|
||||
match other.mType {
|
||||
StyleShapeSourceType::Image => unsafe {
|
||||
let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr;
|
||||
let image = shape_image.into_image().expect("Cannot convert to Image");
|
||||
ShapeSource::ImageOrUrl(image)
|
||||
},
|
||||
_ => other
|
||||
.into_shape_source()
|
||||
.expect("Couldn't convert to StyleSource!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StyleShapeSource> for OffsetPath {
|
||||
fn from(other: &'a StyleShapeSource) -> Self {
|
||||
use crate::values::generics::motion::GenericOffsetPath;
|
||||
match other.mType {
|
||||
StyleShapeSourceType::Path => GenericOffsetPath::Path(
|
||||
other.to_svg_path().expect("Cannot convert to SVGPathData"),
|
||||
),
|
||||
StyleShapeSourceType::None => OffsetPath::none(),
|
||||
StyleShapeSourceType::Shape |
|
||||
StyleShapeSourceType::Box |
|
||||
StyleShapeSourceType::Image => unreachable!("Unsupported offset-path type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShapeBox> for StyleGeometryBox {
|
||||
fn from(reference: ShapeBox) -> Self {
|
||||
use crate::gecko_bindings::structs::StyleGeometryBox::*;
|
||||
match reference {
|
||||
ShapeBox::ContentBox => ContentBox,
|
||||
ShapeBox::PaddingBox => PaddingBox,
|
||||
ShapeBox::BorderBox => BorderBox,
|
||||
ShapeBox::MarginBox => MarginBox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GeometryBox> for StyleGeometryBox {
|
||||
fn from(reference: GeometryBox) -> Self {
|
||||
use crate::gecko_bindings::structs::StyleGeometryBox::*;
|
||||
match reference {
|
||||
GeometryBox::ShapeBox(shape_box) => From::from(shape_box),
|
||||
GeometryBox::FillBox => FillBox,
|
||||
GeometryBox::StrokeBox => StrokeBox,
|
||||
GeometryBox::ViewBox => ViewBox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will panic on NoBox
|
||||
// Ideally these would be implemented on Option<T>,
|
||||
// but coherence doesn't like that and TryFrom isn't stable
|
||||
impl From<StyleGeometryBox> for GeometryBox {
|
||||
fn from(reference: StyleGeometryBox) -> Self {
|
||||
use crate::gecko_bindings::structs::StyleGeometryBox::*;
|
||||
match reference {
|
||||
ContentBox => GeometryBox::ShapeBox(ShapeBox::ContentBox),
|
||||
PaddingBox => GeometryBox::ShapeBox(ShapeBox::PaddingBox),
|
||||
BorderBox => GeometryBox::ShapeBox(ShapeBox::BorderBox),
|
||||
MarginBox => GeometryBox::ShapeBox(ShapeBox::MarginBox),
|
||||
FillBox => GeometryBox::FillBox,
|
||||
StrokeBox => GeometryBox::StrokeBox,
|
||||
ViewBox => GeometryBox::ViewBox,
|
||||
_ => panic!("Unexpected StyleGeometryBox while converting to GeometryBox"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StyleGeometryBox> for ShapeBox {
|
||||
fn from(reference: StyleGeometryBox) -> Self {
|
||||
use crate::gecko_bindings::structs::StyleGeometryBox::*;
|
||||
match reference {
|
||||
ContentBox => ShapeBox::ContentBox,
|
||||
PaddingBox => ShapeBox::PaddingBox,
|
||||
BorderBox => ShapeBox::BorderBox,
|
||||
MarginBox => ShapeBox::MarginBox,
|
||||
_ => panic!("Unexpected StyleGeometryBox while converting to ShapeBox"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RulesMutateError> for nsresult {
|
||||
fn from(other: RulesMutateError) -> Self {
|
||||
|
@ -260,39 +25,6 @@ impl From<RulesMutateError> for nsresult {
|
|||
}
|
||||
}
|
||||
|
||||
impl TextAlign {
|
||||
/// Obtain a specified value from a Gecko keyword value
|
||||
///
|
||||
/// Intended for use with presentation attributes, not style structs
|
||||
pub fn from_gecko_keyword(kw: u32) -> Self {
|
||||
match kw {
|
||||
structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left,
|
||||
structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right,
|
||||
structs::NS_STYLE_TEXT_ALIGN_CENTER => TextAlign::Center,
|
||||
structs::NS_STYLE_TEXT_ALIGN_JUSTIFY => TextAlign::Justify,
|
||||
structs::NS_STYLE_TEXT_ALIGN_MOZ_LEFT => TextAlign::MozLeft,
|
||||
structs::NS_STYLE_TEXT_ALIGN_MOZ_RIGHT => TextAlign::MozRight,
|
||||
structs::NS_STYLE_TEXT_ALIGN_MOZ_CENTER => TextAlign::MozCenter,
|
||||
structs::NS_STYLE_TEXT_ALIGN_CHAR => TextAlign::Char,
|
||||
structs::NS_STYLE_TEXT_ALIGN_END => TextAlign::End,
|
||||
_ => panic!("Found unexpected value in style struct for text-align property"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to String from given chars pointer.
|
||||
pub unsafe fn string_from_chars_pointer(p: *const u16) -> String {
|
||||
use std::slice;
|
||||
let mut length = 0;
|
||||
let mut iter = p;
|
||||
while *iter != 0 {
|
||||
length += 1;
|
||||
iter = iter.offset(1);
|
||||
}
|
||||
let char_vec = slice::from_raw_parts(p, length as usize);
|
||||
String::from_utf16_lossy(char_vec)
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Matrix4x4Components> for Matrix3D {
|
||||
fn from(m: &'a Matrix4x4Components) -> Matrix3D {
|
||||
Matrix3D {
|
||||
|
|
|
@ -310,6 +310,17 @@ impl Device {
|
|||
|
||||
/// Returns safe area insets
|
||||
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
|
||||
SideOffsets2D::zero()
|
||||
let pc = match self.pres_context() {
|
||||
Some(pc) => pc,
|
||||
None => return SideOffsets2D::zero(),
|
||||
};
|
||||
let mut top = 0.0;
|
||||
let mut right = 0.0;
|
||||
let mut bottom = 0.0;
|
||||
let mut left = 0.0;
|
||||
unsafe {
|
||||
bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left)
|
||||
};
|
||||
SideOffsets2D::new(top, right, bottom, left)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,77 +30,78 @@ macro_rules! apply_non_ts_list {
|
|||
($apply_macro:ident) => {
|
||||
$apply_macro! {
|
||||
[
|
||||
("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("link", Link, link, IN_UNVISITED_STATE, _),
|
||||
("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _),
|
||||
("visited", Visited, visited, IN_VISITED_STATE, _),
|
||||
("active", Active, active, IN_ACTIVE_STATE, _),
|
||||
("checked", Checked, checked, IN_CHECKED_STATE, _),
|
||||
("defined", Defined, defined, IN_DEFINED_STATE, _),
|
||||
("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
|
||||
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
|
||||
("focus", Focus, focus, IN_FOCUS_STATE, _),
|
||||
("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _),
|
||||
("hover", Hover, hover, IN_HOVER_STATE, _),
|
||||
("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _),
|
||||
("target", Target, target, IN_TARGET_STATE, _),
|
||||
("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
|
||||
("-moz-devtools-highlighted", MozDevtoolsHighlighted, mozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, mozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("fullscreen", Fullscreen, fullscreen, IN_FULLSCREEN_STATE, _),
|
||||
("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("link", Link, IN_UNVISITED_STATE, _),
|
||||
("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _),
|
||||
("visited", Visited, IN_VISITED_STATE, _),
|
||||
("active", Active, IN_ACTIVE_STATE, _),
|
||||
("checked", Checked, IN_CHECKED_STATE, _),
|
||||
("defined", Defined, IN_DEFINED_STATE, _),
|
||||
("disabled", Disabled, IN_DISABLED_STATE, _),
|
||||
("enabled", Enabled, IN_ENABLED_STATE, _),
|
||||
("focus", Focus, IN_FOCUS_STATE, _),
|
||||
("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _),
|
||||
("focus-visible", FocusVisible, IN_FOCUS_VISIBLE_STATE, _),
|
||||
("hover", Hover, IN_HOVER_STATE, _),
|
||||
("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _),
|
||||
("target", Target, IN_TARGET_STATE, _),
|
||||
("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _),
|
||||
("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _),
|
||||
// TODO(emilio): This is inconsistently named (the capital R).
|
||||
("-moz-focusring", MozFocusRing, mozFocusRing, IN_FOCUSRING_STATE, _),
|
||||
("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _),
|
||||
("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _),
|
||||
("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-has-dir-attr", MozHasDirAttr, mozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-ltr", MozDirAttrLTR, mozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-rtl", MozDirAttrRTL, mozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, mozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-autofill", MozAutofill, mozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-autofill-preview", MozAutofillPreview, mozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _),
|
||||
("-moz-broken", MozBroken, IN_BROKEN_STATE, _),
|
||||
("-moz-loading", MozLoading, IN_LOADING_STATE, _),
|
||||
("-moz-suppressed", MozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-has-dir-attr", MozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-ltr", MozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-rtl", MozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-autofill", MozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
|
||||
("-moz-handler-clicktoplay", MozHandlerClickToPlay, mozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, mozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, mozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-clicktoplay", MozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
|
||||
("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-math-increment-script-level", MozMathIncrementScriptLevel, mozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),
|
||||
("-moz-handler-disabled", MozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-blocked", MozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-handler-crashed", MozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),
|
||||
|
||||
("required", Required, required, IN_REQUIRED_STATE, _),
|
||||
("optional", Optional, optional, IN_OPTIONAL_STATE, _),
|
||||
("valid", Valid, valid, IN_VALID_STATE, _),
|
||||
("invalid", Invalid, invalid, IN_INVALID_STATE, _),
|
||||
("in-range", InRange, inRange, IN_INRANGE_STATE, _),
|
||||
("out-of-range", OutOfRange, outOfRange, IN_OUTOFRANGE_STATE, _),
|
||||
("default", Default, defaultPseudo, IN_DEFAULT_STATE, _),
|
||||
("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
|
||||
("-moz-read-only", MozReadOnly, mozReadOnly, IN_MOZ_READONLY_STATE, _),
|
||||
("-moz-read-write", MozReadWrite, mozReadWrite, IN_MOZ_READWRITE_STATE, _),
|
||||
("-moz-submit-invalid", MozSubmitInvalid, mozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
|
||||
("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _),
|
||||
("-moz-ui-invalid", MozUIInvalid, mozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
|
||||
("-moz-meter-optimum", MozMeterOptimum, mozMeterOptimum, IN_OPTIMUM_STATE, _),
|
||||
("-moz-meter-sub-optimum", MozMeterSubOptimum, mozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
|
||||
("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, mozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _),
|
||||
("required", Required, IN_REQUIRED_STATE, _),
|
||||
("optional", Optional, IN_OPTIONAL_STATE, _),
|
||||
("valid", Valid, IN_VALID_STATE, _),
|
||||
("invalid", Invalid, IN_INVALID_STATE, _),
|
||||
("in-range", InRange, IN_INRANGE_STATE, _),
|
||||
("out-of-range", OutOfRange, IN_OUTOFRANGE_STATE, _),
|
||||
("default", Default, IN_DEFAULT_STATE, _),
|
||||
("placeholder-shown", PlaceholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
|
||||
("-moz-read-only", MozReadOnly, IN_MOZ_READONLY_STATE, _),
|
||||
("-moz-read-write", MozReadWrite, IN_MOZ_READWRITE_STATE, _),
|
||||
("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
|
||||
("-moz-ui-valid", MozUIValid, IN_MOZ_UI_VALID_STATE, _),
|
||||
("-moz-ui-invalid", MozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
|
||||
("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _),
|
||||
("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
|
||||
("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _),
|
||||
|
||||
("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-user-disabled", MozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
|
||||
("-moz-first-node", MozFirstNode, firstNode, _, _),
|
||||
("-moz-last-node", MozLastNode, lastNode, _, _),
|
||||
("-moz-only-whitespace", MozOnlyWhitespace, mozOnlyWhitespace, _, _),
|
||||
("-moz-native-anonymous", MozNativeAnonymous, mozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, mozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, mozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-is-html", MozIsHTML, mozIsHTML, _, _),
|
||||
("-moz-placeholder", MozPlaceholder, mozPlaceholder, _, _),
|
||||
("-moz-lwtheme", MozLWTheme, mozLWTheme, _, _),
|
||||
("-moz-lwtheme-brighttext", MozLWThemeBrightText, mozLWThemeBrightText, _, _),
|
||||
("-moz-lwtheme-darktext", MozLWThemeDarkText, mozLWThemeDarkText, _, _),
|
||||
("-moz-window-inactive", MozWindowInactive, mozWindowInactive, _, _),
|
||||
("-moz-first-node", MozFirstNode, _, _),
|
||||
("-moz-last-node", MozLastNode, _, _),
|
||||
("-moz-only-whitespace", MozOnlyWhitespace, _, _),
|
||||
("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-is-html", MozIsHTML, _, _),
|
||||
("-moz-placeholder", MozPlaceholder, _, _),
|
||||
("-moz-lwtheme", MozLWTheme, _, _),
|
||||
("-moz-lwtheme-brighttext", MozLWThemeBrightText, _, _),
|
||||
("-moz-lwtheme-darktext", MozLWThemeDarkText, _, _),
|
||||
("-moz-window-inactive", MozWindowInactive, _, _),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,12 +134,6 @@ impl PseudoElement {
|
|||
*self == PseudoElement::FirstLine
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is ::-moz-fieldset-content.
|
||||
#[inline]
|
||||
pub fn is_fieldset_content(&self) -> bool {
|
||||
*self == PseudoElement::FieldsetContent
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
|
||||
#[inline]
|
||||
pub fn is_color_swatch(&self) -> bool {
|
||||
|
|
|
@ -94,7 +94,7 @@ class FileAvoidWrite(BytesIO):
|
|||
self.name = filename
|
||||
|
||||
def write(self, buf):
|
||||
if isinstance(buf, unicode):
|
||||
if isinstance(buf, str):
|
||||
buf = buf.encode('utf-8')
|
||||
BytesIO.write(self, buf)
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ bitflags! {
|
|||
pub type Lang = Atom;
|
||||
|
||||
macro_rules! pseudo_class_name {
|
||||
([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
|
||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
/// Our representation of a non tree-structural pseudo-class.
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum NonTSPseudoClass {
|
||||
|
@ -72,7 +72,7 @@ impl ToCss for NonTSPseudoClass {
|
|||
W: fmt::Write,
|
||||
{
|
||||
macro_rules! pseudo_class_serialize {
|
||||
([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
|
||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match *self {
|
||||
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
||||
NonTSPseudoClass::Lang(ref s) => {
|
||||
|
@ -134,7 +134,7 @@ impl NonTSPseudoClass {
|
|||
/// in a particular state.
|
||||
pub fn parse_non_functional(name: &str) -> Option<Self> {
|
||||
macro_rules! pseudo_class_parse {
|
||||
([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
|
||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match_ignore_ascii_case! { &name,
|
||||
$($css => Some(NonTSPseudoClass::$name),)*
|
||||
"-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
|
||||
|
@ -156,7 +156,7 @@ impl NonTSPseudoClass {
|
|||
};
|
||||
}
|
||||
macro_rules! pseudo_class_check_is_enabled_in {
|
||||
([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
|
||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match *self {
|
||||
$(NonTSPseudoClass::$name => check_flag!($flags),)*
|
||||
NonTSPseudoClass::MozLocaleDir(_) |
|
||||
|
@ -172,6 +172,9 @@ impl NonTSPseudoClass {
|
|||
/// Returns whether the pseudo-class is enabled in content sheets.
|
||||
#[inline]
|
||||
fn is_enabled_in_content(&self) -> bool {
|
||||
if matches!(*self, NonTSPseudoClass::FocusVisible) {
|
||||
return static_prefs::pref!("layout.css.focus-visible.enabled");
|
||||
}
|
||||
!self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
|
||||
}
|
||||
|
||||
|
@ -186,7 +189,7 @@ impl NonTSPseudoClass {
|
|||
};
|
||||
}
|
||||
macro_rules! pseudo_class_state {
|
||||
([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
|
||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match *self {
|
||||
$(NonTSPseudoClass::$name => flag!($state),)*
|
||||
NonTSPseudoClass::Dir(..) |
|
||||
|
|
|
@ -193,11 +193,6 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
|||
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]
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::imported_part(&*self.mAttrs, name)
|
||||
|
|
|
@ -83,16 +83,26 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn exported_part(
|
||||
pub(super) fn each_exported_part(
|
||||
attrs: &[structs::AttrArray_InternalAttr],
|
||||
name: &Atom,
|
||||
) -> Option<Atom> {
|
||||
let attr = find_attr(attrs, &atom!("exportparts"))?;
|
||||
let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) };
|
||||
if atom.is_null() {
|
||||
return None;
|
||||
mut callback: impl FnMut(&Atom),
|
||||
) {
|
||||
let attr = match find_attr(attrs, &atom!("exportparts")) {
|
||||
Some(attr) => attr,
|
||||
None => return,
|
||||
};
|
||||
let mut length = 0;
|
||||
let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) };
|
||||
if atoms.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for atom in std::slice::from_raw_parts(atoms, length) {
|
||||
Atom::with(*atom, &mut callback)
|
||||
}
|
||||
}
|
||||
Some(unsafe { Atom::from_raw(atom) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -72,7 +72,7 @@ use crate::values::computed::Length;
|
|||
use crate::values::specified::length::FontBaseSize;
|
||||
use crate::CaseSensitivityExt;
|
||||
use app_units::Au;
|
||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
|
||||
use selectors::attr::{CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::matching::VisitedHandlingMode;
|
||||
|
@ -557,8 +557,9 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
impl<'le> GeckoElement<'le> {
|
||||
/// Gets the raw `ElementData` refcell for the element.
|
||||
#[inline(always)]
|
||||
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
|
||||
pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
|
||||
unsafe { self.0.mServoData.get().as_ref() }
|
||||
}
|
||||
|
||||
|
@ -1281,6 +1282,14 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
snapshot_helpers::each_class_or_part(attr, callback)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn each_exported_part<F>(&self, name: &Atom, callback: F)
|
||||
where
|
||||
F: FnMut(&Atom),
|
||||
{
|
||||
snapshot_helpers::each_exported_part(self.attrs(), name, callback)
|
||||
}
|
||||
|
||||
fn each_part<F>(&self, callback: F)
|
||||
where
|
||||
F: FnMut(&Atom),
|
||||
|
@ -1383,21 +1392,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
panic!("Atomic child count not implemented in Gecko");
|
||||
}
|
||||
|
||||
/// Whether there is an ElementData container.
|
||||
fn has_data(&self) -> bool {
|
||||
self.get_data().is_some()
|
||||
}
|
||||
|
||||
/// Immutably borrows the ElementData.
|
||||
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
|
||||
self.get_data().map(|x| x.borrow())
|
||||
}
|
||||
|
||||
/// Mutably borrows the ElementData.
|
||||
fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
|
||||
self.get_data().map(|x| x.borrow_mut())
|
||||
}
|
||||
|
||||
unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
|
||||
if !self.has_data() {
|
||||
debug!("Creating ElementData for {:?}", self);
|
||||
|
@ -1634,6 +1628,22 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
.any(|property| !transitions_to_keep.contains(*property))
|
||||
}
|
||||
|
||||
/// Whether there is an ElementData container.
|
||||
#[inline]
|
||||
fn has_data(&self) -> bool {
|
||||
self.get_data().is_some()
|
||||
}
|
||||
|
||||
/// Immutably borrows the ElementData.
|
||||
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
|
||||
self.get_data().map(|x| x.borrow())
|
||||
}
|
||||
|
||||
/// Mutably borrows the ElementData.
|
||||
fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
|
||||
self.get_data().map(|x| x.borrow_mut())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lang_attr(&self) -> Option<AttrValue> {
|
||||
let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
|
||||
|
@ -2064,6 +2074,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
NonTSPseudoClass::MozReadOnly |
|
||||
NonTSPseudoClass::MozReadWrite |
|
||||
NonTSPseudoClass::FocusWithin |
|
||||
NonTSPseudoClass::FocusVisible |
|
||||
NonTSPseudoClass::MozDragOver |
|
||||
NonTSPseudoClass::MozDevtoolsHighlighted |
|
||||
NonTSPseudoClass::MozStyleeditorTransitioning |
|
||||
|
@ -2225,11 +2236,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
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]
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::imported_part(self.attrs(), name)
|
||||
|
|
|
@ -24,7 +24,18 @@ macro_rules! ns {
|
|||
}
|
||||
|
||||
/// A Gecko namespace is just a wrapped atom.
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Eq,
|
||||
Hash,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
pub struct Namespace(pub Atom);
|
||||
|
||||
|
|
|
@ -62,9 +62,6 @@ pub trait ElementSnapshot: Sized {
|
|||
/// called if `has_attrs()` returns true.
|
||||
fn is_part(&self, name: &Atom) -> bool;
|
||||
|
||||
/// See Element::exported_part.
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom>;
|
||||
|
||||
/// See Element::imported_part.
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom>;
|
||||
|
||||
|
@ -371,13 +368,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name),
|
||||
_ => self.element.exported_part(name),
|
||||
}
|
||||
}
|
||||
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
|
||||
|
|
Binary file not shown.
BIN
components/style/properties/Mako-1.1.2-py2.py3-none-any.whl
Normal file
BIN
components/style/properties/Mako-1.1.2-py2.py3-none-any.whl
Normal file
Binary file not shown.
|
@ -8,7 +8,7 @@ import re
|
|||
import sys
|
||||
|
||||
BASE = os.path.dirname(__file__.replace('\\', '/'))
|
||||
sys.path.insert(0, os.path.join(BASE, "Mako-0.9.1.zip"))
|
||||
sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl"))
|
||||
sys.path.insert(0, BASE) # For importing `data.py`
|
||||
|
||||
from mako import exceptions
|
||||
|
@ -130,7 +130,7 @@ def main():
|
|||
|
||||
|
||||
def abort(message):
|
||||
sys.stderr.write(message + b"\n")
|
||||
print(message, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
@ -146,18 +146,18 @@ def render(filename, **context):
|
|||
strict_undefined=True)
|
||||
# Uncomment to debug generated Python code:
|
||||
# write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
|
||||
return template.render(**context).encode("utf8")
|
||||
return template.render(**context)
|
||||
except Exception:
|
||||
# Uncomment to see a traceback in generated Python code:
|
||||
# raise
|
||||
abort(exceptions.text_error_template().render().encode("utf8"))
|
||||
abort(exceptions.text_error_template().render())
|
||||
|
||||
|
||||
def write(directory, filename, content):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
full_path = os.path.join(directory, filename)
|
||||
open(full_path, "wb").write(content)
|
||||
open(full_path, "w", encoding="utf-8").write(content)
|
||||
|
||||
python_addr = RE_PYTHON_ADDR.search(content)
|
||||
if python_addr:
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::media_queries::Device;
|
|||
use crate::properties::{ComputedValues, StyleBuilder};
|
||||
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
|
||||
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
|
||||
use crate::properties::CASCADE_PROPERTY;
|
||||
use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags};
|
||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use crate::rule_tree::StrongRuleNode;
|
||||
use crate::selector_parser::PseudoElement;
|
||||
|
@ -337,89 +337,80 @@ where
|
|||
context.builder.build()
|
||||
}
|
||||
|
||||
/// How should a declaration behave when ignoring document colors?
|
||||
enum DeclarationApplication {
|
||||
/// We should apply the declaration.
|
||||
Apply,
|
||||
/// We should ignore the declaration.
|
||||
Ignore,
|
||||
/// We should apply the following declaration, only if any other declaration
|
||||
/// hasn't set it before.
|
||||
ApplyUnlessOverriden(PropertyDeclaration),
|
||||
}
|
||||
/// For ignored colors mode, we sometimes want to do something equivalent to
|
||||
/// "revert-or-initial", where we `revert` for a given origin, but then apply a
|
||||
/// given initial value if nothing in other origins did override it.
|
||||
///
|
||||
/// This is a bit of a clunky way of achieving this.
|
||||
type DeclarationsToApplyUnlessOverriden = SmallVec::<[PropertyDeclaration; 2]>;
|
||||
|
||||
fn application_when_ignoring_colors(
|
||||
fn tweak_when_ignoring_colors(
|
||||
builder: &StyleBuilder,
|
||||
longhand_id: LonghandId,
|
||||
origin: Origin,
|
||||
declaration: &PropertyDeclaration,
|
||||
) -> DeclarationApplication {
|
||||
declaration: &mut Cow<PropertyDeclaration>,
|
||||
declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
|
||||
) {
|
||||
if !longhand_id.ignored_when_document_colors_disabled() {
|
||||
return DeclarationApplication::Apply;
|
||||
return;
|
||||
}
|
||||
|
||||
let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
|
||||
if is_ua_or_user_rule {
|
||||
return DeclarationApplication::Apply;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't override background-color on ::-moz-color-swatch. It is set as an
|
||||
// author style (via the style attribute), but it's pretty important for it
|
||||
// to show up for obvious reasons :)
|
||||
if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
|
||||
return DeclarationApplication::Apply;
|
||||
return;
|
||||
}
|
||||
|
||||
// Treat background-color a bit differently. If the specified color is
|
||||
// anything other than a fully transparent color, convert it into the
|
||||
// Device's default background color.
|
||||
// Also: for now, we treat background-image a bit differently, too.
|
||||
// background-image is marked as ignored, but really, we only ignore
|
||||
// it when backplates are disabled (since then text may be unreadable over
|
||||
// a background image, if we're ignoring document colors).
|
||||
// Here we check backplate status to decide if ignoring background-image
|
||||
// is the right decision.
|
||||
match *declaration {
|
||||
// A few special-cases ahead.
|
||||
match **declaration {
|
||||
// We honor color and background-color: transparent, and
|
||||
// "revert-or-initial" otherwise.
|
||||
PropertyDeclaration::BackgroundColor(ref color) => {
|
||||
if color.is_transparent() {
|
||||
return DeclarationApplication::Apply;
|
||||
if !color.is_transparent() {
|
||||
let color = builder.device.default_background_color();
|
||||
declarations_to_apply_unless_overriden.push(
|
||||
PropertyDeclaration::BackgroundColor(color.into())
|
||||
)
|
||||
}
|
||||
let color = builder.device.default_background_color();
|
||||
DeclarationApplication::ApplyUnlessOverriden(
|
||||
PropertyDeclaration::BackgroundColor(color.into())
|
||||
)
|
||||
}
|
||||
PropertyDeclaration::Color(ref color) => {
|
||||
// otherwise.
|
||||
if color.0.is_transparent() {
|
||||
return DeclarationApplication::Apply;
|
||||
}
|
||||
if builder.get_parent_inherited_text().clone_color().alpha != 0 {
|
||||
return DeclarationApplication::Ignore;
|
||||
return;
|
||||
}
|
||||
let color = builder.device.default_color();
|
||||
DeclarationApplication::ApplyUnlessOverriden(
|
||||
declarations_to_apply_unless_overriden.push(
|
||||
PropertyDeclaration::Color(specified::ColorPropertyValue(color.into()))
|
||||
)
|
||||
},
|
||||
// In the future, if/when we remove the backplate pref, we can remove this
|
||||
// special case along with the 'ignored_when_colors_disabled=True' mako line
|
||||
// for the "background-image" property.
|
||||
// We honor url background-images if backplating.
|
||||
#[cfg(feature = "gecko")]
|
||||
PropertyDeclaration::BackgroundImage(..) => {
|
||||
PropertyDeclaration::BackgroundImage(ref bkg) => {
|
||||
use crate::values::generics::image::Image;
|
||||
if static_prefs::pref!("browser.display.permit_backplate") {
|
||||
DeclarationApplication::Apply
|
||||
} else {
|
||||
DeclarationApplication::Ignore
|
||||
if bkg.0.iter().all(|image| matches!(*image, Image::Url(..))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => DeclarationApplication::Ignore,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
*declaration.to_mut() = PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
|
||||
|
||||
}
|
||||
|
||||
struct Cascade<'a, 'b: 'a> {
|
||||
context: &'a mut computed::Context<'b>,
|
||||
cascade_mode: CascadeMode<'a>,
|
||||
seen: LonghandIdSet,
|
||||
author_specified: LonghandIdSet,
|
||||
reverted: PerOrigin<LonghandIdSet>,
|
||||
}
|
||||
|
||||
|
@ -429,6 +420,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
context,
|
||||
cascade_mode,
|
||||
seen: LonghandIdSet::default(),
|
||||
author_specified: LonghandIdSet::default(),
|
||||
reverted: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +483,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
|
||||
let ignore_colors = !self.context.builder.device.use_document_colors();
|
||||
let mut declarations_to_apply_unless_overriden =
|
||||
SmallVec::<[PropertyDeclaration; 2]>::new();
|
||||
DeclarationsToApplyUnlessOverriden::new();
|
||||
|
||||
for (declaration, origin) in declarations {
|
||||
let declaration_id = declaration.id();
|
||||
|
@ -533,26 +525,23 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let declaration = self.substitute_variables_if_needed(declaration);
|
||||
let mut declaration = self.substitute_variables_if_needed(declaration);
|
||||
|
||||
// When document colors are disabled, do special handling of
|
||||
// properties that are marked as ignored in that mode.
|
||||
if ignore_colors {
|
||||
let application = application_when_ignoring_colors(
|
||||
tweak_when_ignoring_colors(
|
||||
&self.context.builder,
|
||||
longhand_id,
|
||||
origin,
|
||||
&declaration,
|
||||
&mut declaration,
|
||||
&mut declarations_to_apply_unless_overriden,
|
||||
);
|
||||
debug_assert_eq!(
|
||||
declaration.id(),
|
||||
PropertyDeclarationId::Longhand(longhand_id),
|
||||
"Shouldn't change the declaration id!",
|
||||
);
|
||||
|
||||
match application {
|
||||
DeclarationApplication::Ignore => continue,
|
||||
DeclarationApplication::Apply => {},
|
||||
DeclarationApplication::ApplyUnlessOverriden(decl) => {
|
||||
declarations_to_apply_unless_overriden.push(decl);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let css_wide_keyword = declaration.get_css_wide_keyword();
|
||||
|
@ -569,6 +558,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
}
|
||||
|
||||
self.seen.insert(physical_longhand_id);
|
||||
if origin == Origin::Author {
|
||||
self.author_specified.insert(physical_longhand_id);
|
||||
}
|
||||
|
||||
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| {
|
||||
match css_wide_keyword {
|
||||
|
@ -691,6 +683,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
if let Some(svg) = builder.get_svg_if_mutated() {
|
||||
svg.fill_arrays();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if self.author_specified.contains_any(LonghandIdSet::border_background_properties()) {
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
|
||||
}
|
||||
if self.author_specified.contains_any(LonghandIdSet::padding_properties()) {
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -711,12 +711,26 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
None => return false,
|
||||
};
|
||||
|
||||
let cached_style = match cache.find(guards, &self.context.builder) {
|
||||
let builder = &mut self.context.builder;
|
||||
|
||||
let cached_style = match cache.find(guards, &builder) {
|
||||
Some(style) => style,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
self.context.builder.copy_reset_from(cached_style);
|
||||
builder.copy_reset_from(cached_style);
|
||||
|
||||
// We're using the same reset style as another element, and we'll skip
|
||||
// applying the relevant properties. So we need to do the relevant
|
||||
// bookkeeping here to keep these two bits correct.
|
||||
//
|
||||
// Note that all the properties involved are non-inherited, so we don't
|
||||
// need to do anything else other than just copying the bits over.
|
||||
let reset_props_bits =
|
||||
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
|
||||
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING;
|
||||
builder.add_flags(cached_style.flags & reset_props_bits);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,23 @@ bitflags! {
|
|||
|
||||
/// Whether this style is the style of the document element.
|
||||
const IS_ROOT_ELEMENT_STYLE = 1 << 11;
|
||||
|
||||
/// Whether this element is inside an `opacity: 0` subtree.
|
||||
const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 12;
|
||||
|
||||
/// Whether there are author-specified rules for border-* properties
|
||||
/// (except border-image-*), background-color, or background-image.
|
||||
///
|
||||
/// TODO(emilio): Maybe do include border-image, see:
|
||||
///
|
||||
/// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845
|
||||
const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 13;
|
||||
|
||||
/// Whether there are author-specified rules for padding-* properties.
|
||||
///
|
||||
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
|
||||
/// https://github.com/w3c/csswg-drafts/issues/4777
|
||||
const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 14;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,10 +91,11 @@ impl ComputedValueFlags {
|
|||
/// Flags that are unconditionally propagated to descendants.
|
||||
#[inline]
|
||||
fn inherited_flags() -> Self {
|
||||
ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
|
||||
ComputedValueFlags::CAN_BE_FRAGMENTED |
|
||||
ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
||||
ComputedValueFlags::HAS_TEXT_DECORATION_LINES
|
||||
Self::IS_RELEVANT_LINK_VISITED |
|
||||
Self::CAN_BE_FRAGMENTED |
|
||||
Self::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
||||
Self::HAS_TEXT_DECORATION_LINES |
|
||||
Self::IS_IN_OPACITY_ZERO_SUBTREE
|
||||
}
|
||||
|
||||
/// Flags that may be propagated to descendants.
|
||||
|
|
|
@ -382,6 +382,7 @@ class Longhand(object):
|
|||
"ScrollSnapStrictness",
|
||||
"ScrollSnapType",
|
||||
"TextAlign",
|
||||
"TextAlignLast",
|
||||
"TextDecorationLine",
|
||||
"TextEmphasisPosition",
|
||||
"TextTransform",
|
||||
|
@ -602,7 +603,7 @@ class PropertiesData(object):
|
|||
|
||||
longhand = Longhand(self.current_style_struct, name, **kwargs)
|
||||
self.add_prefixed_aliases(longhand)
|
||||
longhand.alias = list(map(lambda xp: Alias(xp[0], longhand, xp[1]), longhand.alias))
|
||||
longhand.alias = [Alias(xp[0], longhand, xp[1]) for xp in longhand.alias]
|
||||
self.longhand_aliases += longhand.alias
|
||||
self.current_style_struct.longhands.append(longhand)
|
||||
self.longhands.append(longhand)
|
||||
|
@ -620,7 +621,7 @@ class PropertiesData(object):
|
|||
sub_properties = [self.longhands_by_name[s] for s in sub_properties]
|
||||
shorthand = Shorthand(name, sub_properties, *args, **kwargs)
|
||||
self.add_prefixed_aliases(shorthand)
|
||||
shorthand.alias = list(map(lambda xp: Alias(xp[0], shorthand, xp[1]), shorthand.alias))
|
||||
shorthand.alias = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.alias]
|
||||
self.shorthand_aliases += shorthand.alias
|
||||
self.shorthands.append(shorthand)
|
||||
self.shorthands_by_name[name] = shorthand
|
||||
|
@ -669,17 +670,17 @@ def _remove_common_first_line_and_first_letter_properties(props, engine):
|
|||
class PropertyRestrictions:
|
||||
@staticmethod
|
||||
def logical_group(data, group):
|
||||
return map(lambda p: p.name, data.longhands_by_logical_group[group])
|
||||
return [p.name for p in data.longhands_by_logical_group[group]]
|
||||
|
||||
@staticmethod
|
||||
def shorthand(data, shorthand):
|
||||
if shorthand not in data.shorthands_by_name:
|
||||
return []
|
||||
return map(lambda p: p.name, data.shorthands_by_name[shorthand].sub_properties)
|
||||
return [p.name for p in data.shorthands_by_name[shorthand].sub_properties]
|
||||
|
||||
@staticmethod
|
||||
def spec(data, spec_path):
|
||||
return map(lambda p: p.name, filter(lambda p: spec_path in p.spec, data.longhands))
|
||||
return [p.name for p in data.longhands if spec_path in p.spec]
|
||||
|
||||
# https://drafts.csswg.org/css-pseudo/#first-letter-styling
|
||||
@staticmethod
|
||||
|
|
|
@ -22,16 +22,14 @@ use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ff
|
|||
use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
|
||||
% endfor
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
|
||||
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
||||
use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
|
||||
use crate::gecko_bindings::structs;
|
||||
use crate::gecko_bindings::structs::nsCSSPropertyID;
|
||||
use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
|
||||
use crate::gecko::data::PerDocumentStyleData;
|
||||
use crate::gecko::values::round_border_to_device_pixels;
|
||||
use crate::logical_geometry::WritingMode;
|
||||
use crate::media_queries::Device;
|
||||
|
@ -43,11 +41,9 @@ use std::mem::{forget, MaybeUninit};
|
|||
use std::{cmp, ops, ptr};
|
||||
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
|
||||
use crate::values::computed::{Percentage, TransitionProperty};
|
||||
use crate::values::computed::url::ComputedImageUrl;
|
||||
use crate::values::computed::BorderStyle;
|
||||
use crate::values::computed::font::FontSize;
|
||||
use crate::values::generics::column::ColumnCount;
|
||||
use crate::values::generics::image::ImageLayer;
|
||||
|
||||
|
||||
pub mod style_structs {
|
||||
|
@ -392,112 +388,6 @@ def set_gecko_property(ffi_name, expr):
|
|||
}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_svg_length(ident, gecko_ffi_name)">
|
||||
// When context-value is used on an SVG length, the corresponding flag is
|
||||
// set on mContextFlags, and the length field is set to the initial value.
|
||||
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
use crate::values::generics::svg::SVGLength;
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
|
||||
let length = match v {
|
||||
SVGLength::LengthPercentage(length) => {
|
||||
self.gecko.mContextFlags &= !CONTEXT_VALUE;
|
||||
length
|
||||
}
|
||||
SVGLength::ContextValue => {
|
||||
self.gecko.mContextFlags |= CONTEXT_VALUE;
|
||||
match longhands::${ident}::get_initial_value() {
|
||||
SVGLength::LengthPercentage(length) => length,
|
||||
_ => unreachable!("Initial value should not be context-value"),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.gecko.${gecko_ffi_name} = length;
|
||||
}
|
||||
|
||||
pub fn copy_${ident}_from(&mut self, other: &Self) {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
|
||||
self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone();
|
||||
self.gecko.mContextFlags =
|
||||
(self.gecko.mContextFlags & !CONTEXT_VALUE) |
|
||||
(other.gecko.mContextFlags & CONTEXT_VALUE);
|
||||
}
|
||||
|
||||
pub fn reset_${ident}(&mut self, other: &Self) {
|
||||
self.copy_${ident}_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
||||
use crate::values::generics::svg::SVGLength;
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
|
||||
if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 {
|
||||
return SVGLength::ContextValue;
|
||||
}
|
||||
SVGLength::LengthPercentage(self.gecko.${gecko_ffi_name}.clone())
|
||||
}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_svg_opacity(ident, gecko_ffi_name)">
|
||||
<% source_prefix = ident.split("_")[0].upper() + "_OPACITY_SOURCE" %>
|
||||
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK;
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT;
|
||||
use crate::gecko_bindings::structs::nsStyleSVGOpacitySource::*;
|
||||
use crate::values::generics::svg::SVGOpacity;
|
||||
self.gecko.mContextFlags &= !MASK;
|
||||
match v {
|
||||
SVGOpacity::Opacity(opacity) => {
|
||||
self.gecko.mContextFlags |=
|
||||
(eStyleSVGOpacitySource_Normal as u8) << SHIFT;
|
||||
self.gecko.${gecko_ffi_name} = opacity;
|
||||
}
|
||||
SVGOpacity::ContextFillOpacity => {
|
||||
self.gecko.mContextFlags |=
|
||||
(eStyleSVGOpacitySource_ContextFillOpacity as u8) << SHIFT;
|
||||
self.gecko.${gecko_ffi_name} = 1.;
|
||||
}
|
||||
SVGOpacity::ContextStrokeOpacity => {
|
||||
self.gecko.mContextFlags |=
|
||||
(eStyleSVGOpacitySource_ContextStrokeOpacity as u8) << SHIFT;
|
||||
self.gecko.${gecko_ffi_name} = 1.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_${ident}_from(&mut self, other: &Self) {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK;
|
||||
self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
|
||||
self.gecko.mContextFlags =
|
||||
(self.gecko.mContextFlags & !MASK) |
|
||||
(other.gecko.mContextFlags & MASK);
|
||||
}
|
||||
|
||||
pub fn reset_${ident}(&mut self, other: &Self) {
|
||||
self.copy_${ident}_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK;
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT;
|
||||
use crate::gecko_bindings::structs::nsStyleSVGOpacitySource::*;
|
||||
use crate::values::generics::svg::SVGOpacity;
|
||||
|
||||
let source = (self.gecko.mContextFlags & MASK) >> SHIFT;
|
||||
if source == eStyleSVGOpacitySource_Normal as u8 {
|
||||
return SVGOpacity::Opacity(self.gecko.${gecko_ffi_name});
|
||||
} else {
|
||||
debug_assert_eq!(self.gecko.${gecko_ffi_name}, 1.0);
|
||||
if source == eStyleSVGOpacitySource_ContextFillOpacity as u8 {
|
||||
SVGOpacity::ContextFillOpacity
|
||||
} else {
|
||||
debug_assert_eq!(source, eStyleSVGOpacitySource_ContextStrokeOpacity as u8);
|
||||
SVGOpacity::ContextStrokeOpacity
|
||||
}
|
||||
}
|
||||
}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None,
|
||||
round_to_pixels=False)">
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -647,12 +537,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name=None)">
|
||||
<%
|
||||
if gecko_ffi_name is None:
|
||||
gecko_ffi_name = "m" + to_camel_case(ident)
|
||||
%>
|
||||
|
||||
<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)">
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
self.gecko.${gecko_ffi_name} = From::from(v)
|
||||
|
@ -711,9 +596,6 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
|||
# Types used with predefined_type()-defined properties that we can auto-generate.
|
||||
predefined_types = {
|
||||
"MozScriptMinSize": impl_absolute_length,
|
||||
"SVGLength": impl_svg_length,
|
||||
"SVGOpacity": impl_svg_opacity,
|
||||
"SVGWidth": impl_svg_length,
|
||||
}
|
||||
|
||||
def longhand_method(longhand):
|
||||
|
@ -778,8 +660,7 @@ fn static_assert() {
|
|||
for x in CORNERS]) %>
|
||||
|
||||
<%self:impl_trait style_struct_name="Border"
|
||||
skip_longhands="${skip_border_longhands} border-image-source
|
||||
border-image-repeat">
|
||||
skip_longhands="${skip_border_longhands} border-image-repeat">
|
||||
% for side in SIDES:
|
||||
pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
|
||||
self.gecko.mBorderStyle[${side.index}] = v;
|
||||
|
@ -848,35 +729,6 @@ fn static_assert() {
|
|||
corner) %>
|
||||
% endfor
|
||||
|
||||
pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) {
|
||||
unsafe {
|
||||
// Prevent leaking of the last elements we did set
|
||||
Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource);
|
||||
}
|
||||
|
||||
if let ImageLayer::Image(image) = image {
|
||||
self.gecko.mBorderImageSource.set(image);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_border_image_source_from(&mut self, other: &Self) {
|
||||
unsafe {
|
||||
Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource,
|
||||
&other.gecko.mBorderImageSource);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_border_image_source(&mut self, other: &Self) {
|
||||
self.copy_border_image_source_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_border_image_source(&self) -> longhands::border_image_source::computed_value::T {
|
||||
match unsafe { self.gecko.mBorderImageSource.into_image() } {
|
||||
Some(image) => ImageLayer::Image(image),
|
||||
None => ImageLayer::None,
|
||||
}
|
||||
}
|
||||
|
||||
<%
|
||||
border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"]
|
||||
%>
|
||||
|
@ -950,11 +802,10 @@ fn static_assert() {
|
|||
|
||||
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
|
||||
<%self:impl_trait style_struct_name="Position"
|
||||
skip_longhands="${skip_position_longhands} grid-auto-flow">
|
||||
skip_longhands="${skip_position_longhands}">
|
||||
% for side in SIDES:
|
||||
<% impl_split_style_coord(side.ident, "mOffset", side.index) %>
|
||||
% endfor
|
||||
${impl_simple_type_with_conversion("grid_auto_flow")}
|
||||
pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
|
||||
debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
|
||||
self.gecko.mJustifyItems.computed = v;
|
||||
|
@ -1005,15 +856,13 @@ fn static_assert() {
|
|||
}
|
||||
</%self:impl_trait>
|
||||
|
||||
<%
|
||||
skip_font_longhands = """font-family font-size font-size-adjust font-weight
|
||||
font-style font-stretch -moz-script-level
|
||||
font-synthesis -x-lang font-variant-alternates
|
||||
font-variant-east-asian font-variant-ligatures
|
||||
font-variant-numeric font-language-override
|
||||
font-feature-settings font-variation-settings
|
||||
-moz-min-font-size-ratio -x-text-zoom"""
|
||||
%>
|
||||
<% skip_font_longhands = """font-family font-size font-size-adjust font-weight
|
||||
font-style font-stretch font-synthesis -x-lang
|
||||
font-variant-alternates font-variant-east-asian
|
||||
font-variant-ligatures font-variant-numeric
|
||||
font-language-override font-feature-settings
|
||||
font-variation-settings -moz-min-font-size-ratio
|
||||
-x-text-zoom""" %>
|
||||
<%self:impl_trait style_struct_name="Font"
|
||||
skip_longhands="${skip_font_longhands}">
|
||||
|
||||
|
@ -1275,9 +1124,7 @@ fn static_assert() {
|
|||
longhands::_x_text_zoom::computed_value::T(self.gecko.mAllowZoom)
|
||||
}
|
||||
|
||||
${impl_simple("_moz_script_level", "mScriptLevel")}
|
||||
<% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %>
|
||||
|
||||
${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
|
||||
${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")}
|
||||
${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")}
|
||||
|
@ -1401,9 +1248,6 @@ fn static_assert() {
|
|||
${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_transition_timing_function()">
|
||||
${impl_animation_or_transition_timing_function('transition')}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_animation_count(ident, gecko_ffi_name)">
|
||||
${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)}
|
||||
|
@ -1413,10 +1257,6 @@ fn static_assert() {
|
|||
${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_animation_timing_function()">
|
||||
${impl_animation_or_transition_timing_function('animation')}
|
||||
</%def>
|
||||
|
||||
<%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set_animation_${ident}<I>(&mut self, v: I)
|
||||
|
@ -1466,7 +1306,7 @@ fn static_assert() {
|
|||
animation-iteration-count animation-timing-function
|
||||
clear transition-duration transition-delay
|
||||
transition-timing-function transition-property
|
||||
shape-outside -webkit-line-clamp""" %>
|
||||
-webkit-line-clamp""" %>
|
||||
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
||||
#[inline]
|
||||
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
||||
|
@ -1509,7 +1349,7 @@ fn static_assert() {
|
|||
|
||||
${impl_transition_time_value('delay', 'Delay')}
|
||||
${impl_transition_time_value('duration', 'Duration')}
|
||||
${impl_transition_timing_function()}
|
||||
${impl_animation_or_transition_timing_function('transition')}
|
||||
|
||||
pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
|
||||
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
|
||||
|
@ -1723,10 +1563,7 @@ fn static_assert() {
|
|||
|
||||
${impl_animation_count('iteration_count', 'IterationCount')}
|
||||
${impl_copy_animation_value('iteration_count', 'IterationCount')}
|
||||
|
||||
${impl_animation_timing_function()}
|
||||
|
||||
<% impl_shape_source("shape_outside", "mShapeOutside") %>
|
||||
${impl_animation_or_transition_timing_function('animation')}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) {
|
||||
|
@ -2009,7 +1846,7 @@ fn static_assert() {
|
|||
for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
|
||||
.zip(other.gecko.${image_layers_field}.mLayers.iter())
|
||||
.take(count as usize) {
|
||||
Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage);
|
||||
layer.mImage = other.mImage.clone();
|
||||
}
|
||||
self.gecko.${image_layers_field}.mImageCount = count;
|
||||
}
|
||||
|
@ -2029,22 +1866,17 @@ fn static_assert() {
|
|||
let images = images.into_iter();
|
||||
|
||||
unsafe {
|
||||
// Prevent leaking of the last elements we did set
|
||||
for image in &mut self.gecko.${image_layers_field}.mLayers {
|
||||
Gecko_SetNullImageValue(&mut image.mImage)
|
||||
}
|
||||
// XXXManishearth clear mSourceURI for masks
|
||||
Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, images.len(),
|
||||
LayerType::${shorthand.title()});
|
||||
Gecko_EnsureImageLayersLength(
|
||||
&mut self.gecko.${image_layers_field},
|
||||
images.len(),
|
||||
LayerType::${shorthand.title()},
|
||||
);
|
||||
}
|
||||
|
||||
self.gecko.${image_layers_field}.mImageCount = images.len() as u32;
|
||||
|
||||
for (image, geckoimage) in images.zip(self.gecko.${image_layers_field}
|
||||
.mLayers.iter_mut()) {
|
||||
if let ImageLayer::Image(image) = image {
|
||||
geckoimage.mImage.set(image)
|
||||
}
|
||||
geckoimage.mImage = image;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2052,12 +1884,8 @@ fn static_assert() {
|
|||
longhands::${shorthand}_image::computed_value::List(
|
||||
self.gecko.${image_layers_field}.mLayers.iter()
|
||||
.take(self.gecko.${image_layers_field}.mImageCount as usize)
|
||||
.map(|ref layer| {
|
||||
match unsafe { layer.mImage.into_image() } {
|
||||
Some(image) => ImageLayer::Image(image),
|
||||
None => ImageLayer::None,
|
||||
}
|
||||
}).collect()
|
||||
.map(|layer| layer.mImage.clone())
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2189,18 +2017,9 @@ fn static_assert() {
|
|||
|
||||
|
||||
<%self:impl_trait style_struct_name="InheritedText"
|
||||
skip_longhands="text-align -webkit-text-stroke-width text-emphasis-position">
|
||||
|
||||
<% text_align_keyword = Keyword("text-align",
|
||||
"start end left right center justify -moz-center -moz-left -moz-right char",
|
||||
gecko_strip_moz_prefix=False) %>
|
||||
${impl_keyword('text_align', 'mTextAlign', text_align_keyword)}
|
||||
|
||||
${impl_simple_type_with_conversion("text_emphasis_position")}
|
||||
|
||||
skip_longhands="-webkit-text-stroke-width">
|
||||
${impl_non_negative_length('_webkit_text_stroke_width',
|
||||
'mWebkitTextStrokeWidth')}
|
||||
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter">
|
||||
|
@ -2244,204 +2063,21 @@ fn static_assert() {
|
|||
}
|
||||
</%self:impl_trait>
|
||||
|
||||
// Set SVGPathData to StyleShapeSource.
|
||||
fn set_style_svg_path(
|
||||
shape_source: &mut structs::mozilla::StyleShapeSource,
|
||||
servo_path: values::specified::svg_path::SVGPathData,
|
||||
fill: values::generics::basic_shape::FillRule,
|
||||
) {
|
||||
// Setup path.
|
||||
unsafe {
|
||||
bindings::Gecko_SetToSVGPath(
|
||||
shape_source,
|
||||
servo_path.0.forget(),
|
||||
fill,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<%def name="impl_shape_source(ident, gecko_ffi_name)">
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
use crate::values::generics::basic_shape::ShapeSource;
|
||||
use crate::gecko_bindings::structs::StyleShapeSourceType;
|
||||
use crate::gecko_bindings::structs::StyleGeometryBox;
|
||||
|
||||
let ref mut ${ident} = self.gecko.${gecko_ffi_name};
|
||||
|
||||
// clean up existing struct.
|
||||
unsafe { bindings::Gecko_DestroyShapeSource(${ident}) };
|
||||
|
||||
${ident}.mType = StyleShapeSourceType::None;
|
||||
|
||||
match v {
|
||||
ShapeSource::None => {} // don't change the type
|
||||
ShapeSource::ImageOrUrl(image) => {
|
||||
% if ident == "clip_path":
|
||||
use crate::values::generics::image::Image;
|
||||
|
||||
let image = Image::Url(ComputedImageUrl(image));
|
||||
% endif
|
||||
unsafe {
|
||||
bindings::Gecko_NewShapeImage(${ident});
|
||||
let style_image = &mut *${ident}.__bindgen_anon_1.mShapeImage.as_mut().mPtr;
|
||||
style_image.set(image);
|
||||
}
|
||||
}
|
||||
ShapeSource::Box(reference) => {
|
||||
${ident}.mReferenceBox = reference.into();
|
||||
${ident}.mType = StyleShapeSourceType::Box;
|
||||
}
|
||||
ShapeSource::Path(p) => set_style_svg_path(${ident}, p.path, p.fill),
|
||||
ShapeSource::Shape(servo_shape, maybe_box) => {
|
||||
unsafe {
|
||||
${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr =
|
||||
Box::into_raw(servo_shape);
|
||||
}
|
||||
${ident}.mReferenceBox =
|
||||
maybe_box.map(Into::into).unwrap_or(StyleGeometryBox::NoBox);
|
||||
${ident}.mType = StyleShapeSourceType::Shape;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
||||
(&self.gecko.${gecko_ffi_name}).into()
|
||||
}
|
||||
|
||||
pub fn copy_${ident}_from(&mut self, other: &Self) {
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyShapeSourceFrom;
|
||||
unsafe {
|
||||
Gecko_CopyShapeSourceFrom(&mut self.gecko.${gecko_ffi_name}, &other.gecko.${gecko_ffi_name});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_${ident}(&mut self, other: &Self) {
|
||||
self.copy_${ident}_from(other)
|
||||
}
|
||||
</%def>
|
||||
|
||||
<% skip_svg_longhands = """
|
||||
mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
|
||||
clip-path
|
||||
"""
|
||||
%>
|
||||
<%self:impl_trait style_struct_name="SVG"
|
||||
skip_longhands="${skip_svg_longhands}">
|
||||
|
||||
<% impl_common_image_layer_properties("mask") %>
|
||||
<% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
|
||||
<% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
|
||||
<% impl_shape_source("clip_path", "mClipPath") %>
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="InheritedSVG"
|
||||
skip_longhands="stroke-dasharray">
|
||||
pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
|
||||
use crate::values::generics::svg::SVGStrokeDashArray;
|
||||
|
||||
match v {
|
||||
SVGStrokeDashArray::Values(v) => {
|
||||
let v = v.into_iter();
|
||||
self.gecko.mContextFlags &= !CONTEXT_VALUE;
|
||||
unsafe {
|
||||
bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut *self.gecko, v.len() as u32);
|
||||
}
|
||||
for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) {
|
||||
*gecko = servo;
|
||||
}
|
||||
}
|
||||
SVGStrokeDashArray::ContextValue => {
|
||||
self.gecko.mContextFlags |= CONTEXT_VALUE;
|
||||
unsafe {
|
||||
bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut *self.gecko, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_stroke_dasharray_from(&mut self, other: &Self) {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
|
||||
unsafe {
|
||||
bindings::Gecko_nsStyleSVG_CopyDashArray(&mut *self.gecko, &*other.gecko);
|
||||
}
|
||||
self.gecko.mContextFlags =
|
||||
(self.gecko.mContextFlags & !CONTEXT_VALUE) |
|
||||
(other.gecko.mContextFlags & CONTEXT_VALUE);
|
||||
}
|
||||
|
||||
pub fn reset_stroke_dasharray(&mut self, other: &Self) {
|
||||
self.copy_stroke_dasharray_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T {
|
||||
use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
|
||||
use crate::values::generics::svg::SVGStrokeDashArray;
|
||||
|
||||
if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
|
||||
debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
|
||||
return SVGStrokeDashArray::ContextValue;
|
||||
}
|
||||
SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect())
|
||||
}
|
||||
<%self:impl_trait style_struct_name="InheritedSVG">
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor">
|
||||
pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
|
||||
self.gecko.mCursor = v.keyword;
|
||||
unsafe {
|
||||
bindings::Gecko_SetCursorArrayCapacity(&mut *self.gecko, v.images.len());
|
||||
}
|
||||
for i in 0..v.images.len() {
|
||||
unsafe {
|
||||
bindings::Gecko_AppendCursorImage(&mut *self.gecko, &v.images[i].url);
|
||||
}
|
||||
|
||||
match v.images[i].hotspot {
|
||||
Some((x, y)) => {
|
||||
self.gecko.mCursorImages[i].mHaveHotspot = true;
|
||||
self.gecko.mCursorImages[i].mHotspotX = x;
|
||||
self.gecko.mCursorImages[i].mHotspotY = y;
|
||||
},
|
||||
_ => {
|
||||
self.gecko.mCursorImages[i].mHaveHotspot = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_cursor_from(&mut self, other: &Self) {
|
||||
self.gecko.mCursor = other.gecko.mCursor;
|
||||
unsafe {
|
||||
Gecko_CopyCursorArrayFrom(&mut *self.gecko, &*other.gecko);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_cursor(&mut self, other: &Self) {
|
||||
self.copy_cursor_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_cursor(&self) -> longhands::cursor::computed_value::T {
|
||||
use crate::values::computed::ui::CursorImage;
|
||||
|
||||
let keyword = self.gecko.mCursor;
|
||||
|
||||
let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| {
|
||||
let url = gecko_cursor_image.mImage.clone();
|
||||
|
||||
let hotspot =
|
||||
if gecko_cursor_image.mHaveHotspot {
|
||||
Some((gecko_cursor_image.mHotspotX, gecko_cursor_image.mHotspotY))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
CursorImage { url, hotspot }
|
||||
}).collect::<Vec<_>>().into_boxed_slice();
|
||||
|
||||
longhands::cursor::computed_value::T { images, keyword }
|
||||
}
|
||||
<%self:impl_trait style_struct_name="InheritedUI">
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="Column"
|
||||
|
@ -2483,8 +2119,7 @@ clip-path
|
|||
}
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="UI" skip_longhands="-moz-force-broken-image-icon">
|
||||
${impl_simple_type_with_conversion("_moz_force_broken_image_icon", "mForceBrokenImageIcon")}
|
||||
<%self:impl_trait style_struct_name="UI">
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="XUL">
|
||||
|
@ -2494,3 +2129,40 @@ clip-path
|
|||
${declare_style_struct(style_struct)}
|
||||
${impl_style_struct(style_struct)}
|
||||
% endfor
|
||||
|
||||
/// Assert that the initial values set in Gecko style struct constructors
|
||||
/// match the values returned by `get_initial_value()` for each longhand.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
|
||||
if cfg!(debug_assertions) {
|
||||
let data = data.borrow();
|
||||
let cv = data.stylist.device().default_computed_values();
|
||||
<%
|
||||
# Skip properties with initial values that change at computed value time.
|
||||
SKIPPED = [
|
||||
"border-top-width",
|
||||
"border-bottom-width",
|
||||
"border-left-width",
|
||||
"border-right-width",
|
||||
"font-family",
|
||||
"font-size",
|
||||
"outline-width",
|
||||
]
|
||||
TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
|
||||
%>
|
||||
% for property in TO_TEST:
|
||||
assert_eq!(
|
||||
cv.clone_${property.ident}(),
|
||||
longhands::${property.ident}::get_initial_value(),
|
||||
concat!(
|
||||
"initial value in Gecko style struct for ",
|
||||
stringify!(${property.ident}),
|
||||
" must match longhands::",
|
||||
stringify!(${property.ident}),
|
||||
"::get_initial_value()"
|
||||
)
|
||||
);
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
|
|
|
@ -680,7 +680,7 @@
|
|||
% endfor
|
||||
|
||||
let mut bits = ${type}::empty();
|
||||
% for servo_bit, gecko_bit in bit_map.iteritems():
|
||||
% for servo_bit, gecko_bit in bit_map.items():
|
||||
if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
|
||||
bits |= ${servo_bit};
|
||||
}
|
||||
|
@ -696,7 +696,7 @@
|
|||
let mut bits: ${kw_type} = 0;
|
||||
// FIXME: if we ensure that the Servo bitflags storage is the same
|
||||
// as Gecko's one, we can just copy it.
|
||||
% for servo_bit, gecko_bit in bit_map.iteritems():
|
||||
% for servo_bit, gecko_bit in bit_map.items():
|
||||
if self.contains(${servo_bit}) {
|
||||
bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
|
||||
}
|
||||
|
@ -707,7 +707,8 @@
|
|||
</%def>
|
||||
|
||||
<%def name="single_keyword(name, values, vector=False,
|
||||
extra_specified=None, needs_conversion=False, **kwargs)">
|
||||
extra_specified=None, needs_conversion=False,
|
||||
gecko_pref_controlled_initial_value=None, **kwargs)">
|
||||
<%
|
||||
keyword_kwargs = {a: kwargs.pop(a, None) for a in [
|
||||
'gecko_constant_prefix',
|
||||
|
@ -724,18 +725,19 @@
|
|||
]}
|
||||
%>
|
||||
|
||||
<%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
|
||||
<%def name="inner_body(keyword, extra_specified=None, needs_conversion=False,
|
||||
gecko_pref_controlled_initial_value=None)">
|
||||
<%def name="variants(variants, include_aliases)">
|
||||
% for variant in variants:
|
||||
% if include_aliases:
|
||||
<%
|
||||
aliases = []
|
||||
for alias, v in keyword.aliases_for(engine).iteritems():
|
||||
for alias, v in keyword.aliases_for(engine).items():
|
||||
if variant == v:
|
||||
aliases.append(alias)
|
||||
%>
|
||||
% if aliases:
|
||||
#[parse(aliases = "${','.join(aliases)}")]
|
||||
#[parse(aliases = "${','.join(sorted(aliases))}")]
|
||||
% endif
|
||||
% endif
|
||||
${to_camel_case(variant)},
|
||||
|
@ -773,10 +775,20 @@
|
|||
}
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
% if engine == "gecko" and gecko_pref_controlled_initial_value:
|
||||
if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") {
|
||||
return computed_value::T::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])};
|
||||
}
|
||||
% endif
|
||||
computed_value::T::${to_camel_case(values.split()[0])}
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_initial_specified_value() -> SpecifiedValue {
|
||||
% if engine == "gecko" and gecko_pref_controlled_initial_value:
|
||||
if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") {
|
||||
return SpecifiedValue::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])};
|
||||
}
|
||||
% endif
|
||||
SpecifiedValue::${to_camel_case(values.split()[0])}
|
||||
}
|
||||
#[inline]
|
||||
|
@ -805,7 +817,8 @@
|
|||
% else:
|
||||
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
|
||||
${inner_body(Keyword(name, values, **keyword_kwargs),
|
||||
extra_specified=extra_specified, needs_conversion=needs_conversion)}
|
||||
extra_specified=extra_specified, needs_conversion=needs_conversion,
|
||||
gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)}
|
||||
% if caller:
|
||||
${caller.body()}
|
||||
% endif
|
||||
|
|
|
@ -21,10 +21,10 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"background-image",
|
||||
"ImageLayer",
|
||||
"Image",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
initial_value="computed::ImageLayer::none()",
|
||||
initial_specified_value="specified::ImageLayer::none()",
|
||||
initial_value="computed::Image::None",
|
||||
initial_specified_value="specified::Image::None",
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
|
||||
vector="True",
|
||||
animation_value_type="discrete",
|
||||
|
@ -35,8 +35,8 @@ ${helpers.predefined_type(
|
|||
${helpers.predefined_type(
|
||||
"background-position-" + axis,
|
||||
"position::" + direction + "Position",
|
||||
"computed::LengthPercentage::zero_percent()",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
initial_value="computed::LengthPercentage::zero()",
|
||||
initial_specified_value="SpecifiedValue::initial_specified_value()",
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
|
||||
animation_value_type="ComputedValue",
|
||||
|
@ -107,7 +107,7 @@ ${helpers.single_keyword(
|
|||
"""normal multiply screen overlay darken lighten color-dodge
|
||||
color-burn hard-light soft-light difference exclusion hue
|
||||
saturation color luminosity""",
|
||||
gecko_constant_prefix="NS_STYLE_BLEND",
|
||||
gecko_enum_prefix="StyleBlend",
|
||||
vector=True,
|
||||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
|
|
|
@ -106,10 +106,10 @@ ${helpers.single_keyword(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"border-image-source",
|
||||
"ImageLayer",
|
||||
"Image",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
initial_value="computed::ImageLayer::none()",
|
||||
initial_specified_value="specified::ImageLayer::none()",
|
||||
initial_value="computed::Image::None",
|
||||
initial_specified_value="specified::Image::None",
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
|
||||
vector=False,
|
||||
animation_value_type="discrete",
|
||||
|
|
|
@ -573,6 +573,7 @@ ${helpers.single_keyword(
|
|||
"backface-visibility",
|
||||
"visible hidden",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
gecko_enum_prefix="StyleBackfaceVisibility",
|
||||
spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
animation_value_type="discrete",
|
||||
|
@ -633,7 +634,7 @@ ${helpers.predefined_type(
|
|||
"Appearance",
|
||||
"computed::Appearance::None",
|
||||
engines="gecko",
|
||||
alias="-webkit-appearance:layout.css.webkit-appearance.enabled",
|
||||
alias="-webkit-appearance",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
|
||||
animation_value_type="discrete",
|
||||
gecko_ffi_name="mAppearance",
|
||||
|
@ -679,10 +680,10 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"shape-outside",
|
||||
"basic_shape::FloatAreaShape",
|
||||
"generics::basic_shape::ShapeSource::None",
|
||||
"basic_shape::ShapeOutside",
|
||||
"generics::basic_shape::ShapeOutside::None",
|
||||
engines="gecko",
|
||||
animation_value_type="basic_shape::FloatAreaShape",
|
||||
animation_value_type="basic_shape::ShapeOutside",
|
||||
spec="https://drafts.csswg.org/css-shapes/#shape-outside-property",
|
||||
)}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ ${helpers.single_keyword(
|
|||
color-burn hard-light soft-light difference exclusion hue
|
||||
saturation color luminosity""",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
gecko_constant_prefix="NS_STYLE_BLEND",
|
||||
gecko_enum_prefix="StyleBlend",
|
||||
animation_value_type="discrete",
|
||||
flags="CREATES_STACKING_CONTEXT",
|
||||
spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",
|
||||
|
|
|
@ -230,7 +230,7 @@ ${helpers.predefined_type(
|
|||
${helpers.predefined_type(
|
||||
"-moz-script-level",
|
||||
"MozScriptLevel",
|
||||
0,
|
||||
"0",
|
||||
engines="gecko",
|
||||
animation_value_type="none",
|
||||
enabled_in="ua",
|
||||
|
|
|
@ -86,6 +86,7 @@ ${helpers.single_keyword(
|
|||
${helpers.single_keyword(
|
||||
"image-orientation",
|
||||
"none from-image",
|
||||
gecko_pref_controlled_initial_value="layout.css.image-orientation.initial-from-image=from-image",
|
||||
engines="gecko",
|
||||
gecko_enum_prefix="StyleImageOrientation",
|
||||
animation_value_type="discrete",
|
||||
|
|
|
@ -36,15 +36,16 @@ ${helpers.single_keyword(
|
|||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty",
|
||||
gecko_enum_prefix="StyleColorInterpolation",
|
||||
)}
|
||||
|
||||
${helpers.single_keyword(
|
||||
"color-interpolation-filters",
|
||||
"linearrgb auto srgb",
|
||||
engines="gecko",
|
||||
gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty",
|
||||
gecko_enum_prefix="StyleColorInterpolation",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
|
|
@ -141,11 +141,12 @@ ${helpers.predefined_type(
|
|||
% endif
|
||||
</%helpers:single_keyword>
|
||||
|
||||
${helpers.single_keyword(
|
||||
${helpers.predefined_type(
|
||||
"text-align-last",
|
||||
"auto start end left right center justify",
|
||||
"TextAlignLast",
|
||||
"computed::text::TextAlignLast::Auto",
|
||||
needs_context=False,
|
||||
engines="gecko",
|
||||
gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
|
||||
)}
|
||||
|
@ -244,7 +245,7 @@ ${helpers.predefined_type(
|
|||
${helpers.predefined_type(
|
||||
"text-emphasis-style",
|
||||
"TextEmphasisStyle",
|
||||
None,
|
||||
"computed::TextEmphasisStyle::None",
|
||||
engines="gecko",
|
||||
initial_specified_value="SpecifiedValue::None",
|
||||
animation_value_type="discrete",
|
||||
|
@ -367,13 +368,12 @@ ${helpers.single_keyword(
|
|||
servo_restyle_damage="rebuild_and_reflow",
|
||||
)}
|
||||
|
||||
// FIXME Firefox expects the initial value of this property to change depending
|
||||
// on the value of the layout.css.control-characters.visible pref.
|
||||
${helpers.single_keyword(
|
||||
"-moz-control-character-visibility",
|
||||
"hidden visible",
|
||||
engines="gecko",
|
||||
gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY",
|
||||
gecko_enum_prefix="StyleControlCharacterVisibility",
|
||||
gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible",
|
||||
animation_value_type="none",
|
||||
gecko_ffi_name="mControlCharacterVisibility",
|
||||
spec="Nonstandard",
|
||||
|
|
|
@ -333,7 +333,7 @@ ${helpers.single_keyword(
|
|||
${helpers.predefined_type(
|
||||
"object-position",
|
||||
"Position",
|
||||
"computed::Position::zero()",
|
||||
"computed::Position::center()",
|
||||
engines="gecko",
|
||||
boxed=True,
|
||||
spec="https://drafts.csswg.org/css-images-3/#the-object-position",
|
||||
|
@ -375,7 +375,7 @@ ${helpers.predefined_type(
|
|||
${helpers.predefined_type(
|
||||
"grid-auto-flow",
|
||||
"GridAutoFlow",
|
||||
"computed::GridAutoFlow::row()",
|
||||
"computed::GridAutoFlow::ROW",
|
||||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",
|
||||
|
|
|
@ -10,6 +10,7 @@ ${helpers.single_keyword(
|
|||
"vector-effect",
|
||||
"none non-scaling-stroke",
|
||||
engines="gecko",
|
||||
gecko_enum_prefix="StyleVectorEffect",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
|
||||
)}
|
||||
|
@ -76,10 +77,10 @@ ${helpers.single_keyword(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"clip-path",
|
||||
"basic_shape::ClippingShape",
|
||||
"generics::basic_shape::ShapeSource::None",
|
||||
"basic_shape::ClipPath",
|
||||
"generics::basic_shape::ClipPath::None",
|
||||
engines="gecko",
|
||||
animation_value_type="basic_shape::ClippingShape",
|
||||
animation_value_type="basic_shape::ClipPath",
|
||||
flags="CREATES_STACKING_CONTEXT",
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
|
||||
)}
|
||||
|
@ -110,7 +111,7 @@ ${helpers.predefined_type(
|
|||
${helpers.predefined_type(
|
||||
"mask-position-" + axis,
|
||||
"position::" + direction + "Position",
|
||||
"computed::LengthPercentage::zero()",
|
||||
"computed::LengthPercentage::zero_percent()",
|
||||
engines="gecko",
|
||||
extra_prefixes="webkit",
|
||||
initial_specified_value="specified::PositionComponent::Center",
|
||||
|
@ -164,6 +165,7 @@ ${helpers.single_keyword(
|
|||
"mask-composite",
|
||||
"add subtract intersect exclude",
|
||||
engines="gecko",
|
||||
gecko_enum_prefix="StyleMaskComposite",
|
||||
vector=True,
|
||||
extra_prefixes="webkit",
|
||||
animation_value_type="discrete",
|
||||
|
@ -172,10 +174,10 @@ ${helpers.single_keyword(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"mask-image",
|
||||
"ImageLayer",
|
||||
"Image",
|
||||
engines="gecko",
|
||||
initial_value="computed::ImageLayer::none()",
|
||||
initial_specified_value="specified::ImageLayer::none()",
|
||||
initial_value="computed::Image::None",
|
||||
initial_specified_value="specified::Image::None",
|
||||
parse_method="parse_with_cors_anonymous",
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
|
||||
vector=True,
|
||||
|
|
|
@ -56,7 +56,7 @@ ${helpers.single_keyword(
|
|||
|
||||
${helpers.single_keyword(
|
||||
"-moz-window-shadow",
|
||||
"none default menu tooltip sheet",
|
||||
"default none menu tooltip sheet",
|
||||
engines="gecko",
|
||||
gecko_ffi_name="mWindowShadow",
|
||||
gecko_enum_prefix="StyleWindowShadow",
|
||||
|
|
|
@ -64,10 +64,12 @@ ${helpers.single_keyword(
|
|||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
|
||||
)}
|
||||
|
||||
// NOTE(heycam): Odd that the initial value is 1 yet 0 is a valid value. There
|
||||
// are uses of `-moz-box-ordinal-group: 0` in the tree, too.
|
||||
${helpers.predefined_type(
|
||||
"-moz-box-ordinal-group",
|
||||
"Integer",
|
||||
"0",
|
||||
"1",
|
||||
engines="gecko",
|
||||
parse_method="parse_non_negative",
|
||||
alias="-webkit-box-ordinal-group",
|
||||
|
|
|
@ -312,7 +312,7 @@ impl Clone for PropertyDeclaration {
|
|||
trait AssertCopy { fn assert() {} }
|
||||
trait AssertNotCopy { fn assert() {} }
|
||||
impl<T: Copy> AssertCopy for Helper<T> {}
|
||||
% for ty in set(x["type"] for x in others):
|
||||
% for ty in sorted(set(x["type"] for x in others)):
|
||||
impl AssertNotCopy for Helper<${ty}> {}
|
||||
Helper::<${ty}>::assert();
|
||||
% endfor
|
||||
|
@ -729,10 +729,10 @@ impl NonCustomPropertyIdSet {
|
|||
<%def name="static_non_custom_property_id_set(name, is_member)">
|
||||
static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
|
||||
<%
|
||||
storage = [0] * ((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
|
||||
storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
|
||||
for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
|
||||
if is_member(property):
|
||||
storage[i / 32] |= 1 << (i % 32)
|
||||
storage[int(i / 32)] |= 1 << (i % 32)
|
||||
%>
|
||||
storage: [${", ".join("0x%x" % word for word in storage)}]
|
||||
};
|
||||
|
@ -741,15 +741,61 @@ static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
|
|||
<%def name="static_longhand_id_set(name, is_member)">
|
||||
static ${name}: LonghandIdSet = LonghandIdSet {
|
||||
<%
|
||||
storage = [0] * ((len(data.longhands) - 1 + 32) / 32)
|
||||
storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
|
||||
for i, property in enumerate(data.longhands):
|
||||
if is_member(property):
|
||||
storage[i / 32] |= 1 << (i % 32)
|
||||
storage[int(i / 32)] |= 1 << (i % 32)
|
||||
%>
|
||||
storage: [${", ".join("0x%x" % word for word in storage)}]
|
||||
};
|
||||
</%def>
|
||||
|
||||
<%
|
||||
logical_groups = defaultdict(list)
|
||||
for prop in data.longhands:
|
||||
if prop.logical_group:
|
||||
logical_groups[prop.logical_group].append(prop)
|
||||
|
||||
for group, props in logical_groups.items():
|
||||
logical_count = sum(1 for p in props if p.logical)
|
||||
if logical_count * 2 != len(props):
|
||||
raise RuntimeError("Logical group {} has ".format(group) +
|
||||
"unbalanced logical / physical properties")
|
||||
|
||||
FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
|
||||
FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
|
||||
MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
|
||||
PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
|
||||
CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
|
||||
|
||||
def restriction_flags(property):
|
||||
name = property.name
|
||||
flags = []
|
||||
if name in FIRST_LINE_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_FIRST_LINE")
|
||||
if name in FIRST_LETTER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_FIRST_LETTER")
|
||||
if name in PLACEHOLDER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_PLACEHOLDER")
|
||||
if name in MARKER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_MARKER")
|
||||
if name in CUE_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_CUE")
|
||||
return flags
|
||||
|
||||
%>
|
||||
|
||||
/// A group for properties which may override each other
|
||||
/// via logical resolution.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum LogicalGroup {
|
||||
% for group in sorted(logical_groups.keys()):
|
||||
/// ${group}
|
||||
${to_camel_case(group)},
|
||||
% endfor
|
||||
}
|
||||
|
||||
|
||||
/// A set of longhand properties
|
||||
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
|
||||
pub struct LonghandIdSet {
|
||||
|
@ -837,6 +883,30 @@ impl LonghandIdSet {
|
|||
&HAS_NO_EFFECT_ON_SCROLLBARS
|
||||
}
|
||||
|
||||
/// Returns the set of padding properties for the purpose of disabling
|
||||
/// native appearance.
|
||||
#[inline]
|
||||
pub fn padding_properties() -> &'static Self {
|
||||
<% assert "padding" in logical_groups %>
|
||||
${static_longhand_id_set(
|
||||
"PADDING_PROPERTIES",
|
||||
lambda p: p.logical_group == "padding"
|
||||
)}
|
||||
&PADDING_PROPERTIES
|
||||
}
|
||||
|
||||
/// Returns the set of border properties for the purpose of disabling native
|
||||
/// appearance.
|
||||
#[inline]
|
||||
pub fn border_background_properties() -> &'static Self {
|
||||
${static_longhand_id_set(
|
||||
"BORDER_BACKGROUND_PROPERTIES",
|
||||
lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
|
||||
p.name in ["background-color", "background-image"]
|
||||
)}
|
||||
&BORDER_BACKGROUND_PROPERTIES
|
||||
}
|
||||
|
||||
/// Iterate over the current longhand id set.
|
||||
pub fn iter(&self) -> LonghandIdSetIterator {
|
||||
LonghandIdSetIterator { longhands: self, cur: 0, }
|
||||
|
@ -998,53 +1068,8 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
<%
|
||||
logical_groups = defaultdict(list)
|
||||
for prop in data.longhands:
|
||||
if prop.logical_group:
|
||||
logical_groups[prop.logical_group].append(prop)
|
||||
|
||||
for group, props in logical_groups.iteritems():
|
||||
logical_count = sum(1 for p in props if p.logical)
|
||||
if logical_count * 2 != len(props):
|
||||
raise RuntimeError("Logical group {} has ".format(group) +
|
||||
"unbalanced logical / physical properties")
|
||||
|
||||
FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
|
||||
FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
|
||||
MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
|
||||
PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
|
||||
CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
|
||||
|
||||
def restriction_flags(property):
|
||||
name = property.name
|
||||
flags = []
|
||||
if name in FIRST_LINE_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_FIRST_LINE")
|
||||
if name in FIRST_LETTER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_FIRST_LETTER")
|
||||
if name in PLACEHOLDER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_PLACEHOLDER")
|
||||
if name in MARKER_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_MARKER")
|
||||
if name in CUE_RESTRICTIONS:
|
||||
flags.append("APPLIES_TO_CUE")
|
||||
return flags
|
||||
|
||||
%>
|
||||
|
||||
/// A group for properties which may override each other
|
||||
/// via logical resolution.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum LogicalGroup {
|
||||
% for group in logical_groups.iterkeys():
|
||||
/// ${group}
|
||||
${to_camel_case(group)},
|
||||
% endfor
|
||||
}
|
||||
|
||||
/// An identifier for a given longhand property.
|
||||
#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
#[repr(u16)]
|
||||
pub enum LonghandId {
|
||||
% for i, property in enumerate(data.longhands):
|
||||
|
@ -1089,6 +1114,7 @@ impl LonghandId {
|
|||
// could potentially do so, which would speed up serialization
|
||||
// algorithms and what not, I guess.
|
||||
<%
|
||||
from functools import cmp_to_key
|
||||
longhand_to_shorthand_map = {}
|
||||
num_sub_properties = {}
|
||||
for shorthand in data.shorthands:
|
||||
|
@ -1099,6 +1125,9 @@ impl LonghandId {
|
|||
|
||||
longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
|
||||
|
||||
def cmp(a, b):
|
||||
return (a > b) - (a < b)
|
||||
|
||||
def preferred_order(x, y):
|
||||
# Since we want properties in order from most subproperties to least,
|
||||
# reverse the arguments to cmp from the expected order.
|
||||
|
@ -1110,8 +1139,8 @@ impl LonghandId {
|
|||
|
||||
# Sort the lists of shorthand properties according to preferred order:
|
||||
# https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
|
||||
for shorthand_list in longhand_to_shorthand_map.itervalues():
|
||||
shorthand_list.sort(cmp=preferred_order)
|
||||
for shorthand_list in longhand_to_shorthand_map.values():
|
||||
shorthand_list.sort(key=cmp_to_key(preferred_order))
|
||||
%>
|
||||
|
||||
// based on lookup results for each longhand, create result arrays
|
||||
|
@ -1353,7 +1382,7 @@ where
|
|||
}
|
||||
|
||||
/// An identifier for a given shorthand property.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
#[repr(u16)]
|
||||
pub enum ShorthandId {
|
||||
% for i, property in enumerate(data.shorthands):
|
||||
|
@ -1590,10 +1619,7 @@ impl UnparsedValue {
|
|||
} else {
|
||||
CSSWideKeyword::Initial
|
||||
};
|
||||
PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id: longhand_id,
|
||||
keyword,
|
||||
})
|
||||
PropertyDeclaration::css_wide_keyword(longhand_id, keyword)
|
||||
};
|
||||
|
||||
let css = match crate::custom_properties::substitute(
|
||||
|
@ -1630,10 +1656,7 @@ impl UnparsedValue {
|
|||
let mut input = Parser::new(&mut input);
|
||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||
if let Ok(keyword) = input.try(CSSWideKeyword::parse) {
|
||||
return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id: longhand_id,
|
||||
keyword,
|
||||
});
|
||||
return PropertyDeclaration::css_wide_keyword(longhand_id, keyword);
|
||||
}
|
||||
|
||||
let declaration = input.parse_entirely(|input| {
|
||||
|
@ -2239,6 +2262,12 @@ impl PropertyDeclaration {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a CSS-wide keyword declaration for a given property.
|
||||
#[inline]
|
||||
pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
|
||||
Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
|
||||
}
|
||||
|
||||
/// Returns a CSS-wide keyword if the declaration's value is one.
|
||||
#[inline]
|
||||
pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
|
||||
|
@ -2367,9 +2396,7 @@ impl PropertyDeclaration {
|
|||
PropertyId::Longhand(id) => {
|
||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||
input.try(CSSWideKeyword::parse).map(|keyword| {
|
||||
PropertyDeclaration::CSSWideKeyword(
|
||||
WideKeywordDeclaration { id, keyword },
|
||||
)
|
||||
PropertyDeclaration::css_wide_keyword(id, keyword)
|
||||
}).or_else(|()| {
|
||||
input.look_for_var_or_env_functions();
|
||||
input.parse_entirely(|input| id.parse_value(context, input))
|
||||
|
@ -2403,12 +2430,7 @@ impl PropertyDeclaration {
|
|||
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
|
||||
} else {
|
||||
for longhand in id.longhands() {
|
||||
declarations.push(PropertyDeclaration::CSSWideKeyword(
|
||||
WideKeywordDeclaration {
|
||||
id: longhand,
|
||||
keyword,
|
||||
},
|
||||
))
|
||||
declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2550,12 +2572,7 @@ impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
|
|||
match *self.all_shorthand {
|
||||
AllShorthand::NotSet => None,
|
||||
AllShorthand::CSSWideKeyword(ref keyword) => {
|
||||
Some(PropertyDeclaration::CSSWideKeyword(
|
||||
WideKeywordDeclaration {
|
||||
id: self.longhands.next()?,
|
||||
keyword: *keyword
|
||||
}
|
||||
))
|
||||
Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword))
|
||||
}
|
||||
AllShorthand::WithVariables(ref unparsed) => {
|
||||
Some(PropertyDeclaration::WithVariables(
|
||||
|
|
|
@ -70,11 +70,10 @@ pub fn parse_border<'i, 't>(
|
|||
let mut width = None;
|
||||
let mut any = false;
|
||||
loop {
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
if width.is_none() {
|
||||
if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) {
|
||||
width = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if style.is_none() {
|
||||
|
@ -84,9 +83,9 @@ pub fn parse_border<'i, 't>(
|
|||
continue
|
||||
}
|
||||
}
|
||||
if width.is_none() {
|
||||
if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) {
|
||||
width = Some(value);
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -549,7 +549,7 @@
|
|||
use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
|
||||
use crate::values::generics::grid::GridTemplateComponent;
|
||||
use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
|
||||
use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas};
|
||||
use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
|
@ -566,28 +566,28 @@
|
|||
input: &mut Parser<'i, 't>,
|
||||
is_row: bool,
|
||||
) -> Result<GridAutoFlow, ParseError<'i>> {
|
||||
let mut auto_flow = None;
|
||||
let mut dense = false;
|
||||
let mut track = None;
|
||||
let mut dense = GridAutoFlow::empty();
|
||||
|
||||
for _ in 0..2 {
|
||||
if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() {
|
||||
auto_flow = if is_row {
|
||||
Some(AutoFlow::Row)
|
||||
track = if is_row {
|
||||
Some(GridAutoFlow::ROW)
|
||||
} else {
|
||||
Some(AutoFlow::Column)
|
||||
Some(GridAutoFlow::COLUMN)
|
||||
};
|
||||
} else if input.try(|i| i.expect_ident_matching("dense")).is_ok() {
|
||||
dense = true;
|
||||
dense = GridAutoFlow::DENSE
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
auto_flow.map(|flow| {
|
||||
GridAutoFlow {
|
||||
autoflow: flow,
|
||||
dense: dense,
|
||||
}
|
||||
}).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
if track.is_some() {
|
||||
Ok(track.unwrap() | dense)
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) {
|
||||
|
@ -637,7 +637,7 @@
|
|||
self.grid_template_areas, dest);
|
||||
}
|
||||
|
||||
if self.grid_auto_flow.autoflow == AutoFlow::Column {
|
||||
if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
|
||||
// It should fail to serialize if other branch of the if condition's values are set.
|
||||
if !self.grid_auto_rows.is_initial() ||
|
||||
!self.grid_template_columns.is_initial() {
|
||||
|
@ -653,7 +653,7 @@
|
|||
|
||||
self.grid_template_rows.to_css(dest)?;
|
||||
dest.write_str(" / auto-flow")?;
|
||||
if self.grid_auto_flow.dense {
|
||||
if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
|
||||
dest.write_str(" dense")?;
|
||||
}
|
||||
|
||||
|
@ -676,7 +676,7 @@
|
|||
}
|
||||
|
||||
dest.write_str("auto-flow")?;
|
||||
if self.grid_auto_flow.dense {
|
||||
if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
|
||||
dest.write_str(" dense")?;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,11 +55,6 @@ pub fn containing_shadow_ignoring_svg_use<E: TElement>(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_rules_from(rules: &mut ApplicableDeclarationList, start: usize) {
|
||||
rules[start..].sort_unstable_by_key(|block| (block.specificity, block.source_order()));
|
||||
}
|
||||
|
||||
/// An object that we use with all the intermediate state needed for the
|
||||
/// cascade.
|
||||
///
|
||||
|
@ -82,6 +77,7 @@ where
|
|||
flags_setter: &'a mut F,
|
||||
matches_user_and_author_rules: bool,
|
||||
matches_document_author_rules: bool,
|
||||
in_sort_scope: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, E, F: 'a> RuleCollector<'a, 'b, E, F>
|
||||
|
@ -134,9 +130,36 @@ where
|
|||
rules,
|
||||
matches_user_and_author_rules,
|
||||
matches_document_author_rules: matches_user_and_author_rules,
|
||||
in_sort_scope: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up the state necessary to collect rules from a given DOM tree
|
||||
/// (either the document tree, or a shadow tree).
|
||||
///
|
||||
/// All rules in the same tree need to be matched together, and this
|
||||
/// function takes care of sorting them by specificity and source order.
|
||||
#[inline]
|
||||
fn in_tree(&mut self, host: Option<E>, f: impl FnOnce(&mut Self)) {
|
||||
debug_assert!(!self.in_sort_scope, "Nested sorting makes no sense");
|
||||
let start = self.rules.len();
|
||||
self.in_sort_scope = true;
|
||||
let old_host = self.context.current_host.take();
|
||||
self.context.current_host = host.map(|e| e.opaque());
|
||||
f(self);
|
||||
if start != self.rules.len() {
|
||||
self.rules[start..]
|
||||
.sort_unstable_by_key(|block| (block.specificity, block.source_order()));
|
||||
}
|
||||
self.context.current_host = old_host;
|
||||
self.in_sort_scope = false;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) {
|
||||
self.in_tree(Some(host), f);
|
||||
}
|
||||
|
||||
fn collect_stylist_rules(&mut self, origin: Origin) {
|
||||
let cascade_level = match origin {
|
||||
Origin::UserAgent => CascadeLevel::UANormal,
|
||||
|
@ -150,7 +173,9 @@ where
|
|||
None => return,
|
||||
};
|
||||
|
||||
self.collect_rules_internal(None, map, cascade_level);
|
||||
self.in_tree(None, |collector| {
|
||||
collector.collect_rules_in_map(map, cascade_level);
|
||||
});
|
||||
}
|
||||
|
||||
fn collect_user_agent_rules(&mut self) {
|
||||
|
@ -189,39 +214,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_rules_in_shadow_tree(
|
||||
&mut self,
|
||||
shadow_host: E,
|
||||
map: &SelectorMap<Rule>,
|
||||
cascade_level: CascadeLevel,
|
||||
) {
|
||||
debug_assert!(shadow_host.shadow_root().is_some());
|
||||
self.collect_rules_internal(Some(shadow_host), map, cascade_level);
|
||||
#[inline]
|
||||
fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel) {
|
||||
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
|
||||
SelectorMap::get_matching_rules(
|
||||
self.element,
|
||||
part_rules,
|
||||
&mut self.rules,
|
||||
&mut self.context,
|
||||
&mut self.flags_setter,
|
||||
cascade_level,
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_rules_internal(
|
||||
&mut self,
|
||||
shadow_host: Option<E>,
|
||||
map: &SelectorMap<Rule>,
|
||||
cascade_level: CascadeLevel,
|
||||
) {
|
||||
let element = self.element;
|
||||
let rule_hash_target = self.rule_hash_target;
|
||||
let rules = &mut self.rules;
|
||||
let flags_setter = &mut self.flags_setter;
|
||||
let start = rules.len();
|
||||
self.context.with_shadow_host(shadow_host, |context| {
|
||||
map.get_all_matching_rules(
|
||||
element,
|
||||
rule_hash_target,
|
||||
rules,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
);
|
||||
});
|
||||
sort_rules_from(rules, start);
|
||||
fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel) {
|
||||
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
|
||||
map.get_all_matching_rules(
|
||||
self.element,
|
||||
self.rule_hash_target,
|
||||
&mut self.rules,
|
||||
&mut self.context,
|
||||
&mut self.flags_setter,
|
||||
cascade_level,
|
||||
);
|
||||
}
|
||||
|
||||
/// Collects the rules for the ::slotted pseudo-element and the :host
|
||||
|
@ -258,17 +274,16 @@ where
|
|||
None => continue,
|
||||
};
|
||||
|
||||
self.collect_rules_in_shadow_tree(
|
||||
shadow.host(),
|
||||
slotted_rules,
|
||||
CascadeLevel::AuthorNormal {
|
||||
self.in_shadow_tree(shadow.host(), |collector| {
|
||||
let cascade_level = CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
};
|
||||
collector.collect_rules_in_map(slotted_rules, cascade_level);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_normal_rules_from_containing_shadow_tree(&mut self) {
|
||||
fn collect_rules_from_containing_shadow_tree(&mut self) {
|
||||
if !self.matches_user_and_author_rules {
|
||||
return;
|
||||
}
|
||||
|
@ -281,11 +296,34 @@ where
|
|||
|
||||
self.matches_document_author_rules = false;
|
||||
|
||||
let cascade_data = containing_shadow.style_data();
|
||||
let host = containing_shadow.host();
|
||||
if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) {
|
||||
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal());
|
||||
}
|
||||
let cascade_data = match containing_shadow.style_data() {
|
||||
Some(c) => c,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let cascade_level = CascadeLevel::same_tree_author_normal();
|
||||
self.in_shadow_tree(containing_shadow.host(), |collector| {
|
||||
if let Some(map) = cascade_data.normal_rules(collector.pseudo_element) {
|
||||
collector.collect_rules_in_map(map, cascade_level);
|
||||
}
|
||||
|
||||
// Collect rules from :host::part() and such
|
||||
let hash_target = collector.rule_hash_target;
|
||||
if !hash_target.has_part_attr() {
|
||||
return;
|
||||
}
|
||||
|
||||
let part_rules = match cascade_data.part_rules(collector.pseudo_element) {
|
||||
Some(p) => p,
|
||||
None => return,
|
||||
};
|
||||
|
||||
hash_target.each_part(|part| {
|
||||
if let Some(part_rules) = part_rules.get(part) {
|
||||
collector.collect_rules_in_list(part_rules, cascade_level);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Collects the rules for the :host pseudo-class.
|
||||
|
@ -311,13 +349,12 @@ where
|
|||
};
|
||||
|
||||
let rule_hash_target = self.rule_hash_target;
|
||||
self.collect_rules_in_shadow_tree(
|
||||
rule_hash_target,
|
||||
host_rules,
|
||||
CascadeLevel::AuthorNormal {
|
||||
self.in_shadow_tree(rule_hash_target, |collector| {
|
||||
let cascade_level = CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
};
|
||||
collector.collect_rules_in_map(host_rules, cascade_level);
|
||||
});
|
||||
}
|
||||
|
||||
fn collect_document_author_rules(&mut self) {
|
||||
|
@ -328,7 +365,7 @@ where
|
|||
self.collect_stylist_rules(Origin::Author);
|
||||
}
|
||||
|
||||
fn collect_part_rules(&mut self) {
|
||||
fn collect_part_rules_from_outer_trees(&mut self) {
|
||||
if !self.rule_hash_target.has_part_attr() {
|
||||
return;
|
||||
}
|
||||
|
@ -363,28 +400,16 @@ where
|
|||
|
||||
if let Some(part_rules) = part_rules {
|
||||
let containing_host = outer_shadow.map(|s| s.host());
|
||||
let element = self.element;
|
||||
let rules = &mut self.rules;
|
||||
let flags_setter = &mut self.flags_setter;
|
||||
let cascade_level = CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
};
|
||||
let start = rules.len();
|
||||
self.context.with_shadow_host(containing_host, |context| {
|
||||
self.in_tree(containing_host, |collector| {
|
||||
for p in &parts {
|
||||
if let Some(part_rules) = part_rules.get(p) {
|
||||
SelectorMap::get_matching_rules(
|
||||
element,
|
||||
&part_rules,
|
||||
rules,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
);
|
||||
collector.collect_rules_in_list(part_rules, cascade_level);
|
||||
}
|
||||
}
|
||||
});
|
||||
sort_rules_from(rules, start);
|
||||
shadow_cascade_order.inc();
|
||||
}
|
||||
|
||||
|
@ -393,14 +418,13 @@ where
|
|||
None => break, // Nowhere to export to.
|
||||
};
|
||||
|
||||
parts.retain(|part| {
|
||||
let exported_part = match inner_shadow_host.exported_part(part) {
|
||||
Some(part) => part,
|
||||
None => return false,
|
||||
};
|
||||
std::mem::replace(part, exported_part);
|
||||
true
|
||||
});
|
||||
let mut new_parts = SmallVec::new();
|
||||
for part in &parts {
|
||||
inner_shadow_host.each_exported_part(part, |exported_part| {
|
||||
new_parts.push(exported_part.clone());
|
||||
});
|
||||
}
|
||||
parts = new_parts;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,10 +485,10 @@ where
|
|||
return;
|
||||
}
|
||||
self.collect_host_and_slotted_rules();
|
||||
self.collect_normal_rules_from_containing_shadow_tree();
|
||||
self.collect_rules_from_containing_shadow_tree();
|
||||
self.collect_document_author_rules();
|
||||
self.collect_style_attribute();
|
||||
self.collect_part_rules();
|
||||
self.collect_part_rules_from_outer_trees();
|
||||
self.collect_animation_rules();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
//! The rule tree.
|
||||
|
||||
use crate::applicable_declarations::ApplicableDeclarationList;
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko::selector_parser::PseudoElement;
|
||||
use crate::hash::{self, FxHashMap};
|
||||
use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
|
||||
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
||||
|
@ -136,29 +134,6 @@ impl StyleSource {
|
|||
let _ = write!(writer, " -> {:?}", self.read(guard).declarations());
|
||||
}
|
||||
|
||||
// This is totally unsafe, should be removed when we figure out the cause of
|
||||
// bug 1607553.
|
||||
#[cfg(feature = "gecko")]
|
||||
unsafe fn dump_unchecked<W: Write>(&self, writer: &mut W) {
|
||||
if let Some(ref rule) = self.0.as_first() {
|
||||
let rule = rule.read_unchecked();
|
||||
let _ = write!(writer, "{:?}", rule.selectors);
|
||||
}
|
||||
let _ = write!(writer, " -> {:?}", self.read_unchecked().declarations());
|
||||
}
|
||||
|
||||
// This is totally unsafe, should be removed when we figure out the cause of
|
||||
// bug 1607553.
|
||||
#[inline]
|
||||
#[cfg(feature = "gecko")]
|
||||
unsafe fn read_unchecked(&self) -> &PropertyDeclarationBlock {
|
||||
let block: &Locked<PropertyDeclarationBlock> = match self.0.borrow() {
|
||||
ArcUnionBorrow::First(ref rule) => &rule.get().read_unchecked().block,
|
||||
ArcUnionBorrow::Second(ref block) => block.get(),
|
||||
};
|
||||
block.read_unchecked()
|
||||
}
|
||||
|
||||
/// Read the style source guard, and obtain thus read access to the
|
||||
/// underlying property declaration block.
|
||||
#[inline]
|
||||
|
@ -1441,198 +1416,6 @@ impl StrongRuleNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if any properties specified by `rule_type_mask` was set by
|
||||
/// an author rule.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn has_author_specified_rules<E>(
|
||||
&self,
|
||||
mut element: E,
|
||||
mut pseudo: Option<PseudoElement>,
|
||||
guards: &StylesheetGuards,
|
||||
rule_type_mask: u32,
|
||||
author_colors_allowed: bool,
|
||||
) -> bool
|
||||
where
|
||||
E: crate::dom::TElement,
|
||||
{
|
||||
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BACKGROUND;
|
||||
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BORDER;
|
||||
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING;
|
||||
use crate::properties::{CSSWideKeyword, LonghandId};
|
||||
use crate::properties::{PropertyDeclaration, PropertyDeclarationId};
|
||||
use std::borrow::Cow;
|
||||
|
||||
// Reset properties:
|
||||
const BACKGROUND_PROPS: &'static [LonghandId] =
|
||||
&[LonghandId::BackgroundColor, LonghandId::BackgroundImage];
|
||||
|
||||
const BORDER_PROPS: &'static [LonghandId] = &[
|
||||
LonghandId::BorderTopColor,
|
||||
LonghandId::BorderTopStyle,
|
||||
LonghandId::BorderTopWidth,
|
||||
LonghandId::BorderRightColor,
|
||||
LonghandId::BorderRightStyle,
|
||||
LonghandId::BorderRightWidth,
|
||||
LonghandId::BorderBottomColor,
|
||||
LonghandId::BorderBottomStyle,
|
||||
LonghandId::BorderBottomWidth,
|
||||
LonghandId::BorderLeftColor,
|
||||
LonghandId::BorderLeftStyle,
|
||||
LonghandId::BorderLeftWidth,
|
||||
LonghandId::BorderTopLeftRadius,
|
||||
LonghandId::BorderTopRightRadius,
|
||||
LonghandId::BorderBottomRightRadius,
|
||||
LonghandId::BorderBottomLeftRadius,
|
||||
LonghandId::BorderInlineStartColor,
|
||||
LonghandId::BorderInlineStartStyle,
|
||||
LonghandId::BorderInlineStartWidth,
|
||||
LonghandId::BorderInlineEndColor,
|
||||
LonghandId::BorderInlineEndStyle,
|
||||
LonghandId::BorderInlineEndWidth,
|
||||
LonghandId::BorderBlockStartColor,
|
||||
LonghandId::BorderBlockStartStyle,
|
||||
LonghandId::BorderBlockStartWidth,
|
||||
LonghandId::BorderBlockEndColor,
|
||||
LonghandId::BorderBlockEndStyle,
|
||||
LonghandId::BorderBlockEndWidth,
|
||||
];
|
||||
|
||||
const PADDING_PROPS: &'static [LonghandId] = &[
|
||||
LonghandId::PaddingTop,
|
||||
LonghandId::PaddingRight,
|
||||
LonghandId::PaddingBottom,
|
||||
LonghandId::PaddingLeft,
|
||||
LonghandId::PaddingInlineStart,
|
||||
LonghandId::PaddingInlineEnd,
|
||||
LonghandId::PaddingBlockStart,
|
||||
LonghandId::PaddingBlockEnd,
|
||||
];
|
||||
|
||||
// Set of properties that we are currently interested in.
|
||||
let mut properties = LonghandIdSet::new();
|
||||
|
||||
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
|
||||
for id in BACKGROUND_PROPS {
|
||||
properties.insert(*id);
|
||||
}
|
||||
}
|
||||
if rule_type_mask & NS_AUTHOR_SPECIFIED_BORDER != 0 {
|
||||
for id in BORDER_PROPS {
|
||||
properties.insert(*id);
|
||||
}
|
||||
}
|
||||
if rule_type_mask & NS_AUTHOR_SPECIFIED_PADDING != 0 {
|
||||
for id in PADDING_PROPS {
|
||||
properties.insert(*id);
|
||||
}
|
||||
}
|
||||
|
||||
// If author colors are not allowed, don't look at those properties
|
||||
// (except for background-color which is special and we handle below).
|
||||
if !author_colors_allowed {
|
||||
properties.remove_all(LonghandIdSet::ignored_when_colors_disabled());
|
||||
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
|
||||
properties.insert(LonghandId::BackgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
let mut element_rule_node = Cow::Borrowed(self);
|
||||
|
||||
loop {
|
||||
// We need to be careful not to count styles covered up by
|
||||
// user-important or UA-important declarations. But we do want to
|
||||
// catch explicit inherit styling in those and check our parent
|
||||
// element to see whether we have user styling for those properties.
|
||||
// Note that we don't care here about inheritance due to lack of a
|
||||
// specified value, since all the properties we care about are reset
|
||||
// properties.
|
||||
|
||||
let mut inherited_properties = LonghandIdSet::new();
|
||||
let mut have_explicit_ua_inherit = false;
|
||||
|
||||
for node in element_rule_node.self_and_ancestors() {
|
||||
let source = node.style_source();
|
||||
let declarations = if source.is_some() {
|
||||
source
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read(node.cascade_level().guard(guards))
|
||||
.declaration_importance_iter()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Iterate over declarations of the longhands we care about.
|
||||
let node_importance = node.importance();
|
||||
let longhands = declarations.rev().filter_map(|(declaration, importance)| {
|
||||
if importance != node_importance {
|
||||
return None;
|
||||
}
|
||||
match declaration.id() {
|
||||
PropertyDeclarationId::Longhand(id) => Some((id, declaration)),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
let is_author = node.cascade_level().origin() == Origin::Author;
|
||||
for (id, declaration) in longhands {
|
||||
if !properties.contains(id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_author {
|
||||
if !author_colors_allowed {
|
||||
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
|
||||
if color.is_transparent() {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This property was set by a non-author rule.
|
||||
// Stop looking for it in this element's rule
|
||||
// nodes.
|
||||
properties.remove(id);
|
||||
|
||||
// However, if it is inherited, then it might be
|
||||
// inherited from an author rule from an
|
||||
// ancestor element's rule nodes.
|
||||
if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) {
|
||||
have_explicit_ua_inherit = true;
|
||||
inherited_properties.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !have_explicit_ua_inherit {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue to the parent element and search for the inherited properties.
|
||||
if let Some(pseudo) = pseudo.take() {
|
||||
if pseudo.inherits_from_default_values() {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
element = match element.inheritance_parent() {
|
||||
Some(parent) => parent,
|
||||
None => break,
|
||||
};
|
||||
|
||||
let parent_data = element.mutate_data().unwrap();
|
||||
let parent_rule_node = parent_data.styles.primary().rules().clone();
|
||||
element_rule_node = Cow::Owned(parent_rule_node);
|
||||
}
|
||||
|
||||
properties = inherited_properties;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if there is either animation or transition level rule.
|
||||
pub fn has_animation_or_transition_rules(&self) -> bool {
|
||||
self.self_and_ancestors()
|
||||
|
@ -1742,47 +1525,6 @@ impl Drop for StrongRuleNode {
|
|||
return;
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline(always)]
|
||||
fn assert_on_release() -> bool {
|
||||
crate::gecko_bindings::structs::GECKO_IS_NIGHTLY
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn assert_on_release() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) || assert_on_release() {
|
||||
let children = node.children.read();
|
||||
if !children.is_empty() {
|
||||
let mut crash_str = vec![];
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
unsafe {
|
||||
// Try to unsafely collect some information of this before
|
||||
// crashing the process.
|
||||
if let Some(ref s) = node.source {
|
||||
s.dump_unchecked(&mut crash_str);
|
||||
crash_str.push(b'\n');
|
||||
}
|
||||
children.each(|child| {
|
||||
(*child.ptr())
|
||||
.source
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.dump_unchecked(&mut crash_str);
|
||||
crash_str.push(b'\n');
|
||||
});
|
||||
}
|
||||
|
||||
panic!(
|
||||
"Children left in the rule tree on drop: {}",
|
||||
String::from_utf8_lossy(&crash_str).trim()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if node.parent.is_none() {
|
||||
debug!("Dropping root node!");
|
||||
// The free list should be null by this point
|
||||
|
|
|
@ -709,10 +709,6 @@ impl ElementSnapshot for ServoElementSnapshot {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -227,15 +227,21 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
fn set_bits(&mut self) {
|
||||
let display = self.style.get_box().clone_display();
|
||||
|
||||
if !display.is_contents() &&
|
||||
!self
|
||||
if !display.is_contents() {
|
||||
if !self
|
||||
.style
|
||||
.get_text()
|
||||
.clone_text_decoration_line()
|
||||
.is_empty()
|
||||
{
|
||||
self.style
|
||||
.add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
|
||||
{
|
||||
self.style
|
||||
.add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
|
||||
}
|
||||
|
||||
if self.style.get_effects().clone_opacity() == 0. {
|
||||
self.style
|
||||
.add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
|
||||
}
|
||||
}
|
||||
|
||||
if self.style.is_pseudo_element() {
|
||||
|
@ -488,6 +494,35 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// <textarea>'s editor root needs to inherit the overflow value from its
|
||||
/// parent, but we need to make sure it's still scrollable.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn adjust_for_text_control_editing_root(&mut self) {
|
||||
use crate::selector_parser::PseudoElement;
|
||||
|
||||
if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
let box_style = self.style.get_box();
|
||||
let overflow_x = box_style.clone_overflow_x();
|
||||
let overflow_y = box_style.clone_overflow_y();
|
||||
|
||||
fn scrollable(v: Overflow) -> bool {
|
||||
v != Overflow::MozHiddenUnscrollable && v != Overflow::Visible
|
||||
}
|
||||
|
||||
// If at least one is scrollable we'll adjust the other one in
|
||||
// adjust_for_overflow if needed.
|
||||
if scrollable(overflow_x) || scrollable(overflow_y) {
|
||||
return;
|
||||
}
|
||||
|
||||
let box_style = self.style.mutate_box();
|
||||
box_style.set_overflow_x(Overflow::Auto);
|
||||
box_style.set_overflow_y(Overflow::Auto);
|
||||
}
|
||||
|
||||
/// If a <fieldset> has grid/flex display type, we need to inherit
|
||||
/// this type into its ::-moz-fieldset-content anonymous box.
|
||||
///
|
||||
|
@ -496,9 +531,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
/// normal cascading process.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
|
||||
match self.style.pseudo {
|
||||
Some(ref p) if p.is_fieldset_content() => {},
|
||||
_ => return,
|
||||
use crate::selector_parser::PseudoElement;
|
||||
|
||||
if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.style.get_box().clone_display(), Display::Block);
|
||||
|
@ -780,6 +816,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
{
|
||||
self.adjust_for_prohibited_display_contents(element);
|
||||
self.adjust_for_fieldset_content(layout_parent_style);
|
||||
// NOTE: It's important that this happens before
|
||||
// adjust_for_overflow.
|
||||
self.adjust_for_text_control_editing_root();
|
||||
}
|
||||
self.adjust_for_top_layer();
|
||||
self.blockify_if_necessary(layout_parent_style, element);
|
||||
|
|
|
@ -294,7 +294,9 @@ where
|
|||
// Removing sheets makes us tear down the whole cascade and invalidation
|
||||
// data, but only if the sheet has been involved in at least one flush.
|
||||
// Checking whether the sheet has been committed allows us to avoid
|
||||
// rebuilding the world when sites quickly append and remove a stylesheet.
|
||||
// rebuilding the world when sites quickly append and remove a
|
||||
// stylesheet.
|
||||
//
|
||||
// See bug 1434756.
|
||||
if sheet.committed {
|
||||
self.set_data_validity_at_least(DataValidity::FullyInvalid);
|
||||
|
|
|
@ -58,8 +58,8 @@ pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};
|
|||
pub use self::rules_iterator::{AllRules, EffectiveRules};
|
||||
pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
|
||||
pub use self::style_rule::StyleRule;
|
||||
pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
|
||||
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
|
||||
pub use self::stylesheet::{SanitizationData, SanitizationKind};
|
||||
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
|
||||
pub use self::supports_rule::SupportsRule;
|
||||
pub use self::viewport_rule::ViewportRule;
|
||||
|
@ -369,6 +369,7 @@ impl CssRule {
|
|||
shared_lock: &SharedRwLock,
|
||||
state: State,
|
||||
loader: Option<&dyn StylesheetLoader>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
) -> Result<Self, RulesMutateError> {
|
||||
let url_data = parent_stylesheet_contents.url_data.read();
|
||||
let context = ParserContext::new(
|
||||
|
@ -395,6 +396,7 @@ impl CssRule {
|
|||
dom_error: None,
|
||||
namespaces: &mut *guard,
|
||||
insert_rule_context: Some(insert_rule_context),
|
||||
allow_import_rules,
|
||||
};
|
||||
|
||||
parse_one_rule(&mut input, &mut rule_parser)
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::str::CssStringWriter;
|
|||
use crate::stylesheets::loader::StylesheetLoader;
|
||||
use crate::stylesheets::rule_parser::{InsertRuleContext, State};
|
||||
use crate::stylesheets::stylesheet::StylesheetContents;
|
||||
use crate::stylesheets::{CssRule, RulesMutateError};
|
||||
use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError};
|
||||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
|
||||
use servo_arc::{Arc, RawOffsetArc};
|
||||
|
@ -128,6 +128,7 @@ pub trait CssRulesHelpers {
|
|||
index: usize,
|
||||
nested: bool,
|
||||
loader: Option<&dyn StylesheetLoader>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
) -> Result<CssRule, RulesMutateError>;
|
||||
}
|
||||
|
||||
|
@ -140,6 +141,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
|
|||
index: usize,
|
||||
nested: bool,
|
||||
loader: Option<&dyn StylesheetLoader>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
) -> Result<CssRule, RulesMutateError> {
|
||||
let new_rule = {
|
||||
let read_guard = lock.read();
|
||||
|
@ -176,6 +178,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
|
|||
lock,
|
||||
state,
|
||||
loader,
|
||||
allow_import_rules,
|
||||
)?
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use crate::stylesheets::keyframes_rule::parse_keyframe_list;
|
|||
use crate::stylesheets::stylesheet::Namespaces;
|
||||
use crate::stylesheets::supports_rule::SupportsCondition;
|
||||
use crate::stylesheets::viewport_rule;
|
||||
use crate::stylesheets::AllowImportRules;
|
||||
use crate::stylesheets::{CorsMode, DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
|
||||
use crate::stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader};
|
||||
use crate::stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
|
||||
|
@ -50,7 +51,7 @@ pub struct TopLevelRuleParser<'a> {
|
|||
/// This won't contain any namespaces, and only nested parsers created with
|
||||
/// `ParserContext::new_with_rule_type` will.
|
||||
pub context: ParserContext<'a>,
|
||||
/// The current state of the parser.
|
||||
/// The current stajkj/te of the parser.
|
||||
pub state: State,
|
||||
/// Whether we have tried to parse was invalid due to being in the wrong
|
||||
/// place (e.g. an @import rule was found while in the `Body` state). Reset
|
||||
|
@ -62,6 +63,8 @@ pub struct TopLevelRuleParser<'a> {
|
|||
pub namespaces: &'a mut Namespaces,
|
||||
/// The info we need insert a rule in a list.
|
||||
pub insert_rule_context: Option<InsertRuleContext<'a>>,
|
||||
/// Whether @import rules will be allowed.
|
||||
pub allow_import_rules: AllowImportRules,
|
||||
}
|
||||
|
||||
impl<'b> TopLevelRuleParser<'b> {
|
||||
|
@ -189,6 +192,10 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
|
||||
}
|
||||
|
||||
if let AllowImportRules::No = self.allow_import_rules {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::DisallowedImportRule))
|
||||
}
|
||||
|
||||
// FIXME(emilio): We should always be able to have a loader
|
||||
// around! See bug 1533783.
|
||||
if self.loader.is_none() {
|
||||
|
@ -203,6 +210,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
let media = Arc::new(self.shared_lock.wrap(media));
|
||||
|
||||
let prelude = AtRuleNonBlockPrelude::Import(url, media);
|
||||
|
||||
return Ok(AtRuleType::WithoutBlock(prelude));
|
||||
},
|
||||
"namespace" => {
|
||||
|
|
|
@ -81,6 +81,7 @@ impl StylesheetContents {
|
|||
quirks_mode: QuirksMode,
|
||||
line_number_offset: u32,
|
||||
use_counters: Option<&UseCounters>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
sanitization_data: Option<&mut SanitizationData>,
|
||||
) -> Self {
|
||||
let namespaces = RwLock::new(Namespaces::default());
|
||||
|
@ -95,6 +96,7 @@ impl StylesheetContents {
|
|||
quirks_mode,
|
||||
line_number_offset,
|
||||
use_counters,
|
||||
allow_import_rules,
|
||||
sanitization_data,
|
||||
);
|
||||
|
||||
|
@ -355,6 +357,16 @@ pub enum SanitizationKind {
|
|||
NoConditionalRules,
|
||||
}
|
||||
|
||||
/// Whether @import rules are allowed.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum AllowImportRules {
|
||||
/// @import rules will be parsed.
|
||||
Yes,
|
||||
/// @import rules will not be parsed.
|
||||
No,
|
||||
}
|
||||
|
||||
impl SanitizationKind {
|
||||
fn allows(self, rule: &CssRule) -> bool {
|
||||
debug_assert_ne!(self, SanitizationKind::None);
|
||||
|
@ -415,6 +427,7 @@ impl Stylesheet {
|
|||
stylesheet_loader: Option<&dyn StylesheetLoader>,
|
||||
error_reporter: Option<&dyn ParseErrorReporter>,
|
||||
line_number_offset: u32,
|
||||
allow_import_rules: AllowImportRules,
|
||||
) {
|
||||
let namespaces = RwLock::new(Namespaces::default());
|
||||
|
||||
|
@ -430,6 +443,7 @@ impl Stylesheet {
|
|||
existing.contents.quirks_mode,
|
||||
line_number_offset,
|
||||
/* use_counters = */ None,
|
||||
allow_import_rules,
|
||||
/* sanitization_data = */ None,
|
||||
);
|
||||
|
||||
|
@ -457,6 +471,7 @@ impl Stylesheet {
|
|||
quirks_mode: QuirksMode,
|
||||
line_number_offset: u32,
|
||||
use_counters: Option<&UseCounters>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
mut sanitization_data: Option<&mut SanitizationData>,
|
||||
) -> (Vec<CssRule>, Option<String>, Option<String>) {
|
||||
let mut rules = Vec::new();
|
||||
|
@ -481,6 +496,7 @@ impl Stylesheet {
|
|||
dom_error: None,
|
||||
insert_rule_context: None,
|
||||
namespaces,
|
||||
allow_import_rules,
|
||||
};
|
||||
|
||||
{
|
||||
|
@ -537,6 +553,7 @@ impl Stylesheet {
|
|||
error_reporter: Option<&dyn ParseErrorReporter>,
|
||||
quirks_mode: QuirksMode,
|
||||
line_number_offset: u32,
|
||||
allow_import_rules: AllowImportRules,
|
||||
) -> Self {
|
||||
// FIXME: Consider adding use counters to Servo?
|
||||
let contents = StylesheetContents::from_str(
|
||||
|
@ -549,6 +566,7 @@ impl Stylesheet {
|
|||
quirks_mode,
|
||||
line_number_offset,
|
||||
/* use_counters = */ None,
|
||||
allow_import_rules,
|
||||
/* sanitized_output = */ None,
|
||||
);
|
||||
|
||||
|
|
|
@ -684,7 +684,7 @@ fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
|
|||
where
|
||||
E: TElement,
|
||||
{
|
||||
use crate::values::generics::image::{GenericImageLayer, Image};
|
||||
use crate::values::generics::image::Image;
|
||||
use style_traits::ToCss;
|
||||
|
||||
// We speculatively evaluate any paint worklets during styling.
|
||||
|
@ -694,9 +694,7 @@ where
|
|||
if let Some(ref values) = data.styles.primary {
|
||||
for image in &values.get_background().background_image.0 {
|
||||
let (name, arguments) = match *image {
|
||||
GenericImageLayer::Image(Image::PaintWorklet(ref worklet)) => {
|
||||
(&worklet.name, &worklet.arguments)
|
||||
},
|
||||
Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
|
||||
_ => continue,
|
||||
};
|
||||
let painter = match context.shared.registered_speculative_painters.get(name) {
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ pub mod color;
|
|||
pub mod effects;
|
||||
mod font;
|
||||
mod grid;
|
||||
mod length;
|
||||
mod svg;
|
||||
pub mod transform;
|
||||
|
||||
|
@ -109,9 +108,6 @@ pub fn animate_multiplicative_factor(
|
|||
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
||||
/// `match` arm returns an error.
|
||||
///
|
||||
/// If the two values are not similar, an error is returned unless a fallback
|
||||
/// function has been specified through `#[animate(fallback)]`.
|
||||
///
|
||||
/// Trait bounds for type parameter `Foo` can be opted out of with
|
||||
/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
|
||||
/// fields can be opted into with `#[animation(field_bound)]` on the field.
|
||||
|
@ -457,6 +453,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedZero for Box<[T]>
|
||||
where
|
||||
T: ToAnimatedZero,
|
||||
{
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
self.iter().map(|v| v.to_animated_zero()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedZero for crate::OwnedSlice<T>
|
||||
where
|
||||
T: ToAnimatedZero,
|
||||
|
|
|
@ -14,11 +14,11 @@ use crate::values::generics::basic_shape as generic;
|
|||
/// A computed alias for FillRule.
|
||||
pub use crate::values::generics::basic_shape::FillRule;
|
||||
|
||||
/// A computed clipping shape.
|
||||
pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>;
|
||||
/// A computed `clip-path` value.
|
||||
pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
|
||||
|
||||
/// A computed float area shape.
|
||||
pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>;
|
||||
/// A computed `shape-outside` value.
|
||||
pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
|
||||
|
||||
/// A computed basic shape.
|
||||
pub type BasicShape = generic::GenericBasicShape<
|
||||
|
|
|
@ -178,7 +178,7 @@ impl ToAnimatedValue for FontSize {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, ToResolvedValue)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf))]
|
||||
/// Specifies a prioritized list of font family names or generic family names.
|
||||
pub struct FontFamily {
|
||||
|
@ -227,7 +227,9 @@ impl ToCss for FontFamily {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
/// The name of a font family of choice
|
||||
pub struct FamilyName {
|
||||
|
@ -270,7 +272,9 @@ impl ToCss for FamilyName {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
/// Font family names must either be given quoted as strings,
|
||||
/// or unquoted as a sequence of one or more identifiers.
|
||||
|
@ -285,7 +289,9 @@ pub enum FontFamilyNameSyntax {
|
|||
Identifiers,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
|
||||
/// A set of faces that vary in weight, width or slope.
|
||||
pub enum SingleFontFamily {
|
||||
|
@ -301,15 +307,28 @@ pub enum SingleFontFamily {
|
|||
/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
|
||||
/// sSingleGenerics are updated as well.
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, Parse, ToCss, ToResolvedValue, ToShmem,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
Parse,
|
||||
ToCss,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum GenericFontFamily {
|
||||
/// No generic family specified, only for internal usage.
|
||||
///
|
||||
/// NOTE(emilio): Gecko code relies on this variant being zero.
|
||||
#[css(skip)]
|
||||
None,
|
||||
None = 0,
|
||||
Serif,
|
||||
SansSerif,
|
||||
#[parse(aliases = "-moz-fixed")]
|
||||
|
@ -350,19 +369,22 @@ impl SingleFontFamily {
|
|||
};
|
||||
|
||||
let mut value = first_ident.as_ref().to_owned();
|
||||
let mut serialize_quoted = value.contains(' ');
|
||||
|
||||
// These keywords are not allowed by themselves.
|
||||
// The only way this value can be valid with with another keyword.
|
||||
if reserved {
|
||||
let ident = input.expect_ident()?;
|
||||
serialize_quoted = serialize_quoted || ident.contains(' ');
|
||||
value.push(' ');
|
||||
value.push_str(&ident);
|
||||
}
|
||||
while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
|
||||
serialize_quoted = serialize_quoted || ident.contains(' ');
|
||||
value.push(' ');
|
||||
value.push_str(&ident);
|
||||
}
|
||||
let syntax = if value.starts_with(' ') || value.ends_with(' ') || value.contains(" ") {
|
||||
let syntax = if serialize_quoted {
|
||||
// For font family names which contains special white spaces, e.g.
|
||||
// `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
|
||||
// as identifiers correctly. Just mark them quoted so we don't need
|
||||
|
@ -422,16 +444,22 @@ impl SingleFontFamily {
|
|||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
/// A list of SingleFontFamily
|
||||
pub struct FontFamilyList(Box<[SingleFontFamily]>);
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)]
|
||||
/// A list of SingleFontFamily
|
||||
pub enum FontFamilyList {
|
||||
/// A strong reference to a Gecko SharedFontList object.
|
||||
SharedFontList(RefPtr<structs::SharedFontList>),
|
||||
SharedFontList(
|
||||
#[compute(no_field_bound)]
|
||||
#[resolve(no_field_bound)]
|
||||
RefPtr<structs::SharedFontList>,
|
||||
),
|
||||
/// A font-family generic ID.
|
||||
Generic(GenericFontFamily),
|
||||
}
|
||||
|
@ -675,7 +703,7 @@ pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
|
|||
/// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)]
|
||||
#[repr(C)]
|
||||
pub struct FontLanguageOverride(u32);
|
||||
pub struct FontLanguageOverride(pub u32);
|
||||
|
||||
impl FontLanguageOverride {
|
||||
#[inline]
|
||||
|
@ -686,10 +714,7 @@ impl FontLanguageOverride {
|
|||
|
||||
/// Returns this value as a `&str`, backed by `storage`.
|
||||
#[inline]
|
||||
pub fn to_str(self, storage: &mut [u8; 4]) -> &str {
|
||||
if self.0 == 0 {
|
||||
return "normal";
|
||||
}
|
||||
pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
|
||||
*storage = u32::to_be_bytes(self.0);
|
||||
// Safe because we ensure it's ASCII during computing
|
||||
let slice = if cfg!(debug_assertions) {
|
||||
|
@ -730,10 +755,22 @@ impl ToCss for FontLanguageOverride {
|
|||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
if self.0 == 0 {
|
||||
return dest.write_str("normal");
|
||||
}
|
||||
self.to_str(&mut [0; 4]).to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then
|
||||
// remove this.
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u32> for FontLanguageOverride {
|
||||
fn from(v: u32) -> Self {
|
||||
unsafe { Self::from_u32(v) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<FontLanguageOverride> for u32 {
|
||||
fn from(v: FontLanguageOverride) -> u32 {
|
||||
|
|
|
@ -13,7 +13,8 @@ use crate::values::computed::url::ComputedImageUrl;
|
|||
use crate::values::computed::NumberOrPercentage;
|
||||
use crate::values::computed::{Angle, Color, Context};
|
||||
use crate::values::computed::{
|
||||
LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, ToComputedValue,
|
||||
AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
|
||||
ToComputedValue,
|
||||
};
|
||||
use crate::values::generics::image::{self as generic, GradientCompatMode};
|
||||
use crate::values::specified::image::LineDirection as SpecifiedLineDirection;
|
||||
|
@ -22,9 +23,6 @@ use std::f32::consts::PI;
|
|||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
/// A computed image layer.
|
||||
pub type ImageLayer = generic::GenericImageLayer<Image>;
|
||||
|
||||
/// Computed values for an image according to CSS-IMAGES.
|
||||
/// <https://drafts.csswg.org/css-images/#image-values>
|
||||
pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl>;
|
||||
|
@ -37,6 +35,8 @@ pub type Gradient = generic::GenericGradient<
|
|||
NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
Position,
|
||||
Angle,
|
||||
AngleOrPercentage,
|
||||
Color,
|
||||
>;
|
||||
|
||||
|
@ -57,15 +57,9 @@ pub enum LineDirection {
|
|||
Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
|
||||
}
|
||||
|
||||
/// A computed gradient item.
|
||||
pub type GradientItem = generic::GenericGradientItem<Color, LengthPercentage>;
|
||||
|
||||
/// A computed color stop.
|
||||
pub type ColorStop = generic::ColorStop<Color, LengthPercentage>;
|
||||
|
||||
/// Computed values for `-moz-image-rect(...)`.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>;
|
||||
pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>;
|
||||
|
||||
/// Empty enum on non-gecko
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat};
|
|||
use crate::Zero;
|
||||
use app_units::Au;
|
||||
use std::fmt::{self, Write};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
|
||||
use style_traits::{CSSPixel, CssWriter, ToCss};
|
||||
|
||||
pub use super::image::Image;
|
||||
|
@ -203,6 +203,7 @@ impl Size {
|
|||
Serialize,
|
||||
ToAnimatedValue,
|
||||
ToAnimatedZero,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
|
@ -331,6 +332,13 @@ impl Div<CSSFloat> for CSSPixelLength {
|
|||
}
|
||||
}
|
||||
|
||||
impl MulAssign<CSSFloat> for CSSPixelLength {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: CSSFloat) {
|
||||
self.0 *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for CSSPixelLength {
|
||||
type Output = Self;
|
||||
|
||||
|
|
|
@ -25,15 +25,16 @@
|
|||
//! our expectations.
|
||||
|
||||
use super::{Context, Length, Percentage, ToComputedValue};
|
||||
use crate::values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::generics::{calc, NonNegative};
|
||||
use crate::values::specified::length::FontBaseSize;
|
||||
use crate::values::{specified, CSSFloat};
|
||||
use crate::Zero;
|
||||
use app_units::Au;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::values::specified::AllowedNumericType;
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
@ -162,13 +163,20 @@ impl MallocSizeOf for LengthPercentage {
|
|||
}
|
||||
|
||||
/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, ToCss)]
|
||||
enum Unpacked<'a> {
|
||||
Calc(&'a CalcLengthPercentage),
|
||||
Length(Length),
|
||||
Percentage(Percentage),
|
||||
}
|
||||
|
||||
/// An unpacked `<length-percentage>` that mutably borrows the `calc()` variant.
|
||||
enum UnpackedMut<'a> {
|
||||
Calc(&'a mut CalcLengthPercentage),
|
||||
Length(Length),
|
||||
Percentage(Percentage),
|
||||
}
|
||||
|
||||
/// An unpacked `<length-percentage>` that owns the `calc()` variant, for
|
||||
/// serialization purposes.
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
|
@ -185,6 +193,22 @@ impl LengthPercentage {
|
|||
Self::new_length(Length::new(1.))
|
||||
}
|
||||
|
||||
/// 0%
|
||||
#[inline]
|
||||
pub fn zero_percent() -> Self {
|
||||
Self::new_percent(Percentage::zero())
|
||||
}
|
||||
|
||||
fn to_calc_node(&self) -> Cow<CalcNode> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))),
|
||||
Unpacked::Percentage(p) => {
|
||||
Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)))
|
||||
},
|
||||
Unpacked::Calc(p) => Cow::Borrowed(&p.node),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a length value.
|
||||
#[inline]
|
||||
pub fn new_length(length: Length) -> Self {
|
||||
|
@ -211,25 +235,46 @@ impl LengthPercentage {
|
|||
percent
|
||||
}
|
||||
|
||||
/// Given a `LengthPercentage` value `v`, construct the value representing
|
||||
/// `calc(100% - v)`.
|
||||
pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
|
||||
// TODO: This could in theory take ownership of the calc node in `v` if
|
||||
// possible instead of cloning.
|
||||
let mut node = v.to_calc_node().into_owned();
|
||||
node.negate();
|
||||
|
||||
let new_node = CalcNode::Sum(
|
||||
vec![
|
||||
CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())),
|
||||
node,
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
|
||||
Self::new_calc(new_node, clamping_mode)
|
||||
}
|
||||
|
||||
/// Constructs a `calc()` value.
|
||||
#[inline]
|
||||
pub fn new_calc(
|
||||
length: Length,
|
||||
percentage: Option<Percentage>,
|
||||
clamping_mode: AllowedNumericType,
|
||||
) -> Self {
|
||||
let percentage = match percentage {
|
||||
Some(p) => p,
|
||||
None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))),
|
||||
};
|
||||
if length.is_zero() {
|
||||
return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0)));
|
||||
pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
|
||||
node.simplify_and_sort();
|
||||
|
||||
match node {
|
||||
CalcNode::Leaf(l) => {
|
||||
return match l {
|
||||
CalcLengthPercentageLeaf::Length(l) => {
|
||||
Self::new_length(Length::new(clamping_mode.clamp(l.px())))
|
||||
},
|
||||
CalcLengthPercentageLeaf::Percentage(p) => {
|
||||
Self::new_percent(Percentage(clamping_mode.clamp(p.0)))
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
node,
|
||||
})),
|
||||
}
|
||||
Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
|
||||
length,
|
||||
percentage,
|
||||
clamping_mode,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Private version of new_calc() that constructs a calc() variant without
|
||||
|
@ -262,7 +307,18 @@ impl LengthPercentage {
|
|||
LengthPercentageUnion::TAG_CALC => Tag::Calc,
|
||||
LengthPercentageUnion::TAG_LENGTH => Tag::Length,
|
||||
LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage,
|
||||
_ => unreachable!("Bogus tag?"),
|
||||
_ => unsafe { debug_unreachable!("Bogus tag?") },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> {
|
||||
unsafe {
|
||||
match self.tag() {
|
||||
Tag::Calc => UnpackedMut::Calc(&mut *self.calc_ptr()),
|
||||
Tag::Length => UnpackedMut::Length(self.0.length.length),
|
||||
Tag::Percentage => UnpackedMut::Percentage(self.0.percentage.percentage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,57 +369,7 @@ impl LengthPercentage {
|
|||
match self.unpack() {
|
||||
Unpacked::Length(l) => l.px() == 0.0,
|
||||
Unpacked::Percentage(p) => p.0 == 0.0,
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert_ne!(
|
||||
c.length.px(),
|
||||
0.0,
|
||||
"Should've been simplified to a percentage"
|
||||
);
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `<length>` component of this `calc()`, unclamped.
|
||||
#[inline]
|
||||
pub fn unclamped_length(&self) -> Length {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => l,
|
||||
Unpacked::Percentage(..) => Zero::zero(),
|
||||
Unpacked::Calc(c) => c.unclamped_length(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this `calc()` as a `<length>`.
|
||||
///
|
||||
/// Panics in debug mode if a percentage is present in the expression.
|
||||
#[inline]
|
||||
fn length(&self) -> Length {
|
||||
debug_assert!(!self.has_percentage());
|
||||
self.length_component()
|
||||
}
|
||||
|
||||
/// Returns the `<length>` component of this `calc()`, clamped.
|
||||
#[inline]
|
||||
pub fn length_component(&self) -> Length {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => l,
|
||||
Unpacked::Percentage(..) => Zero::zero(),
|
||||
Unpacked::Calc(c) => c.length_component(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `<percentage>` component of this `calc()`, unclamped, as a
|
||||
/// float.
|
||||
///
|
||||
/// FIXME: This are very different semantics from length(), we should
|
||||
/// probably rename this.
|
||||
#[inline]
|
||||
pub fn percentage(&self) -> CSSFloat {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => 0.,
|
||||
Unpacked::Percentage(p) => p.0,
|
||||
Unpacked::Calc(c) => c.percentage.0,
|
||||
Unpacked::Calc(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,25 +413,8 @@ impl LengthPercentage {
|
|||
#[inline]
|
||||
pub fn to_percentage(&self) -> Option<Percentage> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => None,
|
||||
Unpacked::Percentage(p) => Some(p),
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert!(!c.length.is_zero());
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the specified percentage if any.
|
||||
#[inline]
|
||||
pub fn specified_percentage(&self) -> Option<Percentage> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => None,
|
||||
Unpacked::Percentage(p) => Some(p),
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert!(self.has_percentage());
|
||||
Some(c.percentage)
|
||||
},
|
||||
Unpacked::Length(..) | Unpacked::Calc(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,19 +441,22 @@ impl LengthPercentage {
|
|||
/// the height property), they apply whenever a calc() expression contains
|
||||
/// percentages.
|
||||
pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
|
||||
if self.has_percentage() {
|
||||
return Some(self.resolve(container_len?));
|
||||
if let Unpacked::Length(l) = self.unpack() {
|
||||
return Some(l);
|
||||
}
|
||||
Some(self.length())
|
||||
Some(self.resolve(container_len?))
|
||||
}
|
||||
|
||||
/// Returns the clamped non-negative values.
|
||||
#[inline]
|
||||
pub fn clamp_to_non_negative(&self) -> Self {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => Self::new_length(l.clamp_to_non_negative()),
|
||||
Unpacked::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
|
||||
Unpacked::Calc(c) => c.clamp_to_non_negative(),
|
||||
pub fn clamp_to_non_negative(mut self) -> Self {
|
||||
match self.unpack_mut() {
|
||||
UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),
|
||||
UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
|
||||
UnpackedMut::Calc(ref mut c) => {
|
||||
c.clamping_mode = AllowedNumericType::NonNegative;
|
||||
self
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +541,7 @@ impl ToCss for LengthPercentage {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
||||
self.unpack().to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,46 +576,138 @@ impl<'de> Deserialize<'de> for LengthPercentage {
|
|||
}
|
||||
}
|
||||
|
||||
/// The 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.
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)]
|
||||
#[derive(
|
||||
Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct CalcLengthPercentage {
|
||||
length: Length,
|
||||
|
||||
percentage: Percentage,
|
||||
|
||||
#[animation(constant)]
|
||||
#[css(skip)]
|
||||
clamping_mode: AllowedNumericType,
|
||||
node: CalcNode,
|
||||
}
|
||||
|
||||
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.
|
||||
#[inline]
|
||||
pub fn resolve(&self, basis: Length) -> Length {
|
||||
let length = self.length.px() + basis.px() * self.percentage.0;
|
||||
Length::new(self.clamping_mode.clamp(length))
|
||||
}
|
||||
|
||||
/// Returns the length, without clamping.
|
||||
#[inline]
|
||||
fn unclamped_length(&self) -> Length {
|
||||
self.length
|
||||
}
|
||||
|
||||
/// Returns the clamped non-negative values.
|
||||
#[inline]
|
||||
fn clamp_to_non_negative(&self) -> LengthPercentage {
|
||||
LengthPercentage::new_calc(
|
||||
self.length,
|
||||
Some(self.percentage),
|
||||
AllowedNumericType::NonNegative,
|
||||
)
|
||||
fn resolve(&self, basis: Length) -> Length {
|
||||
// unwrap() is fine because the conversion below is infallible.
|
||||
let px = self
|
||||
.node
|
||||
.resolve(|l| {
|
||||
Ok(match *l {
|
||||
CalcLengthPercentageLeaf::Length(l) => l.px(),
|
||||
CalcLengthPercentageLeaf::Percentage(ref p) => basis.px() * p.0,
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
Length::new(self.clamping_mode.clamp(px))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,7 +725,7 @@ impl CalcLengthPercentage {
|
|||
// maybe.
|
||||
impl PartialEq for CalcLengthPercentage {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.length == other.length && self.percentage == other.percentage
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,43 +740,22 @@ impl specified::CalcLengthPercentage {
|
|||
where
|
||||
F: Fn(Length) -> Length,
|
||||
{
|
||||
use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength};
|
||||
use std::f32;
|
||||
use crate::values::specified::calc::Leaf;
|
||||
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 {
|
||||
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,
|
||||
)
|
||||
LengthPercentage::new_calc(node, self.clamping_mode)
|
||||
}
|
||||
|
||||
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
||||
|
@ -711,25 +774,14 @@ impl specified::CalcLengthPercentage {
|
|||
/// Compute the value into pixel length as CSSFloat without context,
|
||||
/// so it returns Err(()) if there is any non-absolute unit.
|
||||
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||
if self.vw.is_some() ||
|
||||
self.vh.is_some() ||
|
||||
self.vmin.is_some() ||
|
||||
self.vmax.is_some() ||
|
||||
self.em.is_some() ||
|
||||
self.ex.is_some() ||
|
||||
self.ch.is_some() ||
|
||||
self.rem.is_some() ||
|
||||
self.percentage.is_some()
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
use crate::values::specified::calc::Leaf;
|
||||
use crate::values::specified::length::NoCalcLength;
|
||||
|
||||
match self.absolute {
|
||||
Some(abs) => Ok(abs.to_px()),
|
||||
None => {
|
||||
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
||||
Err(())
|
||||
},
|
||||
// Simplification should've turned this into an absolute length,
|
||||
// otherwise it wouldn't have been able to.
|
||||
match self.node {
|
||||
calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,17 +792,51 @@ impl specified::CalcLengthPercentage {
|
|||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
|
||||
use crate::values::specified::length::AbsoluteLength;
|
||||
use crate::values::specified::calc::Leaf;
|
||||
use crate::values::specified::length::NoCalcLength;
|
||||
|
||||
specified::CalcLengthPercentage {
|
||||
clamping_mode: computed.clamping_mode,
|
||||
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
||||
percentage: Some(computed.percentage),
|
||||
..Default::default()
|
||||
node: computed.node.map_leaves(|l| match l {
|
||||
CalcLengthPercentageLeaf::Length(ref l) => {
|
||||
Leaf::Length(NoCalcLength::from_px(l.px()))
|
||||
},
|
||||
CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
/// https://drafts.csswg.org/css-values-4/#combine-math
|
||||
/// https://drafts.csswg.org/css-values-4/#combine-mixed
|
||||
impl Animate for LengthPercentage {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(match (self.unpack(), other.unpack()) {
|
||||
(Unpacked::Length(one), Unpacked::Length(other)) => {
|
||||
Self::new_length(one.animate(&other, procedure)?)
|
||||
},
|
||||
(Unpacked::Percentage(one), Unpacked::Percentage(other)) => {
|
||||
Self::new_percent(one.animate(&other, procedure)?)
|
||||
},
|
||||
_ => {
|
||||
let mut one = self.to_calc_node().into_owned();
|
||||
let mut other = other.to_calc_node().into_owned();
|
||||
let (l, r) = procedure.weights();
|
||||
|
||||
one.mul_by(l as f32);
|
||||
other.mul_by(r as f32);
|
||||
|
||||
Self::new_calc(
|
||||
CalcNode::Sum(vec![one, other].into()),
|
||||
AllowedNumericType::All,
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
||||
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
||||
|
||||
|
|
|
@ -21,10 +21,9 @@ use crate::media_queries::Device;
|
|||
use crate::properties;
|
||||
use crate::properties::{ComputedValues, LonghandId, StyleBuilder};
|
||||
use crate::rule_cache::RuleCacheConditions;
|
||||
use crate::Atom;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::Prefix;
|
||||
use crate::{ArcSlice, Atom};
|
||||
use euclid::default::Size2D;
|
||||
use servo_arc::Arc;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp;
|
||||
use std::f32;
|
||||
|
@ -57,7 +56,7 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
|
|||
pub use self::font::{FontVariantAlternates, FontWeight};
|
||||
pub use self::font::{FontVariantEastAsian, FontVariationSettings};
|
||||
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
|
||||
pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
|
||||
pub use self::image::{Gradient, Image, LineDirection, MozImageRect};
|
||||
pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
|
||||
pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
|
||||
pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
|
||||
|
@ -78,7 +77,7 @@ pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
|||
pub use self::text::TextUnderlinePosition;
|
||||
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
|
||||
pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing};
|
||||
pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle};
|
||||
pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
|
||||
pub use self::text::{TextDecorationLength, TextDecorationSkipInk};
|
||||
pub use self::time::Time;
|
||||
pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
|
||||
|
@ -450,6 +449,46 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE(emilio): This is implementable more generically, but it's unlikely
|
||||
// what you want there, as it forces you to have an extra allocation.
|
||||
//
|
||||
// We could do that if needed, ideally with specialization for the case where
|
||||
// ComputedValue = T. But we don't need it for now.
|
||||
impl<T> ToComputedValue for Arc<T>
|
||||
where
|
||||
T: ToComputedValue<ComputedValue = T>,
|
||||
{
|
||||
type ComputedValue = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, _: &Context) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self) -> Self {
|
||||
computed.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Same caveat as above applies.
|
||||
impl<T> ToComputedValue for ArcSlice<T>
|
||||
where
|
||||
T: ToComputedValue<ComputedValue = T>,
|
||||
{
|
||||
type ComputedValue = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, _: &Context) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self) -> Self {
|
||||
computed.clone()
|
||||
}
|
||||
}
|
||||
|
||||
trivial_to_computed_value!(());
|
||||
trivial_to_computed_value!(bool);
|
||||
trivial_to_computed_value!(f32);
|
||||
|
@ -460,10 +499,13 @@ trivial_to_computed_value!(u32);
|
|||
trivial_to_computed_value!(usize);
|
||||
trivial_to_computed_value!(Atom);
|
||||
#[cfg(feature = "servo")]
|
||||
trivial_to_computed_value!(Prefix);
|
||||
trivial_to_computed_value!(html5ever::Namespace);
|
||||
#[cfg(feature = "servo")]
|
||||
trivial_to_computed_value!(html5ever::Prefix);
|
||||
trivial_to_computed_value!(String);
|
||||
trivial_to_computed_value!(Box<str>);
|
||||
trivial_to_computed_value!(crate::OwnedStr);
|
||||
trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
use crate::values::computed::{Integer, LengthPercentage, Percentage};
|
||||
use crate::values::generics::position::Position as GenericPosition;
|
||||
use crate::values::generics::position::PositionComponent as GenericPositionComponent;
|
||||
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
|
||||
use crate::values::generics::position::ZIndex as GenericZIndex;
|
||||
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
|
||||
|
@ -56,5 +57,14 @@ impl ToCss for Position {
|
|||
}
|
||||
}
|
||||
|
||||
impl GenericPositionComponent for LengthPercentage {
|
||||
fn is_center(&self) -> bool {
|
||||
match self.to_percentage() {
|
||||
Some(Percentage(per)) => per == 0.5,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A computed value for the `z-index` property.
|
||||
pub type ZIndex = GenericZIndex<Integer>;
|
||||
|
|
|
@ -31,7 +31,7 @@ impl SVGPaint {
|
|||
}
|
||||
|
||||
/// <length> | <percentage> | <number> | context-value
|
||||
pub type SVGLength = generic::SVGLength<LengthPercentage>;
|
||||
pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
|
||||
|
||||
impl SVGLength {
|
||||
/// `0px`
|
||||
|
@ -41,7 +41,7 @@ impl SVGLength {
|
|||
}
|
||||
|
||||
/// An non-negative wrapper of SVGLength.
|
||||
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
|
||||
pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
|
||||
|
||||
impl SVGWidth {
|
||||
/// `1px`.
|
||||
|
|
|
@ -18,8 +18,7 @@ use crate::Zero;
|
|||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
pub use crate::values::specified::TextAlignKeyword as TextAlign;
|
||||
pub use crate::values::specified::TextUnderlinePosition;
|
||||
pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition};
|
||||
pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak};
|
||||
pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
|
||||
pub use crate::values::specified::{TextDecorationSkipInk, TextTransform};
|
||||
|
@ -30,6 +29,9 @@ pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
|
|||
/// Implements type for `text-decoration-thickness` property.
|
||||
pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
|
||||
|
||||
/// The computed value of `text-align`.
|
||||
pub type TextAlign = specified::TextAlignKeyword;
|
||||
|
||||
/// A computed value for the `letter-spacing` property.
|
||||
#[repr(transparent)]
|
||||
#[derive(
|
||||
|
|
|
@ -13,10 +13,10 @@ pub use crate::values::specified::ui::CursorKind;
|
|||
pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
|
||||
|
||||
/// A computed value for the `cursor` property.
|
||||
pub type Cursor = generics::Cursor<CursorImage>;
|
||||
pub type Cursor = generics::GenericCursor<CursorImage>;
|
||||
|
||||
/// A computed value for item of `image cursors`.
|
||||
pub type CursorImage = generics::CursorImage<ComputedImageUrl, Number>;
|
||||
pub type CursorImage = generics::GenericCursorImage<ComputedImageUrl, Number>;
|
||||
|
||||
/// A computed value for `scrollbar-color` property.
|
||||
pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;
|
||||
|
|
|
@ -19,9 +19,6 @@ use std::ops::Add;
|
|||
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
||||
/// `match` arm returns an error.
|
||||
///
|
||||
/// If the two values are not similar, an error is returned unless a fallback
|
||||
/// function has been specified through `#[distance(fallback)]`.
|
||||
///
|
||||
/// Trait bounds for type parameter `Foo` can be opted out of with
|
||||
/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
|
||||
/// fields can be opted into with `#[distance(field_bound)]` on the field.
|
||||
|
@ -81,6 +78,16 @@ impl ComputeSquaredDistance for Au {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> ComputeSquaredDistance for Box<T>
|
||||
where
|
||||
T: ComputeSquaredDistance,
|
||||
{
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
(**self).compute_squared_distance(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComputeSquaredDistance for Option<T>
|
||||
where
|
||||
T: ComputeSquaredDistance,
|
||||
|
|
|
@ -15,18 +15,17 @@ use crate::Zero;
|
|||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
/// A clipping shape, for `clip-path`.
|
||||
pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
|
||||
|
||||
/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
|
||||
#[allow(missing_docs)]
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Copy,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
Parse,
|
||||
SpecifiedValueInfo,
|
||||
ToAnimatedValue,
|
||||
ToComputedValue,
|
||||
|
@ -34,15 +33,27 @@ pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, U
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum GeometryBox {
|
||||
#[repr(u8)]
|
||||
pub enum ShapeGeometryBox {
|
||||
/// Depending on which kind of element this style value applied on, the
|
||||
/// default value of the reference-box can be different. For an HTML
|
||||
/// element, the default value of reference-box is border-box; for an SVG
|
||||
/// element, the default value is fill-box. Since we can not determine the
|
||||
/// default value at parsing time, we keep this value to make a decision on
|
||||
/// it.
|
||||
#[css(skip)]
|
||||
ElementDependent,
|
||||
FillBox,
|
||||
StrokeBox,
|
||||
ViewBox,
|
||||
ShapeBox(ShapeBox),
|
||||
}
|
||||
|
||||
/// A float area shape, for `shape-outside`.
|
||||
pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>;
|
||||
impl Default for ShapeGeometryBox {
|
||||
fn default() -> Self {
|
||||
Self::ElementDependent
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
|
||||
#[allow(missing_docs)]
|
||||
|
@ -51,6 +62,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I
|
|||
Animate,
|
||||
Clone,
|
||||
Copy,
|
||||
ComputeSquaredDistance,
|
||||
Debug,
|
||||
Eq,
|
||||
MallocSizeOf,
|
||||
|
@ -63,6 +75,7 @@ pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, I
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum ShapeBox {
|
||||
MarginBox,
|
||||
BorderBox,
|
||||
|
@ -70,12 +83,19 @@ pub enum ShapeBox {
|
|||
ContentBox,
|
||||
}
|
||||
|
||||
/// A shape source, for some reference box.
|
||||
impl Default for ShapeBox {
|
||||
fn default() -> Self {
|
||||
ShapeBox::MarginBox
|
||||
}
|
||||
}
|
||||
|
||||
/// A value for the `clip-path` property.
|
||||
#[allow(missing_docs)]
|
||||
#[animation(no_bound(ImageOrUrl))]
|
||||
#[animation(no_bound(U))]
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
|
@ -86,18 +106,54 @@ pub enum ShapeBox {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
|
||||
#[animation(error)]
|
||||
ImageOrUrl(ImageOrUrl),
|
||||
Shape(Box<BasicShape>, Option<ReferenceBox>),
|
||||
#[animation(error)]
|
||||
Box(ReferenceBox),
|
||||
#[css(function)]
|
||||
Path(Path),
|
||||
#[repr(u8)]
|
||||
pub enum GenericClipPath<BasicShape, U> {
|
||||
#[animation(error)]
|
||||
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)]
|
||||
#[derive(
|
||||
Animate,
|
||||
|
@ -252,7 +308,7 @@ pub use self::GenericShapeRadius as ShapeRadius;
|
|||
#[repr(C)]
|
||||
pub struct GenericPolygon<LengthPercentage> {
|
||||
/// The filling rule for a polygon.
|
||||
#[css(skip_if = "fill_is_default")]
|
||||
#[css(skip_if = "is_default")]
|
||||
pub fill: FillRule,
|
||||
/// A collection of (x, y) coordinates to draw the polygon.
|
||||
#[css(iterable)]
|
||||
|
@ -311,6 +367,7 @@ pub enum FillRule {
|
|||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
|
@ -321,39 +378,23 @@ pub enum FillRule {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct Path {
|
||||
/// The filling rule for the svg path.
|
||||
#[css(skip_if = "fill_is_default")]
|
||||
#[css(skip_if = "is_default")]
|
||||
#[animation(constant)]
|
||||
pub fill: FillRule,
|
||||
/// The svg path data.
|
||||
pub path: SVGPathData,
|
||||
}
|
||||
|
||||
// FIXME(nox): Implement ComputeSquaredDistance for T types and stop
|
||||
// using PartialEq here, this will let us derive this impl.
|
||||
impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U>
|
||||
where
|
||||
B: ComputeSquaredDistance,
|
||||
T: PartialEq,
|
||||
{
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
match (self, other) {
|
||||
(
|
||||
&ShapeSource::Shape(ref this, ref this_box),
|
||||
&ShapeSource::Shape(ref other, ref other_box),
|
||||
) if this_box == other_box => this.compute_squared_distance(other),
|
||||
(&ShapeSource::Path(ref this), &ShapeSource::Path(ref other))
|
||||
if this.fill == other.fill =>
|
||||
{
|
||||
this.path.compute_squared_distance(&other.path)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
impl<B, U> ToAnimatedZero for ClipPath<B, U> {
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T, U> ToAnimatedZero for ShapeSource<B, T, U> {
|
||||
impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
|
@ -488,6 +529,6 @@ impl Default for FillRule {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn fill_is_default(fill: &FillRule) -> bool {
|
||||
*fill == FillRule::default()
|
||||
fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
|
||||
*fill == Default::default()
|
||||
}
|
||||
|
|
573
components/style/values/generics/calc.rs
Normal file
573
components/style/values/generics/calc.rs
Normal 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(¢er) {
|
||||
Some(o) => o,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// So if we can prove that min is more than center, then we won,
|
||||
// as that's what we should always return.
|
||||
if matches!(min_cmp_center, cmp::Ordering::Greater) {
|
||||
return replace_self_with!(&mut **min);
|
||||
}
|
||||
|
||||
// Otherwise try with max.
|
||||
let max_cmp_center = match max.partial_cmp(¢er) {
|
||||
Some(o) => o,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if matches!(max_cmp_center, cmp::Ordering::Less) {
|
||||
// max is less than center, so we need to return effectively
|
||||
// `max(min, max)`.
|
||||
let max_cmp_min = match max.partial_cmp(&min) {
|
||||
Some(o) => o,
|
||||
None => {
|
||||
debug_assert!(
|
||||
false,
|
||||
"We compared center with min and max, how are \
|
||||
min / max not comparable with each other?"
|
||||
);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if matches!(max_cmp_min, cmp::Ordering::Less) {
|
||||
return replace_self_with!(&mut **min);
|
||||
}
|
||||
|
||||
return replace_self_with!(&mut **max);
|
||||
}
|
||||
|
||||
// Otherwise we're the center node.
|
||||
return replace_self_with!(&mut **center);
|
||||
},
|
||||
Self::MinMax(ref mut children, op) => {
|
||||
let winning_order = match op {
|
||||
MinMaxOp::Min => cmp::Ordering::Less,
|
||||
MinMaxOp::Max => cmp::Ordering::Greater,
|
||||
};
|
||||
|
||||
let mut result = 0;
|
||||
for i in 1..children.len() {
|
||||
let o = match children[i].partial_cmp(&children[result]) {
|
||||
// We can't compare all the children, so we can't
|
||||
// know which one will actually win. Bail out and
|
||||
// keep ourselves as a min / max function.
|
||||
//
|
||||
// TODO: Maybe we could simplify compatible children,
|
||||
// see https://github.com/w3c/csswg-drafts/issues/4756
|
||||
None => return,
|
||||
Some(o) => o,
|
||||
};
|
||||
|
||||
if o == winning_order {
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
|
||||
replace_self_with!(&mut children[result]);
|
||||
},
|
||||
Self::Sum(ref mut children_slot) => {
|
||||
let mut sums_to_merge = SmallVec::<[_; 3]>::new();
|
||||
let mut extra_kids = 0;
|
||||
for (i, child) in children_slot.iter().enumerate() {
|
||||
if let Self::Sum(ref children) = *child {
|
||||
extra_kids += children.len();
|
||||
sums_to_merge.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// If we only have one kid, we've already simplified it, and it
|
||||
// doesn't really matter whether it's a sum already or not, so
|
||||
// lift it up and continue.
|
||||
if children_slot.len() == 1 {
|
||||
return replace_self_with!(&mut children_slot[0]);
|
||||
}
|
||||
|
||||
let mut children = mem::replace(children_slot, Default::default()).into_vec();
|
||||
|
||||
if !sums_to_merge.is_empty() {
|
||||
children.reserve(extra_kids - sums_to_merge.len());
|
||||
// Merge all our nested sums, in reverse order so that the
|
||||
// list indices are not invalidated.
|
||||
for i in sums_to_merge.drain(..).rev() {
|
||||
let kid_children = match children.swap_remove(i) {
|
||||
Self::Sum(c) => c,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// This would be nicer with
|
||||
// https://github.com/rust-lang/rust/issues/59878 fixed.
|
||||
children.extend(kid_children.into_vec());
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(children.len() >= 2, "Should still have multiple kids!");
|
||||
|
||||
// Sort by spec order.
|
||||
children.sort_unstable_by_key(|c| c.sort_key());
|
||||
|
||||
// NOTE: if the function returns true, by the docs of dedup_by,
|
||||
// a is removed.
|
||||
children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());
|
||||
|
||||
if children.len() == 1 {
|
||||
// If only one children remains, lift it up, and carry on.
|
||||
replace_self_with!(&mut children[0]);
|
||||
} else {
|
||||
// Else put our simplified children back.
|
||||
mem::replace(children_slot, children.into_boxed_slice().into());
|
||||
}
|
||||
},
|
||||
Self::Leaf(ref mut l) => {
|
||||
l.simplify();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplifies and sorts the kids in the whole calculation subtree.
|
||||
pub fn simplify_and_sort(&mut self) {
|
||||
self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
|
||||
}
|
||||
|
||||
fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let write_closing_paren = match *self {
|
||||
Self::MinMax(_, op) => {
|
||||
dest.write_str(match op {
|
||||
MinMaxOp::Max => "max(",
|
||||
MinMaxOp::Min => "min(",
|
||||
})?;
|
||||
true
|
||||
},
|
||||
Self::Clamp { .. } => {
|
||||
dest.write_str("clamp(")?;
|
||||
true
|
||||
},
|
||||
_ => {
|
||||
if is_outermost {
|
||||
dest.write_str("calc(")?;
|
||||
}
|
||||
is_outermost
|
||||
},
|
||||
};
|
||||
|
||||
match *self {
|
||||
Self::MinMax(ref children, _) => {
|
||||
let mut first = true;
|
||||
for child in &**children {
|
||||
if !first {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
first = false;
|
||||
child.to_css_impl(dest, false)?;
|
||||
}
|
||||
},
|
||||
Self::Sum(ref children) => {
|
||||
let mut first = true;
|
||||
for child in &**children {
|
||||
if !first {
|
||||
if child.is_negative_leaf() {
|
||||
dest.write_str(" - ")?;
|
||||
let mut c = child.clone();
|
||||
c.negate();
|
||||
c.to_css_impl(dest, false)?;
|
||||
} else {
|
||||
dest.write_str(" + ")?;
|
||||
child.to_css_impl(dest, false)?;
|
||||
}
|
||||
} else {
|
||||
first = false;
|
||||
child.to_css_impl(dest, false)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
Self::Clamp {
|
||||
ref min,
|
||||
ref center,
|
||||
ref max,
|
||||
} => {
|
||||
min.to_css_impl(dest, false)?;
|
||||
dest.write_str(", ")?;
|
||||
center.to_css_impl(dest, false)?;
|
||||
dest.write_str(", ")?;
|
||||
max.to_css_impl(dest, false)?;
|
||||
},
|
||||
Self::Leaf(ref l) => l.to_css(dest)?,
|
||||
}
|
||||
|
||||
if write_closing_paren {
|
||||
dest.write_str(")")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: CalcNodeLeaf> PartialOrd for CalcNode<L> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => one.partial_cmp(other),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: CalcNodeLeaf> ToCss for CalcNode<L> {
|
||||
/// <https://drafts.csswg.org/css-values/#calc-serialize>
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
self.to_css_impl(dest, /* is_outermost = */ true)
|
||||
}
|
||||
}
|
|
@ -655,8 +655,10 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
|
|||
pub struct LineNameList {
|
||||
/// The optional `<line-name-list>`
|
||||
pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
|
||||
/// Indicates the line name that requires `auto-fill`, if in bounds.
|
||||
pub fill_idx: usize,
|
||||
/// Indicates the starting line names that requires `auto-fill`, if in bounds.
|
||||
pub fill_start: usize,
|
||||
/// Indicates the number of line names in the auto-fill
|
||||
pub fill_len: usize,
|
||||
}
|
||||
|
||||
impl Parse for LineNameList {
|
||||
|
@ -666,7 +668,7 @@ impl Parse for LineNameList {
|
|||
) -> Result<Self, ParseError<'i>> {
|
||||
input.expect_ident_matching("subgrid")?;
|
||||
let mut line_names = vec![];
|
||||
let mut fill_idx = None;
|
||||
let mut fill_data = None;
|
||||
|
||||
loop {
|
||||
let repeat_parse_result = input.try(|input| {
|
||||
|
@ -682,8 +684,7 @@ impl Parse for LineNameList {
|
|||
Ok((names_list, count))
|
||||
})
|
||||
});
|
||||
|
||||
if let Ok((mut names_list, count)) = repeat_parse_result {
|
||||
if let Ok((names_list, count)) = repeat_parse_result {
|
||||
match count {
|
||||
// FIXME(emilio): we shouldn't expand repeat() at
|
||||
// parse time for subgrid. (bug 1583429)
|
||||
|
@ -694,19 +695,11 @@ impl Parse for LineNameList {
|
|||
.cycle()
|
||||
.take(num.value() as usize * names_list.len()),
|
||||
),
|
||||
RepeatCount::AutoFill if fill_idx.is_none() => {
|
||||
// `repeat(autof-fill, ..)` should have just one line name.
|
||||
// FIXME(bug 1341507) the above comment is wrong per:
|
||||
// https://drafts.csswg.org/css-grid-2/#typedef-name-repeat
|
||||
if names_list.len() != 1 {
|
||||
return Err(
|
||||
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
);
|
||||
}
|
||||
let names = names_list.pop().unwrap();
|
||||
|
||||
line_names.push(names);
|
||||
fill_idx = Some(line_names.len() - 1);
|
||||
RepeatCount::AutoFill if fill_data.is_none() => {
|
||||
let fill_idx = line_names.len();
|
||||
let fill_len = names_list.len();
|
||||
fill_data = Some((fill_idx, fill_len));
|
||||
line_names.extend(names_list.into_iter());
|
||||
},
|
||||
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
|
@ -721,9 +714,12 @@ impl Parse for LineNameList {
|
|||
line_names.truncate(MAX_GRID_LINE as usize);
|
||||
}
|
||||
|
||||
let (fill_start, fill_len) = fill_data.unwrap_or((usize::MAX, 0));
|
||||
|
||||
Ok(LineNameList {
|
||||
names: line_names.into(),
|
||||
fill_idx: fill_idx.unwrap_or(usize::MAX),
|
||||
fill_start: fill_start,
|
||||
fill_len: fill_len,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -734,9 +730,10 @@ impl ToCss for LineNameList {
|
|||
W: Write,
|
||||
{
|
||||
dest.write_str("subgrid")?;
|
||||
let fill_idx = self.fill_idx;
|
||||
let fill_start = self.fill_start;
|
||||
let fill_len = self.fill_len;
|
||||
for (i, names) in self.names.iter().enumerate() {
|
||||
if i == fill_idx {
|
||||
if i == fill_start {
|
||||
dest.write_str(" repeat(auto-fill,")?;
|
||||
}
|
||||
|
||||
|
@ -751,7 +748,7 @@ impl ToCss for LineNameList {
|
|||
}
|
||||
|
||||
dest.write_str("]")?;
|
||||
if i == fill_idx {
|
||||
if i == fill_start + fill_len - 1 {
|
||||
dest.write_str(")")?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,55 +9,28 @@
|
|||
use crate::custom_properties;
|
||||
use crate::values::serialize_atom_identifier;
|
||||
use crate::Atom;
|
||||
use crate::Zero;
|
||||
use servo_arc::Arc;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
use values::generics::position::PositionComponent;
|
||||
|
||||
/// An <image> | <none> (for background-image, for example).
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
Parse,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum GenericImageLayer<Image> {
|
||||
/// The `none` value.
|
||||
None,
|
||||
/// The `<image>` value.
|
||||
Image(Image),
|
||||
}
|
||||
|
||||
pub use self::GenericImageLayer as ImageLayer;
|
||||
|
||||
impl<I> ImageLayer<I> {
|
||||
/// Returns `none`.
|
||||
#[inline]
|
||||
pub fn none() -> Self {
|
||||
ImageLayer::None
|
||||
}
|
||||
}
|
||||
|
||||
/// An [image].
|
||||
/// An `<image> | none` value.
|
||||
///
|
||||
/// [image]: https://drafts.csswg.org/css-images/#image-values
|
||||
/// https://drafts.csswg.org/css-images/#image-values
|
||||
#[derive(
|
||||
Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericImage<Gradient, MozImageRect, ImageUrl> {
|
||||
pub enum GenericImage<G, MozImageRect, ImageUrl> {
|
||||
/// `none` variant.
|
||||
None,
|
||||
/// A `<url()>` image.
|
||||
Url(ImageUrl),
|
||||
|
||||
/// A `<gradient>` image. Gradients are rather large, and not nearly as
|
||||
/// common as urls, so we box them here to keep the size of this enum sane.
|
||||
Gradient(Box<Gradient>),
|
||||
|
||||
Gradient(Box<G>),
|
||||
/// A `-moz-image-rect` image. Also fairly large and rare.
|
||||
// not cfg’ed out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used`
|
||||
// Instead we make MozImageRect an empty enum
|
||||
|
@ -80,27 +53,51 @@ pub use self::GenericImage as Image;
|
|||
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
#[repr(C)]
|
||||
pub struct GenericGradient<
|
||||
pub enum GenericGradient<
|
||||
LineDirection,
|
||||
LengthPercentage,
|
||||
NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
Position,
|
||||
Angle,
|
||||
AngleOrPercentage,
|
||||
Color,
|
||||
> {
|
||||
/// Gradients can be linear or radial.
|
||||
pub kind: GenericGradientKind<
|
||||
LineDirection,
|
||||
NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
Position,
|
||||
>,
|
||||
/// The color stops and interpolation hints.
|
||||
pub items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
||||
/// True if this is a repeating gradient.
|
||||
pub repeating: bool,
|
||||
/// Compatibility mode.
|
||||
pub compat_mode: GradientCompatMode,
|
||||
/// A linear gradient.
|
||||
Linear {
|
||||
/// Line direction
|
||||
direction: LineDirection,
|
||||
/// The color stops and interpolation hints.
|
||||
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
||||
/// True if this is a repeating gradient.
|
||||
repeating: bool,
|
||||
/// Compatibility mode.
|
||||
compat_mode: GradientCompatMode,
|
||||
},
|
||||
/// A radial gradient.
|
||||
Radial {
|
||||
/// Shape of gradient
|
||||
shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
|
||||
/// Center of gradient
|
||||
position: Position,
|
||||
/// The color stops and interpolation hints.
|
||||
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
|
||||
/// True if this is a repeating gradient.
|
||||
repeating: bool,
|
||||
/// Compatibility mode.
|
||||
compat_mode: GradientCompatMode,
|
||||
},
|
||||
/// A conic gradient.
|
||||
Conic {
|
||||
/// Start angle of gradient
|
||||
angle: Angle,
|
||||
/// Center of gradient
|
||||
position: Position,
|
||||
/// The color stops and interpolation hints.
|
||||
items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
|
||||
/// True if this is a repeating gradient.
|
||||
repeating: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub use self::GenericGradient as Gradient;
|
||||
|
@ -117,26 +114,6 @@ pub enum GradientCompatMode {
|
|||
Moz,
|
||||
}
|
||||
|
||||
/// A gradient kind.
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericGradientKind<
|
||||
LineDirection,
|
||||
NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
Position,
|
||||
> {
|
||||
/// A linear gradient.
|
||||
Linear(LineDirection),
|
||||
/// A radial gradient.
|
||||
Radial(
|
||||
GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
|
||||
Position,
|
||||
),
|
||||
}
|
||||
|
||||
pub use self::GenericGradientKind as GradientKind;
|
||||
|
||||
/// A radial gradient's ending shape.
|
||||
#[derive(
|
||||
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
|
||||
|
@ -209,7 +186,7 @@ pub enum ShapeExtent {
|
|||
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericGradientItem<Color, LengthPercentage> {
|
||||
pub enum GenericGradientItem<Color, T> {
|
||||
/// A simple color stop, without position.
|
||||
SimpleColorStop(Color),
|
||||
/// A complex color stop, with a position.
|
||||
|
@ -217,10 +194,10 @@ pub enum GenericGradientItem<Color, LengthPercentage> {
|
|||
/// The color for the stop.
|
||||
color: Color,
|
||||
/// The position for the stop.
|
||||
position: LengthPercentage,
|
||||
position: T,
|
||||
},
|
||||
/// An interpolation hint.
|
||||
InterpolationHint(LengthPercentage),
|
||||
InterpolationHint(T),
|
||||
}
|
||||
|
||||
pub use self::GenericGradientItem as GradientItem;
|
||||
|
@ -230,17 +207,17 @@ pub use self::GenericGradientItem as GradientItem;
|
|||
#[derive(
|
||||
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
pub struct ColorStop<Color, LengthPercentage> {
|
||||
pub struct ColorStop<Color, T> {
|
||||
/// The color of this stop.
|
||||
pub color: Color,
|
||||
/// The position of this stop.
|
||||
pub position: Option<LengthPercentage>,
|
||||
pub position: Option<T>,
|
||||
}
|
||||
|
||||
impl<Color, LengthPercentage> ColorStop<Color, LengthPercentage> {
|
||||
impl<Color, T> ColorStop<Color, T> {
|
||||
/// Convert the color stop into an appropriate `GradientItem`.
|
||||
#[inline]
|
||||
pub fn into_item(self) -> GradientItem<Color, LengthPercentage> {
|
||||
pub fn into_item(self) -> GradientItem<Color, T> {
|
||||
match self.position {
|
||||
Some(position) => GradientItem::ComplexColorStop {
|
||||
color: self.color,
|
||||
|
@ -261,6 +238,8 @@ pub struct PaintWorklet {
|
|||
/// The arguments for the worklet.
|
||||
/// TODO: store a parsed representation of the arguments.
|
||||
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
|
||||
#[compute(no_field_bound)]
|
||||
#[resolve(no_field_bound)]
|
||||
pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
|
||||
}
|
||||
|
||||
|
@ -285,7 +264,7 @@ impl ToCss for PaintWorklet {
|
|||
///
|
||||
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
|
||||
#[allow(missing_docs)]
|
||||
#[css(comma, function)]
|
||||
#[css(comma, function = "-moz-image-rect")]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
@ -297,7 +276,8 @@ impl ToCss for PaintWorklet {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
|
||||
#[repr(C)]
|
||||
pub struct GenericMozImageRect<NumberOrPercentage, MozImageRectUrl> {
|
||||
pub url: MozImageRectUrl,
|
||||
pub top: NumberOrPercentage,
|
||||
pub right: NumberOrPercentage,
|
||||
|
@ -305,6 +285,8 @@ pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
|
|||
pub left: NumberOrPercentage,
|
||||
}
|
||||
|
||||
pub use self::GenericMozImageRect as MozImageRect;
|
||||
|
||||
impl<G, R, U> fmt::Debug for Image<G, R, U>
|
||||
where
|
||||
G: ToCss,
|
||||
|
@ -327,6 +309,7 @@ where
|
|||
W: Write,
|
||||
{
|
||||
match *self {
|
||||
Image::None => dest.write_str("none"),
|
||||
Image::Url(ref url) => url.to_css(dest),
|
||||
Image::Gradient(ref gradient) => gradient.to_css(dest),
|
||||
Image::Rect(ref rect) => rect.to_css(dest),
|
||||
|
@ -342,81 +325,146 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, LP, NL, NLP, P, C> ToCss for Gradient<D, LP, NL, NLP, P, C>
|
||||
impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
|
||||
where
|
||||
D: LineDirection,
|
||||
LP: ToCss,
|
||||
NL: ToCss,
|
||||
NLP: ToCss,
|
||||
P: ToCss,
|
||||
P: PositionComponent + ToCss,
|
||||
A: ToCss,
|
||||
AoP: ToCss,
|
||||
C: ToCss,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
match self.compat_mode {
|
||||
let (compat_mode, repeating) = match *self {
|
||||
Gradient::Linear {
|
||||
compat_mode,
|
||||
repeating,
|
||||
..
|
||||
} => (compat_mode, repeating),
|
||||
Gradient::Radial {
|
||||
compat_mode,
|
||||
repeating,
|
||||
..
|
||||
} => (compat_mode, repeating),
|
||||
Gradient::Conic { repeating, .. } => (GradientCompatMode::Modern, repeating),
|
||||
};
|
||||
|
||||
match compat_mode {
|
||||
GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
|
||||
GradientCompatMode::Moz => dest.write_str("-moz-")?,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
if self.repeating {
|
||||
if repeating {
|
||||
dest.write_str("repeating-")?;
|
||||
}
|
||||
dest.write_str(self.kind.label())?;
|
||||
dest.write_str("-gradient(")?;
|
||||
let mut skip_comma = match self.kind {
|
||||
GradientKind::Linear(ref direction) if direction.points_downwards(self.compat_mode) => {
|
||||
true
|
||||
|
||||
match *self {
|
||||
Gradient::Linear {
|
||||
ref direction,
|
||||
ref items,
|
||||
compat_mode,
|
||||
..
|
||||
} => {
|
||||
dest.write_str("linear-gradient(")?;
|
||||
let mut skip_comma = if !direction.points_downwards(compat_mode) {
|
||||
direction.to_css(dest, compat_mode)?;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
for item in &**items {
|
||||
if !skip_comma {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
skip_comma = false;
|
||||
item.to_css(dest)?;
|
||||
}
|
||||
},
|
||||
GradientKind::Linear(ref direction) => {
|
||||
direction.to_css(dest, self.compat_mode)?;
|
||||
false
|
||||
},
|
||||
GradientKind::Radial(ref shape, ref position) => {
|
||||
Gradient::Radial {
|
||||
ref shape,
|
||||
ref position,
|
||||
ref items,
|
||||
compat_mode,
|
||||
..
|
||||
} => {
|
||||
dest.write_str("radial-gradient(")?;
|
||||
let omit_shape = match *shape {
|
||||
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
|
||||
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
|
||||
_ => false,
|
||||
};
|
||||
if self.compat_mode == GradientCompatMode::Modern {
|
||||
let omit_position = position.is_center();
|
||||
if compat_mode == GradientCompatMode::Modern {
|
||||
if !omit_shape {
|
||||
shape.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
if !omit_position {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
}
|
||||
if !omit_position {
|
||||
dest.write_str("at ")?;
|
||||
position.to_css(dest)?;
|
||||
}
|
||||
dest.write_str("at ")?;
|
||||
position.to_css(dest)?;
|
||||
} else {
|
||||
position.to_css(dest)?;
|
||||
if !omit_position {
|
||||
position.to_css(dest)?;
|
||||
if !omit_shape {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
if !omit_shape {
|
||||
dest.write_str(", ")?;
|
||||
shape.to_css(dest)?;
|
||||
}
|
||||
}
|
||||
false
|
||||
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)?;
|
||||
}
|
||||
},
|
||||
};
|
||||
for item in &*self.items {
|
||||
if !skip_comma {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
skip_comma = false;
|
||||
item.to_css(dest)?;
|
||||
}
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
|
||||
fn label(&self) -> &str {
|
||||
match *self {
|
||||
GradientKind::Linear(..) => "linear",
|
||||
GradientKind::Radial(..) => "radial",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The direction of a linear gradient.
|
||||
pub trait LineDirection {
|
||||
/// Whether this direction points towards, and thus can be omitted.
|
||||
|
|
|
@ -19,6 +19,7 @@ pub mod basic_shape;
|
|||
pub mod border;
|
||||
#[path = "box.rs"]
|
||||
pub mod box_;
|
||||
pub mod calc;
|
||||
pub mod color;
|
||||
pub mod column;
|
||||
pub mod counters;
|
||||
|
|
|
@ -31,6 +31,17 @@ pub struct GenericPosition<H, V> {
|
|||
pub vertical: V,
|
||||
}
|
||||
|
||||
impl<H, V> PositionComponent for Position<H, V>
|
||||
where
|
||||
H: PositionComponent,
|
||||
V: PositionComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn is_center(&self) -> bool {
|
||||
self.horizontal.is_center() && self.vertical.is_center()
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::GenericPosition as Position;
|
||||
|
||||
impl<H, V> Position<H, V> {
|
||||
|
@ -43,6 +54,13 @@ impl<H, V> Position<H, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Implements a method that checks if the position is centered.
|
||||
pub trait PositionComponent {
|
||||
/// Returns if the position component is 50% or center.
|
||||
/// For pixel lengths, it always returns false.
|
||||
fn is_center(&self) -> bool;
|
||||
}
|
||||
|
||||
/// A generic type for representing an `Auto | <position>`.
|
||||
/// This is used by <offset-anchor> for now.
|
||||
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
|
||||
|
|
|
@ -152,7 +152,8 @@ impl<C: Parse, U: Parse> Parse for SVGPaint<C, U> {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum SVGLength<L> {
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericSVGLength<L> {
|
||||
/// `<length> | <percentage> | <number>`
|
||||
LengthPercentage(L),
|
||||
/// `context-value`
|
||||
|
@ -160,6 +161,8 @@ pub enum SVGLength<L> {
|
|||
ContextValue,
|
||||
}
|
||||
|
||||
pub use self::GenericSVGLength as SVGLength;
|
||||
|
||||
/// Generic value for stroke-dasharray.
|
||||
#[derive(
|
||||
Clone,
|
||||
|
|
|
@ -354,7 +354,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage {
|
|||
match *self {
|
||||
Length(len) => len.to_computed_pixel_length_without_context(),
|
||||
Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
|
||||
_ => Err(()),
|
||||
Percentage(..) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,19 +21,22 @@ use values::specified::ui::CursorKind;
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub struct Cursor<Image> {
|
||||
#[repr(C)]
|
||||
pub struct GenericCursor<Image> {
|
||||
/// The parsed images for the cursor.
|
||||
pub images: Box<[Image]>,
|
||||
pub images: crate::OwnedSlice<Image>,
|
||||
/// The kind of the cursor [default | help | ...].
|
||||
pub keyword: CursorKind,
|
||||
}
|
||||
|
||||
pub use self::GenericCursor as Cursor;
|
||||
|
||||
impl<Image> Cursor<Image> {
|
||||
/// Set `cursor` to `auto`
|
||||
#[inline]
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
images: vec![].into_boxed_slice(),
|
||||
images: Default::default(),
|
||||
keyword: CursorKind::Auto,
|
||||
}
|
||||
}
|
||||
|
@ -63,24 +66,31 @@ impl<Image: ToCss> ToCss for Cursor<Image> {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub struct CursorImage<ImageUrl, Number> {
|
||||
#[repr(C)]
|
||||
pub struct GenericCursorImage<ImageUrl, Number> {
|
||||
/// The url to parse images from.
|
||||
pub url: ImageUrl,
|
||||
/// The <x> and <y> coordinates.
|
||||
pub hotspot: Option<(Number, Number)>,
|
||||
/// Whether the image has a hotspot or not.
|
||||
pub has_hotspot: bool,
|
||||
/// The x coordinate.
|
||||
pub hotspot_x: Number,
|
||||
/// The y coordinate.
|
||||
pub hotspot_y: Number,
|
||||
}
|
||||
|
||||
pub use self::GenericCursorImage as CursorImage;
|
||||
|
||||
impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
self.url.to_css(dest)?;
|
||||
if let Some((ref x, ref y)) = self.hotspot {
|
||||
if self.has_hotspot {
|
||||
dest.write_str(" ")?;
|
||||
x.to_css(dest)?;
|
||||
self.hotspot_x.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
y.to_css(dest)?;
|
||||
self.hotspot_y.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
//! there are used values.
|
||||
|
||||
use crate::properties::ComputedValues;
|
||||
use crate::ArcSlice;
|
||||
use cssparser;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
mod color;
|
||||
|
@ -77,8 +79,11 @@ trivial_to_resolved_value!(computed::url::ComputedUrl);
|
|||
#[cfg(feature = "gecko")]
|
||||
trivial_to_resolved_value!(computed::url::ComputedImageUrl);
|
||||
#[cfg(feature = "servo")]
|
||||
trivial_to_resolved_value!(html5ever::Namespace);
|
||||
#[cfg(feature = "servo")]
|
||||
trivial_to_resolved_value!(html5ever::Prefix);
|
||||
trivial_to_resolved_value!(computed::LengthPercentage);
|
||||
trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType);
|
||||
|
||||
impl<A, B> ToResolvedValue for (A, B)
|
||||
where
|
||||
|
@ -214,3 +219,43 @@ where
|
|||
Self::from(Box::from_resolved_value(resolved.into_box()))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(emilio): This is implementable more generically, but it's unlikely what
|
||||
// you want there, as it forces you to have an extra allocation.
|
||||
//
|
||||
// We could do that if needed, ideally with specialization for the case where
|
||||
// ResolvedValue = T. But we don't need it for now.
|
||||
impl<T> ToResolvedValue for Arc<T>
|
||||
where
|
||||
T: ToResolvedValue<ResolvedValue = T>,
|
||||
{
|
||||
type ResolvedValue = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_resolved_value(self, _: &Context) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_resolved_value(resolved: Self) -> Self {
|
||||
resolved
|
||||
}
|
||||
}
|
||||
|
||||
// Same caveat as above applies.
|
||||
impl<T> ToResolvedValue for ArcSlice<T>
|
||||
where
|
||||
T: ToResolvedValue<ResolvedValue = T>,
|
||||
{
|
||||
type ResolvedValue = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_resolved_value(self, _: &Context) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_resolved_value(resolved: Self) -> Self {
|
||||
resolved
|
||||
}
|
||||
}
|
||||
|
|
|
@ -556,7 +556,7 @@ impl SpecifiedValueInfo for AlignItems {
|
|||
/// Value of the `justify-items` property
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-align/#justify-items-property>
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
|
||||
#[repr(C)]
|
||||
pub struct JustifyItems(pub AlignFlags);
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
use crate::values::generics::basic_shape as generic;
|
||||
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
|
||||
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
|
||||
use crate::values::generics::basic_shape::{Path, PolygonCoord};
|
||||
use crate::values::generics::rect::Rect;
|
||||
use crate::values::specified::border::BorderRadius;
|
||||
use crate::values::specified::image::Image;
|
||||
|
@ -25,14 +24,14 @@ use style_traits::{ParseError, StyleParseErrorKind};
|
|||
/// A specified alias for FillRule.
|
||||
pub use crate::values::generics::basic_shape::FillRule;
|
||||
|
||||
/// A specified clipping shape.
|
||||
pub type ClippingShape = generic::ClippingShape<BasicShape, SpecifiedUrl>;
|
||||
/// A specified `clip-path` value.
|
||||
pub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;
|
||||
|
||||
/// A specified float area shape.
|
||||
pub type FloatAreaShape = generic::FloatAreaShape<BasicShape, Image>;
|
||||
/// A specified `shape-outside` value.
|
||||
pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
|
||||
|
||||
/// A specified basic shape.
|
||||
pub type BasicShape = generic::BasicShape<
|
||||
pub type BasicShape = generic::GenericBasicShape<
|
||||
HorizontalPosition,
|
||||
VerticalPosition,
|
||||
LengthPercentage,
|
||||
|
@ -65,99 +64,90 @@ fn is_clip_path_path_enabled(_: &ParserContext) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
impl Parse for ClippingShape {
|
||||
#[inline]
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if is_clip_path_path_enabled(context) {
|
||||
if let Ok(p) = input.try(|i| Path::parse(context, i)) {
|
||||
return Ok(ShapeSource::Path(p));
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
/// A helper for both clip-path and shape-outside parsing of shapes.
|
||||
fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R,
|
||||
to_reference_box: impl FnOnce(ReferenceBox) -> R,
|
||||
) -> Result<R, ParseError<'i>>
|
||||
where
|
||||
ReferenceBox: Parse,
|
||||
ReferenceBox: Default + Parse,
|
||||
{
|
||||
/// The internal parser for ShapeSource.
|
||||
fn parse_common<'i, 't>(
|
||||
fn parse_component<U: Parse>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser,
|
||||
component: &mut Option<U>,
|
||||
) -> bool {
|
||||
if component.is_some() {
|
||||
return false; // already parsed this component
|
||||
}
|
||||
|
||||
*component = input.try(|i| U::parse(context, i)).ok();
|
||||
component.is_some()
|
||||
}
|
||||
|
||||
let mut shape = None;
|
||||
let mut ref_box = None;
|
||||
|
||||
while parse_component(context, input, &mut shape) ||
|
||||
parse_component(context, input, &mut ref_box)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
if let Some(shp) = shape {
|
||||
return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default()));
|
||||
}
|
||||
|
||||
match ref_box {
|
||||
Some(r) => Ok(to_reference_box(r)),
|
||||
None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ClipPath {
|
||||
#[inline]
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(ShapeSource::None);
|
||||
return Ok(ClipPath::None);
|
||||
}
|
||||
|
||||
fn parse_component<U: Parse>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser,
|
||||
component: &mut Option<U>,
|
||||
) -> bool {
|
||||
if component.is_some() {
|
||||
return false; // already parsed this component
|
||||
if is_clip_path_path_enabled(context) {
|
||||
if let Ok(p) = input.try(|i| Path::parse(context, i)) {
|
||||
return Ok(ClipPath::Path(p));
|
||||
}
|
||||
|
||||
*component = input.try(|i| U::parse(context, i)).ok();
|
||||
component.is_some()
|
||||
}
|
||||
|
||||
let mut shape = None;
|
||||
let mut ref_box = None;
|
||||
|
||||
while parse_component(context, input, &mut shape) ||
|
||||
parse_component(context, input, &mut ref_box)
|
||||
{
|
||||
//
|
||||
if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
|
||||
return Ok(ClipPath::Url(url));
|
||||
}
|
||||
|
||||
if let Some(shp) = shape {
|
||||
return Ok(ShapeSource::Shape(Box::new(shp), ref_box));
|
||||
}
|
||||
|
||||
ref_box
|
||||
.map(ShapeSource::Box)
|
||||
.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
parse_shape_or_box(context, input, ClipPath::Shape, ClipPath::Box)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for GeometryBox {
|
||||
impl Parse for ShapeOutside {
|
||||
#[inline]
|
||||
fn parse<'i, 't>(
|
||||
_context: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if let Ok(shape_box) = input.try(ShapeBox::parse) {
|
||||
return Ok(GeometryBox::ShapeBox(shape_box));
|
||||
// Need to parse this here so that `Image::parse_with_cors_anonymous`
|
||||
// doesn't parse it.
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(ShapeOutside::None);
|
||||
}
|
||||
|
||||
try_match_ident_ignore_ascii_case! { input,
|
||||
"fill-box" => Ok(GeometryBox::FillBox),
|
||||
"stroke-box" => Ok(GeometryBox::StrokeBox),
|
||||
"view-box" => Ok(GeometryBox::ViewBox),
|
||||
if let Ok(image) = input.try(|i| Image::parse_with_cors_anonymous(context, i)) {
|
||||
debug_assert_ne!(image, Image::None);
|
||||
return Ok(ShapeOutside::Image(image));
|
||||
}
|
||||
|
||||
parse_shape_or_box(context, input, ShapeOutside::Shape, ShapeOutside::Box)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -234,7 +234,18 @@ impl Parse for BorderSpacing {
|
|||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
MallocSizeOf,
|
||||
Parse,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum BorderImageRepeatKeyword {
|
||||
Stretch,
|
||||
|
|
|
@ -104,6 +104,8 @@ pub enum DisplayInside {
|
|||
#[cfg(feature = "gecko")]
|
||||
MozGridLine,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozStack,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozDeck,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozPopup,
|
||||
|
@ -227,6 +229,8 @@ impl Display {
|
|||
#[cfg(feature = "gecko")]
|
||||
pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
|
||||
|
@ -616,6 +620,8 @@ impl Parse for Display {
|
|||
#[cfg(feature = "gecko")]
|
||||
"-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
|
||||
|
@ -1595,7 +1601,7 @@ pub enum Appearance {
|
|||
Meterchunk,
|
||||
/// The "arrowed" part of the dropdown button that open up a dropdown list.
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozMenulistButton,
|
||||
MozMenulistArrowButton,
|
||||
/// For HTML's <input type=number>
|
||||
NumberInput,
|
||||
/// A horizontal progress bar.
|
||||
|
@ -1624,7 +1630,7 @@ pub enum Appearance {
|
|||
RadioLabel,
|
||||
/// nsRangeFrame and its subparts
|
||||
Range,
|
||||
RangeThumb,
|
||||
RangeThumb, // FIXME: This should not be exposed to content.
|
||||
/// The resizer background area in a status bar for the resizer widget in
|
||||
/// the corner of a window.
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
|
@ -1850,6 +1856,14 @@ pub enum Appearance {
|
|||
Count,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
/// Returns whether we're the `none` value.
|
||||
#[inline]
|
||||
pub fn is_none(self) -> bool {
|
||||
self == Appearance::None
|
||||
}
|
||||
}
|
||||
|
||||
/// A kind of break between two boxes.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-break/#break-between
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
|
||||
|
||||
use crate::parser::ParserContext;
|
||||
use crate::values::computed;
|
||||
use crate::values::generics::calc as generic;
|
||||
use crate::values::generics::calc::{MinMaxOp, SortKey};
|
||||
use crate::values::specified::length::ViewportPercentageLength;
|
||||
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
||||
use crate::values::specified::{self, Angle, Time};
|
||||
use crate::values::{CSSFloat, CSSInteger};
|
||||
use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
use std::fmt::{self, Write};
|
||||
use std::{cmp, mem};
|
||||
use style_traits::values::specified::AllowedNumericType;
|
||||
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
||||
|
||||
|
@ -32,40 +33,9 @@ pub enum MathFunction {
|
|||
Clamp,
|
||||
}
|
||||
|
||||
/// This determines the order in which we serialize members of a calc()
|
||||
/// sum.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
enum SortKey {
|
||||
Number,
|
||||
Percentage,
|
||||
Ch,
|
||||
Deg,
|
||||
Em,
|
||||
Ex,
|
||||
Px,
|
||||
Rem,
|
||||
Sec,
|
||||
Vh,
|
||||
Vmax,
|
||||
Vmin,
|
||||
Vw,
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Whether we're a `min` or `max` function.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum MinMaxOp {
|
||||
/// `min()`
|
||||
Min,
|
||||
/// `max()`
|
||||
Max,
|
||||
}
|
||||
|
||||
/// A node inside a `Calc` expression's AST.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum CalcNode {
|
||||
/// A leaf node inside a `Calc` expression's AST.
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum Leaf {
|
||||
/// `<length>`
|
||||
Length(NoCalcLength),
|
||||
/// `<angle>`
|
||||
|
@ -76,27 +46,28 @@ pub enum CalcNode {
|
|||
Percentage(CSSFloat),
|
||||
/// `<number>`
|
||||
Number(CSSFloat),
|
||||
/// An expression of the form `x + y + ...`. Subtraction is represented by
|
||||
/// the negated expression of the right hand side.
|
||||
Sum(Box<[CalcNode]>),
|
||||
/// A `min()` / `max()` function.
|
||||
MinMax(Box<[CalcNode]>, MinMaxOp),
|
||||
/// A `clamp()` function.
|
||||
Clamp {
|
||||
/// The minimum value.
|
||||
min: Box<CalcNode>,
|
||||
/// The central value.
|
||||
center: Box<CalcNode>,
|
||||
/// The maximum value.
|
||||
max: Box<CalcNode>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ToCss for Leaf {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
match *self {
|
||||
Self::Length(ref l) => l.to_css(dest),
|
||||
Self::Number(ref n) => n.to_css(dest),
|
||||
Self::Percentage(p) => crate::values::serialize_percentage(p, dest),
|
||||
Self::Angle(ref a) => a.to_css(dest),
|
||||
Self::Time(ref t) => t.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An expected unit we intend to parse within a `calc()` expression.
|
||||
///
|
||||
/// This is used as a hint for the parser to fast-reject invalid expressions.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum CalcUnit {
|
||||
enum CalcUnit {
|
||||
/// `<number>`
|
||||
Number,
|
||||
/// `<length>`
|
||||
|
@ -117,178 +88,50 @@ pub enum CalcUnit {
|
|||
/// relative lengths, and to_computed_pixel_length_without_context() handles
|
||||
/// this case. Therefore, if you want to add a new field, please make sure this
|
||||
/// function work properly.
|
||||
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct CalcLengthPercentage {
|
||||
#[css(skip)]
|
||||
pub clamping_mode: AllowedNumericType,
|
||||
pub absolute: Option<AbsoluteLength>,
|
||||
pub vw: Option<CSSFloat>,
|
||||
pub vh: Option<CSSFloat>,
|
||||
pub vmin: Option<CSSFloat>,
|
||||
pub vmax: Option<CSSFloat>,
|
||||
pub em: Option<CSSFloat>,
|
||||
pub ex: Option<CSSFloat>,
|
||||
pub ch: Option<CSSFloat>,
|
||||
pub rem: Option<CSSFloat>,
|
||||
pub percentage: Option<computed::Percentage>,
|
||||
}
|
||||
|
||||
impl ToCss for CalcLengthPercentage {
|
||||
/// <https://drafts.csswg.org/css-values/#calc-serialize>
|
||||
///
|
||||
/// FIXME(emilio): Should this simplify away zeros?
|
||||
#[allow(unused_assignments)]
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
use num_traits::Zero;
|
||||
|
||||
let mut first_value = true;
|
||||
macro_rules! first_value_check {
|
||||
($val:expr) => {
|
||||
if !first_value {
|
||||
dest.write_str(if $val < Zero::zero() { " - " } else { " + " })?;
|
||||
} else if $val < Zero::zero() {
|
||||
dest.write_str("-")?;
|
||||
}
|
||||
first_value = false;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! serialize {
|
||||
( $( $val:ident ),* ) => {
|
||||
$(
|
||||
if let Some(val) = self.$val {
|
||||
first_value_check!(val);
|
||||
val.abs().to_css(dest)?;
|
||||
dest.write_str(stringify!($val))?;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! serialize_abs {
|
||||
( $( $val:ident ),+ ) => {
|
||||
$(
|
||||
if let Some(AbsoluteLength::$val(v)) = self.absolute {
|
||||
first_value_check!(v);
|
||||
AbsoluteLength::$val(v.abs()).to_css(dest)?;
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
dest.write_str("calc(")?;
|
||||
|
||||
// NOTE(emilio): Percentages first because of web-compat problems, see:
|
||||
// https://github.com/w3c/csswg-drafts/issues/1731
|
||||
if let Some(val) = self.percentage {
|
||||
first_value_check!(val.0);
|
||||
val.abs().to_css(dest)?;
|
||||
}
|
||||
|
||||
// NOTE(emilio): The order here it's very intentional, and alphabetic
|
||||
// per the spec linked above.
|
||||
serialize!(ch);
|
||||
serialize_abs!(Cm);
|
||||
serialize!(em, ex);
|
||||
serialize_abs!(In, Mm, Pc, Pt, Px, Q);
|
||||
serialize!(rem, vh, vmax, vmin, vw);
|
||||
|
||||
dest.write_str(")")
|
||||
}
|
||||
pub node: CalcNode,
|
||||
}
|
||||
|
||||
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
||||
|
||||
macro_rules! impl_generic_to_type {
|
||||
($self:ident, $self_variant:ident, $to_self:ident, $to_float:ident, $from_float:path) => {{
|
||||
if let Self::$self_variant(ref v) = *$self {
|
||||
return Ok(v.clone());
|
||||
impl PartialOrd for Leaf {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
use self::Leaf::*;
|
||||
|
||||
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
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> {
|
||||
use self::CalcNode::*;
|
||||
match (self, other) {
|
||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
||||
(&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
|
||||
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
|
||||
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
|
||||
_ => None,
|
||||
_ => {
|
||||
match *self {
|
||||
Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..) => {},
|
||||
}
|
||||
unsafe {
|
||||
debug_unreachable!("Forgot a branch?");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CalcNode {
|
||||
fn negate(&mut self) {
|
||||
self.mul_by(-1.);
|
||||
impl generic::CalcNodeLeaf for Leaf {
|
||||
fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
Self::Length(ref l) => l.is_negative(),
|
||||
Self::Percentage(n) | Self::Number(n) => n < 0.,
|
||||
Self::Angle(ref a) => a.degrees() < 0.,
|
||||
Self::Time(ref t) => t.seconds() < 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_by(&mut self, scalar: f32) {
|
||||
|
@ -310,44 +153,10 @@ impl CalcNode {
|
|||
Self::Percentage(ref mut p) => {
|
||||
*p *= scalar;
|
||||
},
|
||||
// Multiplication is distributive across this.
|
||||
Self::Sum(ref mut children) => {
|
||||
for node in &mut **children {
|
||||
node.mul_by(scalar);
|
||||
}
|
||||
},
|
||||
// This one is a bit trickier.
|
||||
Self::MinMax(ref mut children, ref mut op) => {
|
||||
for node in &mut **children {
|
||||
node.mul_by(scalar);
|
||||
}
|
||||
|
||||
// For negatives we need to invert the operation.
|
||||
if scalar < 0. {
|
||||
*op = match *op {
|
||||
MinMaxOp::Min => MinMaxOp::Max,
|
||||
MinMaxOp::Max => MinMaxOp::Min,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Multiplication is distributive across these.
|
||||
Self::Clamp {
|
||||
ref mut min,
|
||||
ref mut center,
|
||||
ref mut max,
|
||||
} => {
|
||||
min.mul_by(scalar);
|
||||
center.mul_by(scalar);
|
||||
max.mul_by(scalar);
|
||||
// For negatives we need to swap min / max.
|
||||
if scalar < 0. {
|
||||
mem::swap(min, max);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_node_sort_key(&self) -> SortKey {
|
||||
fn sort_key(&self) -> SortKey {
|
||||
match *self {
|
||||
Self::Number(..) => SortKey::Number,
|
||||
Self::Percentage(..) => SortKey::Percentage,
|
||||
|
@ -369,7 +178,12 @@ impl CalcNode {
|
|||
},
|
||||
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
||||
},
|
||||
Self::Sum(..) | Self::MinMax(..) | Self::Clamp { .. } => SortKey::Other,
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify(&mut self) {
|
||||
if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self {
|
||||
*abs = AbsoluteLength::Px(abs.to_px());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,7 +192,11 @@ impl CalcNode {
|
|||
/// Only handles leaf nodes, it's the caller's responsibility to simplify
|
||||
/// them before calling this if needed.
|
||||
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
|
||||
use self::CalcNode::*;
|
||||
use self::Leaf::*;
|
||||
|
||||
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
(&mut Number(ref mut one), &Number(ref other)) |
|
||||
|
@ -394,170 +212,24 @@ impl CalcNode {
|
|||
(&mut Length(ref mut one), &Length(ref other)) => {
|
||||
*one = one.try_sum(other)?;
|
||||
},
|
||||
_ => return Err(()),
|
||||
_ => {
|
||||
match *other {
|
||||
Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) => {},
|
||||
}
|
||||
unsafe {
|
||||
debug_unreachable!();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplifies and sorts the calculation. This is only needed if it's going
|
||||
/// to be preserved after parsing (so, for `<length-percentage>`). Otherwise
|
||||
/// we can just evaluate it and we'll come up with a simplified value
|
||||
/// anyways.
|
||||
fn simplify_and_sort_children(&mut self) {
|
||||
macro_rules! replace_self_with {
|
||||
($slot:expr) => {{
|
||||
let result = mem::replace($slot, Self::Number(0.));
|
||||
mem::replace(self, result);
|
||||
}};
|
||||
}
|
||||
match *self {
|
||||
Self::Clamp {
|
||||
ref mut min,
|
||||
ref mut center,
|
||||
ref mut max,
|
||||
} => {
|
||||
min.simplify_and_sort_children();
|
||||
center.simplify_and_sort_children();
|
||||
max.simplify_and_sort_children();
|
||||
|
||||
// NOTE: clamp() is max(min, min(center, max))
|
||||
let min_cmp_center = match min.partial_cmp(¢er) {
|
||||
Some(o) => o,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// So if we can prove that min is more than center, then we won,
|
||||
// as that's what we should always return.
|
||||
if matches!(min_cmp_center, cmp::Ordering::Greater) {
|
||||
return replace_self_with!(&mut **min);
|
||||
}
|
||||
|
||||
// Otherwise try with max.
|
||||
let max_cmp_center = match max.partial_cmp(¢er) {
|
||||
Some(o) => o,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if matches!(max_cmp_center, cmp::Ordering::Less) {
|
||||
// max is less than center, so we need to return effectively
|
||||
// `max(min, max)`.
|
||||
let max_cmp_min = match max.partial_cmp(&min) {
|
||||
Some(o) => o,
|
||||
None => {
|
||||
debug_assert!(
|
||||
false,
|
||||
"We compared center with min and max, how are \
|
||||
min / max not comparable with each other?"
|
||||
);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if matches!(max_cmp_min, cmp::Ordering::Less) {
|
||||
return replace_self_with!(&mut **min);
|
||||
}
|
||||
|
||||
return replace_self_with!(&mut **max);
|
||||
}
|
||||
|
||||
// Otherwise we're the center node.
|
||||
return replace_self_with!(&mut **center);
|
||||
},
|
||||
Self::MinMax(ref mut children, op) => {
|
||||
for child in &mut **children {
|
||||
child.simplify_and_sort_children();
|
||||
}
|
||||
|
||||
let winning_order = match op {
|
||||
MinMaxOp::Min => cmp::Ordering::Less,
|
||||
MinMaxOp::Max => cmp::Ordering::Greater,
|
||||
};
|
||||
|
||||
let mut result = 0;
|
||||
for i in 1..children.len() {
|
||||
let o = match children[i].partial_cmp(&children[result]) {
|
||||
// We can't compare all the children, so we can't
|
||||
// know which one will actually win. Bail out and
|
||||
// keep ourselves as a min / max function.
|
||||
//
|
||||
// TODO: Maybe we could simplify compatible children,
|
||||
// see https://github.com/w3c/csswg-drafts/issues/4756
|
||||
None => return,
|
||||
Some(o) => o,
|
||||
};
|
||||
|
||||
if o == winning_order {
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
|
||||
replace_self_with!(&mut children[result]);
|
||||
},
|
||||
Self::Sum(ref mut children_slot) => {
|
||||
let mut sums_to_merge = SmallVec::<[_; 3]>::new();
|
||||
let mut extra_kids = 0;
|
||||
for (i, child) in children_slot.iter_mut().enumerate() {
|
||||
child.simplify_and_sort_children();
|
||||
if let Self::Sum(ref mut children) = *child {
|
||||
extra_kids += children.len();
|
||||
sums_to_merge.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// If we only have one kid, we've already simplified it, and it
|
||||
// doesn't really matter whether it's a sum already or not, so
|
||||
// lift it up and continue.
|
||||
if children_slot.len() == 1 {
|
||||
return replace_self_with!(&mut children_slot[0]);
|
||||
}
|
||||
|
||||
let mut children = mem::replace(children_slot, Box::new([])).into_vec();
|
||||
|
||||
if !sums_to_merge.is_empty() {
|
||||
children.reserve(extra_kids - sums_to_merge.len());
|
||||
// Merge all our nested sums, in reverse order so that the
|
||||
// list indices are not invalidated.
|
||||
for i in sums_to_merge.drain(..).rev() {
|
||||
let kid_children = match children.swap_remove(i) {
|
||||
Self::Sum(c) => c,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// This would be nicer with
|
||||
// https://github.com/rust-lang/rust/issues/59878 fixed.
|
||||
children.extend(kid_children.into_vec());
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(children.len() >= 2, "Should still have multiple kids!");
|
||||
|
||||
// Sort by spec order.
|
||||
children.sort_unstable_by_key(|c| c.calc_node_sort_key());
|
||||
|
||||
// NOTE: if the function returns true, by the docs of dedup_by,
|
||||
// a is removed.
|
||||
children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());
|
||||
|
||||
if children.len() == 1 {
|
||||
// If only one children remains, lift it up, and carry on.
|
||||
replace_self_with!(&mut children[0]);
|
||||
} else {
|
||||
// Else put our simplified children back.
|
||||
mem::replace(children_slot, children.into_boxed_slice());
|
||||
}
|
||||
},
|
||||
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.
|
||||
},
|
||||
}
|
||||
}
|
||||
/// A calc node representation for specified values.
|
||||
pub type CalcNode = generic::GenericCalcNode<Leaf>;
|
||||
|
||||
impl CalcNode {
|
||||
/// Tries to parse a single element in the expression, that is, a
|
||||
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
|
||||
/// `expected_unit`.
|
||||
|
@ -571,7 +243,7 @@ impl CalcNode {
|
|||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
match (input.next()?, expected_unit) {
|
||||
(&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)),
|
||||
(&Token::Number { value, .. }, _) => Ok(CalcNode::Leaf(Leaf::Number(value))),
|
||||
(
|
||||
&Token::Dimension {
|
||||
value, ref unit, ..
|
||||
|
@ -583,18 +255,22 @@ impl CalcNode {
|
|||
value, ref unit, ..
|
||||
},
|
||||
CalcUnit::LengthPercentage,
|
||||
) => NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(CalcNode::Length)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
) => match NoCalcLength::parse_dimension(context, value, unit) {
|
||||
Ok(l) => Ok(CalcNode::Leaf(Leaf::Length(l))),
|
||||
Err(()) => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
},
|
||||
(
|
||||
&Token::Dimension {
|
||||
value, ref unit, ..
|
||||
},
|
||||
CalcUnit::Angle,
|
||||
) => {
|
||||
Angle::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
.map(CalcNode::Angle)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
match Angle::parse_dimension(value, unit, /* from_calc = */ true) {
|
||||
Ok(a) => Ok(CalcNode::Leaf(Leaf::Angle(a))),
|
||||
Err(()) => {
|
||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
}
|
||||
},
|
||||
(
|
||||
&Token::Dimension {
|
||||
|
@ -602,13 +278,16 @@ impl CalcNode {
|
|||
},
|
||||
CalcUnit::Time,
|
||||
) => {
|
||||
Time::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
.map(CalcNode::Time)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
match Time::parse_dimension(value, unit, /* from_calc = */ true) {
|
||||
Ok(t) => Ok(CalcNode::Leaf(Leaf::Time(t))),
|
||||
Err(()) => {
|
||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
}
|
||||
},
|
||||
(&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
|
||||
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
|
||||
Ok(CalcNode::Percentage(unit_value))
|
||||
Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
|
||||
},
|
||||
(&Token::ParenthesisBlock, _) => input.parse_nested_block(|input| {
|
||||
CalcNode::parse_argument(context, input, expected_unit)
|
||||
|
@ -654,11 +333,9 @@ impl CalcNode {
|
|||
//
|
||||
// Consider adding an API to cssparser to specify the
|
||||
// initial vector capacity?
|
||||
let arguments = input
|
||||
.parse_comma_separated(|input| {
|
||||
Self::parse_argument(context, input, expected_unit)
|
||||
})?
|
||||
.into_boxed_slice();
|
||||
let arguments = input.parse_comma_separated(|input| {
|
||||
Self::parse_argument(context, input, expected_unit)
|
||||
})?;
|
||||
|
||||
let op = match function {
|
||||
MathFunction::Min => MinMaxOp::Min,
|
||||
|
@ -666,7 +343,7 @@ impl CalcNode {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(Self::MinMax(arguments, op))
|
||||
Ok(Self::MinMax(arguments.into(), op))
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -712,7 +389,7 @@ impl CalcNode {
|
|||
Ok(if sum.len() == 1 {
|
||||
sum.drain(..).next().unwrap()
|
||||
} else {
|
||||
Self::Sum(sum.into_boxed_slice())
|
||||
Self::Sum(sum.into_boxed_slice().into())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -773,106 +450,64 @@ impl CalcNode {
|
|||
Ok(node)
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into a `<length>` or `<percentage`>
|
||||
/// Tries to simplify this expression into a `<length>` or `<percentage>`
|
||||
/// value.
|
||||
fn to_length_or_percentage(
|
||||
&mut self,
|
||||
fn into_length_or_percentage(
|
||||
mut self,
|
||||
clamping_mode: AllowedNumericType,
|
||||
) -> Result<CalcLengthPercentage, ()> {
|
||||
let mut ret = CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
..Default::default()
|
||||
};
|
||||
self.simplify_and_sort_children();
|
||||
self.add_length_or_percentage_to(&mut ret, 1.0)?;
|
||||
Ok(ret)
|
||||
}
|
||||
// Keep track of whether there's any invalid member of the calculation,
|
||||
// so as to reject the calculation properly at parse-time.
|
||||
let mut any_invalid = false;
|
||||
self.visit_depth_first(|node| {
|
||||
if let CalcNode::Leaf(ref l) = *node {
|
||||
any_invalid |= !matches!(*l, Leaf::Percentage(..) | Leaf::Length(..));
|
||||
}
|
||||
node.simplify_and_sort_direct_children();
|
||||
});
|
||||
|
||||
/// Puts this `<length>` or `<percentage>` into `ret`, or error.
|
||||
///
|
||||
/// `factor` is the sign or multiplicative factor to account for the sign
|
||||
/// (this allows adding and substracting into the return value).
|
||||
fn add_length_or_percentage_to(
|
||||
&self,
|
||||
ret: &mut CalcLengthPercentage,
|
||||
factor: CSSFloat,
|
||||
) -> Result<(), ()> {
|
||||
match *self {
|
||||
CalcNode::Percentage(pct) => {
|
||||
ret.percentage = Some(computed::Percentage(
|
||||
ret.percentage.map_or(0., |p| p.0) + pct * factor,
|
||||
));
|
||||
},
|
||||
CalcNode::Length(ref l) => match *l {
|
||||
NoCalcLength::Absolute(abs) => {
|
||||
ret.absolute = Some(match ret.absolute {
|
||||
Some(value) => value + abs * factor,
|
||||
None => abs * factor,
|
||||
});
|
||||
},
|
||||
NoCalcLength::FontRelative(rel) => match rel {
|
||||
FontRelativeLength::Em(em) => {
|
||||
ret.em = Some(ret.em.unwrap_or(0.) + em * factor);
|
||||
},
|
||||
FontRelativeLength::Ex(ex) => {
|
||||
ret.ex = Some(ret.ex.unwrap_or(0.) + ex * factor);
|
||||
},
|
||||
FontRelativeLength::Ch(ch) => {
|
||||
ret.ch = Some(ret.ch.unwrap_or(0.) + ch * factor);
|
||||
},
|
||||
FontRelativeLength::Rem(rem) => {
|
||||
ret.rem = Some(ret.rem.unwrap_or(0.) + rem * factor);
|
||||
},
|
||||
},
|
||||
NoCalcLength::ViewportPercentage(rel) => match rel {
|
||||
ViewportPercentageLength::Vh(vh) => {
|
||||
ret.vh = Some(ret.vh.unwrap_or(0.) + vh * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vw(vw) => {
|
||||
ret.vw = Some(ret.vw.unwrap_or(0.) + vw * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vmax(vmax) => {
|
||||
ret.vmax = Some(ret.vmax.unwrap_or(0.) + vmax * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vmin(vmin) => {
|
||||
ret.vmin = Some(ret.vmin.unwrap_or(0.) + vmin * factor)
|
||||
},
|
||||
},
|
||||
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
||||
},
|
||||
CalcNode::Sum(ref children) => {
|
||||
for child in &**children {
|
||||
child.add_length_or_percentage_to(ret, factor)?;
|
||||
}
|
||||
},
|
||||
CalcNode::MinMax(..) | CalcNode::Clamp { .. } => {
|
||||
// FIXME(emilio): Implement min/max/clamp for length-percentage.
|
||||
return Err(());
|
||||
},
|
||||
CalcNode::Angle(..) | CalcNode::Time(..) | CalcNode::Number(..) => return Err(()),
|
||||
if any_invalid {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
node: self,
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into a `<time>` value.
|
||||
fn to_time(&self) -> Result<Time, ()> {
|
||||
impl_generic_to_type!(self, Time, to_time, seconds, Time::from_calc)
|
||||
let seconds = self.resolve(|leaf| match *leaf {
|
||||
Leaf::Time(ref t) => Ok(t.seconds()),
|
||||
_ => Err(()),
|
||||
})?;
|
||||
Ok(Time::from_calc(seconds))
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into an `Angle` value.
|
||||
fn to_angle(&self) -> Result<Angle, ()> {
|
||||
impl_generic_to_type!(self, Angle, to_angle, degrees, Angle::from_calc)
|
||||
let degrees = self.resolve(|leaf| match *leaf {
|
||||
Leaf::Angle(ref angle) => Ok(angle.degrees()),
|
||||
_ => Err(()),
|
||||
})?;
|
||||
Ok(Angle::from_calc(degrees))
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into a `<number>` value.
|
||||
fn to_number(&self) -> Result<CSSFloat, ()> {
|
||||
impl_generic_to_type!(self, Number, to_number, clone, From::from)
|
||||
self.resolve(|leaf| match *leaf {
|
||||
Leaf::Number(n) => Ok(n),
|
||||
_ => Err(()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into a `<percentage>` value.
|
||||
fn to_percentage(&self) -> Result<CSSFloat, ()> {
|
||||
impl_generic_to_type!(self, Percentage, to_percentage, clone, From::from)
|
||||
self.resolve(|leaf| match *leaf {
|
||||
Leaf::Percentage(p) => Ok(p),
|
||||
_ => Err(()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Given a function name, and the location from where the token came from,
|
||||
|
@ -926,7 +561,7 @@ impl CalcNode {
|
|||
function: MathFunction,
|
||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||
Self::parse(context, input, function, CalcUnit::LengthPercentage)?
|
||||
.to_length_or_percentage(clamping_mode)
|
||||
.into_length_or_percentage(clamping_mode)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
|
@ -949,7 +584,7 @@ impl CalcNode {
|
|||
function: MathFunction,
|
||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||
Self::parse(context, input, function, CalcUnit::Length)?
|
||||
.to_length_or_percentage(clamping_mode)
|
||||
.into_length_or_percentage(clamping_mode)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
|
|
|
@ -140,8 +140,10 @@ pub enum SystemColor {
|
|||
Windowframe,
|
||||
Windowtext,
|
||||
MozButtondefault,
|
||||
MozDefaultColor,
|
||||
MozDefaultBackgroundColor,
|
||||
#[parse(aliases = "-moz-default-color")]
|
||||
Canvastext,
|
||||
#[parse(aliases = "-moz-default-background-color")]
|
||||
Canvas,
|
||||
MozDialog,
|
||||
MozDialogtext,
|
||||
/// Used to highlight valid regions to drop something onto.
|
||||
|
@ -230,9 +232,12 @@ pub enum SystemColor {
|
|||
/// colors.
|
||||
MozNativehyperlinktext,
|
||||
|
||||
MozHyperlinktext,
|
||||
MozActivehyperlinktext,
|
||||
MozVisitedhyperlinktext,
|
||||
#[parse(aliases = "-moz-hyperlinktext")]
|
||||
Linktext,
|
||||
#[parse(aliases = "-moz-activehyperlinktext")]
|
||||
Activetext,
|
||||
#[parse(aliases = "-moz-visitedhyperlinktext")]
|
||||
Visitedtext,
|
||||
|
||||
/// Combobox widgets
|
||||
MozComboboxtext,
|
||||
|
@ -253,11 +258,11 @@ impl SystemColor {
|
|||
let prefs = cx.device().pref_sheet_prefs();
|
||||
|
||||
convert_nscolor_to_computedcolor(match *self {
|
||||
SystemColor::MozDefaultColor => prefs.mDefaultColor,
|
||||
SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
|
||||
SystemColor::MozHyperlinktext => prefs.mLinkColor,
|
||||
SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor,
|
||||
SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
|
||||
SystemColor::Canvastext => prefs.mDefaultColor,
|
||||
SystemColor::Canvas => prefs.mDefaultBackgroundColor,
|
||||
SystemColor::Linktext => prefs.mLinkColor,
|
||||
SystemColor::Activetext => prefs.mActiveLinkColor,
|
||||
SystemColor::Visitedtext => prefs.mVisitedLinkColor,
|
||||
|
||||
_ => unsafe {
|
||||
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
|
||||
|
|
|
@ -492,7 +492,9 @@ impl ToComputedValue for FontStretch {
|
|||
SpecifiedValueInfo,
|
||||
ToAnimatedValue,
|
||||
ToAnimatedZero,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[allow(missing_docs)]
|
||||
|
@ -534,7 +536,9 @@ impl Default for KeywordSize {
|
|||
PartialEq,
|
||||
ToAnimatedValue,
|
||||
ToAnimatedZero,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
/// Additional information for keyword-derived font sizes.
|
||||
|
@ -567,7 +571,7 @@ impl KeywordInfo {
|
|||
/// Computes the final size for this font-size keyword, accounting for
|
||||
/// text-zoom.
|
||||
fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
|
||||
let base = context.maybe_zoom_text(self.kw.to_computed_value(context).0);
|
||||
let base = context.maybe_zoom_text(self.kw.to_length(context).0);
|
||||
base * self.factor + context.maybe_zoom_text(self.offset)
|
||||
}
|
||||
|
||||
|
@ -760,11 +764,10 @@ const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
|
|||
/// The default font size.
|
||||
pub const FONT_MEDIUM_PX: i32 = 16;
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
impl ToComputedValue for KeywordSize {
|
||||
type ComputedValue = NonNegativeLength;
|
||||
impl KeywordSize {
|
||||
#[inline]
|
||||
fn to_computed_value(&self, _: &Context) -> NonNegativeLength {
|
||||
#[cfg(feature = "servo")]
|
||||
fn to_length(&self, _: &Context) -> NonNegativeLength {
|
||||
let medium = Length::new(FONT_MEDIUM_PX as f32);
|
||||
// https://drafts.csswg.org/css-fonts-3/#font-size-prop
|
||||
NonNegative(match *self {
|
||||
|
@ -779,17 +782,9 @@ impl ToComputedValue for KeywordSize {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
fn from_computed_value(_: &NonNegativeLength) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl ToComputedValue for KeywordSize {
|
||||
type ComputedValue = NonNegativeLength;
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> NonNegativeLength {
|
||||
fn to_length(&self, cx: &Context) -> NonNegativeLength {
|
||||
use crate::context::QuirksMode;
|
||||
|
||||
// The tables in this function are originally from
|
||||
|
@ -857,11 +852,6 @@ impl ToComputedValue for KeywordSize {
|
|||
base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(_: &NonNegativeLength) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl FontSize {
|
||||
|
|
|
@ -185,16 +185,6 @@ impl TrackRepeat<LengthPercentage, Integer> {
|
|||
|
||||
values.push(track_size);
|
||||
names.push(current_names);
|
||||
if is_auto {
|
||||
// FIXME: In the older version of the spec
|
||||
// (https://www.w3.org/TR/2015/WD-css-grid-1-20150917/#typedef-auto-repeat),
|
||||
// if the repeat type is `<auto-repeat>` we shouldn't try to parse more than
|
||||
// one `TrackSize`. But in current version of the spec, this is deprecated
|
||||
// but we are adding this for gecko parity. We should remove this when
|
||||
// gecko implements new spec.
|
||||
names.push(input.try(parse_line_names).unwrap_or_default());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if values.is_empty() {
|
||||
// expecting at least one <track-size>
|
||||
|
|
|
@ -19,7 +19,8 @@ use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPosi
|
|||
use crate::values::specified::position::{Position, PositionComponent, Side};
|
||||
use crate::values::specified::url::SpecifiedImageUrl;
|
||||
use crate::values::specified::{
|
||||
Angle, Color, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
|
||||
Angle, AngleOrPercentage, Color, Length, LengthPercentage, NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
};
|
||||
use crate::values::specified::{Number, NumberOrPercentage, Percentage};
|
||||
use crate::Atom;
|
||||
|
@ -32,24 +33,6 @@ use std::fmt::{self, Write};
|
|||
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};
|
||||
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
||||
|
||||
/// A specified image layer.
|
||||
pub type ImageLayer = generic::GenericImageLayer<Image>;
|
||||
|
||||
impl ImageLayer {
|
||||
/// This is a specialization of Either with an alternative parse
|
||||
/// method to provide anonymous CORS headers for the Image url fetch.
|
||||
pub fn parse_with_cors_anonymous<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if let Ok(v) = input.try(|i| Image::parse_with_cors_anonymous(context, i)) {
|
||||
return Ok(generic::GenericImageLayer::Image(v));
|
||||
}
|
||||
input.expect_ident_matching("none")?;
|
||||
Ok(generic::GenericImageLayer::None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specified values for an image according to CSS-IMAGES.
|
||||
/// <https://drafts.csswg.org/css-images/#image-values>
|
||||
pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl>;
|
||||
|
@ -62,9 +45,23 @@ pub type Gradient = generic::Gradient<
|
|||
NonNegativeLength,
|
||||
NonNegativeLengthPercentage,
|
||||
Position,
|
||||
Angle,
|
||||
AngleOrPercentage,
|
||||
Color,
|
||||
>;
|
||||
|
||||
type LengthPercentageItemList = crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn conic_gradients_enabled() -> bool {
|
||||
static_prefs::pref!("layout.css.conic-gradient.enabled")
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn conic_gradients_enabled() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl SpecifiedValueInfo for Gradient {
|
||||
const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
|
||||
|
||||
|
@ -85,13 +82,13 @@ impl SpecifiedValueInfo for Gradient {
|
|||
"-moz-repeating-radial-gradient",
|
||||
"-webkit-gradient",
|
||||
]);
|
||||
|
||||
if conic_gradients_enabled() {
|
||||
f(&["conic-gradient", "repeating-conic-gradient"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A specified gradient kind.
|
||||
pub type GradientKind =
|
||||
generic::GradientKind<LineDirection, NonNegativeLength, NonNegativeLengthPercentage, Position>;
|
||||
|
||||
/// A specified gradient line direction.
|
||||
///
|
||||
/// FIXME(emilio): This should be generic over Angle.
|
||||
|
@ -110,16 +107,10 @@ pub enum LineDirection {
|
|||
/// A specified ending shape.
|
||||
pub type EndingShape = generic::EndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
|
||||
|
||||
/// A specified gradient item.
|
||||
pub type GradientItem = generic::GradientItem<Color, LengthPercentage>;
|
||||
|
||||
/// A computed color stop.
|
||||
pub type ColorStop = generic::ColorStop<Color, LengthPercentage>;
|
||||
|
||||
/// Specified values for `moz-image-rect`
|
||||
/// -moz-image-rect(<uri>, top, right, bottom, left);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, SpecifiedImageUrl>;
|
||||
#[cfg(all(feature = "gecko", not(feature = "cbindgen")))]
|
||||
pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, SpecifiedImageUrl>;
|
||||
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
#[derive(
|
||||
|
@ -141,6 +132,9 @@ impl Parse for Image {
|
|||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Image, ParseError<'i>> {
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(generic::Image::None);
|
||||
}
|
||||
if let Ok(url) = input.try(|input| SpecifiedImageUrl::parse(context, input)) {
|
||||
return Ok(generic::Image::Url(url));
|
||||
}
|
||||
|
@ -211,10 +205,11 @@ impl Parse for Gradient {
|
|||
enum Shape {
|
||||
Linear,
|
||||
Radial,
|
||||
Conic,
|
||||
}
|
||||
|
||||
let func = input.expect_function()?;
|
||||
let (shape, repeating, mut compat_mode) = match_ignore_ascii_case! { &func,
|
||||
let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &func,
|
||||
"linear-gradient" => {
|
||||
(Shape::Linear, false, GradientCompatMode::Modern)
|
||||
},
|
||||
|
@ -255,6 +250,12 @@ impl Parse for Gradient {
|
|||
"-moz-repeating-radial-gradient" => {
|
||||
(Shape::Radial, true, GradientCompatMode::Moz)
|
||||
},
|
||||
"conic-gradient" if conic_gradients_enabled() => {
|
||||
(Shape::Conic, false, GradientCompatMode::Modern)
|
||||
},
|
||||
"repeating-conic-gradient" if conic_gradients_enabled() => {
|
||||
(Shape::Conic, true, GradientCompatMode::Modern)
|
||||
},
|
||||
"-webkit-gradient" => {
|
||||
return input.parse_nested_block(|i| {
|
||||
Self::parse_webkit_gradient_argument(context, i)
|
||||
|
@ -266,25 +267,13 @@ impl Parse for Gradient {
|
|||
}
|
||||
};
|
||||
|
||||
let (kind, items) = input.parse_nested_block(|i| {
|
||||
let shape = match shape {
|
||||
Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?,
|
||||
Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?,
|
||||
};
|
||||
let items = GradientItem::parse_comma_separated(context, i)?;
|
||||
Ok((shape, items))
|
||||
})?;
|
||||
|
||||
if items.len() < 2 {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(Gradient {
|
||||
items,
|
||||
repeating,
|
||||
kind,
|
||||
compat_mode,
|
||||
})
|
||||
Ok(input.parse_nested_block(|i| {
|
||||
Ok(match shape {
|
||||
Shape::Linear => Self::parse_linear(context, i, repeating, compat_mode)?,
|
||||
Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?,
|
||||
Shape::Conic => Self::parse_conic(context, i, repeating)?,
|
||||
})
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,16 +393,21 @@ impl Gradient {
|
|||
let ident = input.expect_ident_cloned()?;
|
||||
input.expect_comma()?;
|
||||
|
||||
let (kind, reverse_stops) = match_ignore_ascii_case! { &ident,
|
||||
Ok(match_ignore_ascii_case! { &ident,
|
||||
"linear" => {
|
||||
let first = Point::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let second = Point::parse(context, input)?;
|
||||
|
||||
let direction = LineDirection::from_points(first, second);
|
||||
let kind = generic::GradientKind::Linear(direction);
|
||||
let items = Gradient::parse_webkit_gradient_stops(context, input, false)?;
|
||||
|
||||
(kind, false)
|
||||
generic::Gradient::Linear {
|
||||
direction,
|
||||
items,
|
||||
repeating: false,
|
||||
compat_mode: GradientCompatMode::Modern,
|
||||
}
|
||||
},
|
||||
"radial" => {
|
||||
let first_point = Point::parse(context, input)?;
|
||||
|
@ -433,16 +427,28 @@ impl Gradient {
|
|||
let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));
|
||||
let shape = generic::EndingShape::Circle(rad);
|
||||
let position: Position = point.into();
|
||||
let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?;
|
||||
|
||||
let kind = generic::GradientKind::Radial(shape, position);
|
||||
(kind, reverse_stops)
|
||||
generic::Gradient::Radial {
|
||||
shape,
|
||||
position,
|
||||
items,
|
||||
repeating: false,
|
||||
compat_mode: GradientCompatMode::Modern,
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());
|
||||
return Err(input.new_custom_error(e));
|
||||
},
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_webkit_gradient_stops<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
reverse_stops: bool,
|
||||
) -> Result<LengthPercentageItemList, ParseError<'i>> {
|
||||
let mut items = input
|
||||
.try(|i| {
|
||||
i.expect_comma()?;
|
||||
|
@ -521,46 +527,62 @@ impl Gradient {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(generic::Gradient {
|
||||
kind,
|
||||
items: items.into(),
|
||||
repeating: false,
|
||||
compat_mode: GradientCompatMode::Modern,
|
||||
})
|
||||
Ok(items.into())
|
||||
}
|
||||
|
||||
/// Not used for -webkit-gradient syntax and conic-gradient
|
||||
fn parse_stops<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<LengthPercentageItemList, ParseError<'i>> {
|
||||
let items =
|
||||
generic::GradientItem::parse_comma_separated(context, input, LengthPercentage::parse)?;
|
||||
if items.len() < 2 {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
Ok(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl GradientKind {
|
||||
/// Parses a linear gradient.
|
||||
/// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
|
||||
fn parse_linear<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
compat_mode: &mut GradientCompatMode,
|
||||
repeating: bool,
|
||||
mut compat_mode: GradientCompatMode,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode))
|
||||
{
|
||||
input.expect_comma()?;
|
||||
d
|
||||
} else {
|
||||
match *compat_mode {
|
||||
GradientCompatMode::Modern => {
|
||||
LineDirection::Vertical(VerticalPositionKeyword::Bottom)
|
||||
},
|
||||
_ => LineDirection::Vertical(VerticalPositionKeyword::Top),
|
||||
}
|
||||
};
|
||||
Ok(generic::GradientKind::Linear(direction))
|
||||
let direction =
|
||||
if let Ok(d) = input.try(|i| LineDirection::parse(context, i, &mut compat_mode)) {
|
||||
input.expect_comma()?;
|
||||
d
|
||||
} else {
|
||||
match compat_mode {
|
||||
GradientCompatMode::Modern => {
|
||||
LineDirection::Vertical(VerticalPositionKeyword::Bottom)
|
||||
},
|
||||
_ => LineDirection::Vertical(VerticalPositionKeyword::Top),
|
||||
}
|
||||
};
|
||||
let items = Gradient::parse_stops(context, input)?;
|
||||
|
||||
Ok(Gradient::Linear {
|
||||
direction,
|
||||
items,
|
||||
repeating,
|
||||
compat_mode,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses a radial gradient.
|
||||
fn parse_radial<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
compat_mode: &mut GradientCompatMode,
|
||||
repeating: bool,
|
||||
compat_mode: GradientCompatMode,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let (shape, position) = match *compat_mode {
|
||||
let (shape, position) = match compat_mode {
|
||||
GradientCompatMode::Modern => {
|
||||
let shape = input.try(|i| EndingShape::parse(context, i, *compat_mode));
|
||||
let shape = input.try(|i| EndingShape::parse(context, i, compat_mode));
|
||||
let position = input.try(|i| {
|
||||
i.expect_ident_matching("at")?;
|
||||
Position::parse(context, i)
|
||||
|
@ -573,7 +595,7 @@ impl GradientKind {
|
|||
if position.is_ok() {
|
||||
i.expect_comma()?;
|
||||
}
|
||||
EndingShape::parse(context, i, *compat_mode)
|
||||
EndingShape::parse(context, i, compat_mode)
|
||||
});
|
||||
(shape, position.ok())
|
||||
},
|
||||
|
@ -588,7 +610,54 @@ impl GradientKind {
|
|||
});
|
||||
|
||||
let position = position.unwrap_or(Position::center());
|
||||
Ok(generic::GradientKind::Radial(shape, position))
|
||||
|
||||
let items = Gradient::parse_stops(context, input)?;
|
||||
|
||||
Ok(Gradient::Radial {
|
||||
shape,
|
||||
position,
|
||||
items,
|
||||
repeating,
|
||||
compat_mode,
|
||||
})
|
||||
}
|
||||
fn parse_conic<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
repeating: bool,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let angle = input.try(|i| {
|
||||
i.expect_ident_matching("from")?;
|
||||
// Spec allows unitless zero start angles
|
||||
// https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle
|
||||
Angle::parse_with_unitless(context, i)
|
||||
});
|
||||
let position = input.try(|i| {
|
||||
i.expect_ident_matching("at")?;
|
||||
Position::parse(context, i)
|
||||
});
|
||||
if angle.is_ok() || position.is_ok() {
|
||||
input.expect_comma()?;
|
||||
}
|
||||
|
||||
let angle = angle.unwrap_or(Angle::zero());
|
||||
let position = position.unwrap_or(Position::center());
|
||||
let items = generic::GradientItem::parse_comma_separated(
|
||||
context,
|
||||
input,
|
||||
AngleOrPercentage::parse_with_unitless,
|
||||
)?;
|
||||
|
||||
if items.len() < 2 {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(Gradient::Conic {
|
||||
angle,
|
||||
position,
|
||||
items,
|
||||
repeating,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,10 +862,12 @@ impl ShapeExtent {
|
|||
}
|
||||
}
|
||||
|
||||
impl GradientItem {
|
||||
impl<T> generic::GradientItem<Color, T> {
|
||||
fn parse_comma_separated<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
parse_position: impl for<'i1, 't1> Fn(&ParserContext, &mut Parser<'i1, 't1>) -> Result<T, ParseError<'i1>>
|
||||
+ Copy,
|
||||
) -> Result<crate::OwnedSlice<Self>, ParseError<'i>> {
|
||||
let mut items = Vec::new();
|
||||
let mut seen_stop = false;
|
||||
|
@ -804,20 +875,20 @@ impl GradientItem {
|
|||
loop {
|
||||
input.parse_until_before(Delimiter::Comma, |input| {
|
||||
if seen_stop {
|
||||
if let Ok(hint) = input.try(|i| LengthPercentage::parse(context, i)) {
|
||||
if let Ok(hint) = input.try(|i| parse_position(context, i)) {
|
||||
seen_stop = false;
|
||||
items.push(generic::GradientItem::InterpolationHint(hint));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let stop = ColorStop::parse(context, input)?;
|
||||
let stop = generic::ColorStop::parse(context, input, parse_position)?;
|
||||
|
||||
if let Ok(multi_position) = input.try(|i| LengthPercentage::parse(context, i)) {
|
||||
if let Ok(multi_position) = input.try(|i| parse_position(context, i)) {
|
||||
let stop_color = stop.color.clone();
|
||||
items.push(stop.into_item());
|
||||
items.push(
|
||||
ColorStop {
|
||||
generic::ColorStop {
|
||||
color: stop_color,
|
||||
position: Some(multi_position),
|
||||
}
|
||||
|
@ -845,14 +916,18 @@ impl GradientItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl Parse for ColorStop {
|
||||
impl<T> generic::ColorStop<Color, T> {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
parse_position: impl for<'i1, 't1> Fn(
|
||||
&ParserContext,
|
||||
&mut Parser<'i1, 't1>,
|
||||
) -> Result<T, ParseError<'i1>>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
Ok(ColorStop {
|
||||
Ok(generic::ColorStop {
|
||||
color: Color::parse(context, input)?,
|
||||
position: input.try(|i| LengthPercentage::parse(context, i)).ok(),
|
||||
position: input.try(|i| parse_position(context, i)).ok(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::values::generics::length::{
|
|||
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
||||
};
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::specified::calc::CalcNode;
|
||||
use crate::values::specified::calc::{self, CalcNode};
|
||||
use crate::values::specified::NonNegativeNumber;
|
||||
use crate::values::CSSFloat;
|
||||
use crate::Zero;
|
||||
|
@ -28,8 +28,8 @@ use std::ops::{Add, Mul};
|
|||
use style_traits::values::specified::AllowedNumericType;
|
||||
use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
|
||||
|
||||
pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
|
||||
pub use super::image::{GradientKind, Image};
|
||||
pub use super::image::Image;
|
||||
pub use super::image::{EndingShape as GradientEndingShape, Gradient};
|
||||
pub use crate::values::specified::calc::CalcLengthPercentage;
|
||||
|
||||
/// Number of app units per pixel
|
||||
|
@ -92,7 +92,16 @@ impl FontRelativeLength {
|
|||
FontRelativeLength::Em(v) |
|
||||
FontRelativeLength::Ex(v) |
|
||||
FontRelativeLength::Ch(v) |
|
||||
FontRelativeLength::Rem(v) => v == 0.0,
|
||||
FontRelativeLength::Rem(v) => v == 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
FontRelativeLength::Em(v) |
|
||||
FontRelativeLength::Ex(v) |
|
||||
FontRelativeLength::Ch(v) |
|
||||
FontRelativeLength::Rem(v) => v < 0.,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +275,16 @@ impl ViewportPercentageLength {
|
|||
ViewportPercentageLength::Vw(v) |
|
||||
ViewportPercentageLength::Vh(v) |
|
||||
ViewportPercentageLength::Vmin(v) |
|
||||
ViewportPercentageLength::Vmax(v) => v == 0.0,
|
||||
ViewportPercentageLength::Vmax(v) => v == 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
ViewportPercentageLength::Vw(v) |
|
||||
ViewportPercentageLength::Vh(v) |
|
||||
ViewportPercentageLength::Vmin(v) |
|
||||
ViewportPercentageLength::Vmax(v) => v < 0.,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,6 +388,18 @@ impl AbsoluteLength {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
AbsoluteLength::Px(v) |
|
||||
AbsoluteLength::In(v) |
|
||||
AbsoluteLength::Cm(v) |
|
||||
AbsoluteLength::Mm(v) |
|
||||
AbsoluteLength::Q(v) |
|
||||
AbsoluteLength::Pt(v) |
|
||||
AbsoluteLength::Pc(v) => v < 0.,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this into a pixel value.
|
||||
#[inline]
|
||||
pub fn to_px(&self) -> CSSFloat {
|
||||
|
@ -484,6 +514,16 @@ impl Mul<CSSFloat> for NoCalcLength {
|
|||
}
|
||||
|
||||
impl NoCalcLength {
|
||||
/// Returns whether the value of this length without unit is less than zero.
|
||||
pub fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
NoCalcLength::Absolute(v) => v.is_negative(),
|
||||
NoCalcLength::FontRelative(v) => v.is_negative(),
|
||||
NoCalcLength::ViewportPercentage(v) => v.is_negative(),
|
||||
NoCalcLength::ServoCharacterWidth(c) => c.0 < 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a given absolute or relative dimension.
|
||||
pub fn parse_dimension(
|
||||
context: &ParserContext,
|
||||
|
@ -912,9 +952,10 @@ impl From<Percentage> for LengthPercentage {
|
|||
#[inline]
|
||||
fn from(pc: Percentage) -> Self {
|
||||
if pc.is_calc() {
|
||||
// FIXME(emilio): Hard-coding the clamping mode is suspect.
|
||||
LengthPercentage::Calc(Box::new(CalcLengthPercentage {
|
||||
percentage: Some(computed::Percentage(pc.get())),
|
||||
..Default::default()
|
||||
clamping_mode: AllowedNumericType::All,
|
||||
node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
|
||||
}))
|
||||
} else {
|
||||
LengthPercentage::Percentage(computed::Percentage(pc.get()))
|
||||
|
|
|
@ -55,8 +55,8 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
|
|||
pub use self::font::{FontVariantAlternates, FontWeight};
|
||||
pub use self::font::{FontVariantEastAsian, FontVariationSettings};
|
||||
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
|
||||
pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
|
||||
pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
|
||||
pub use self::image::{EndingShape as GradientEndingShape, Gradient};
|
||||
pub use self::image::{Image, MozImageRect};
|
||||
pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
|
||||
pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
|
||||
pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||
|
@ -80,6 +80,7 @@ pub use self::svg::MozContextProperties;
|
|||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
|
||||
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
||||
pub use self::svg_path::SVGPathData;
|
||||
pub use self::text::TextAlignLast;
|
||||
pub use self::text::TextUnderlinePosition;
|
||||
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
|
||||
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
|
||||
|
|
|
@ -120,9 +120,6 @@ impl Percentage {
|
|||
Token::Function(ref name) => {
|
||||
let function = CalcNode::math_function(name, location)?;
|
||||
let value = CalcNode::parse_percentage(context, input, function)?;
|
||||
|
||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||
// the clamping mode... I guess we could disallow it...
|
||||
Ok(Percentage {
|
||||
value,
|
||||
calc_clamping_mode: Some(num_context),
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::str::HTML_SPACE_CHARACTERS;
|
|||
use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
|
||||
use crate::values::computed::{Context, Percentage, ToComputedValue};
|
||||
use crate::values::generics::position::Position as GenericPosition;
|
||||
use crate::values::generics::position::PositionComponent as GenericPositionComponent;
|
||||
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
|
||||
use crate::values::generics::position::ZIndex as GenericZIndex;
|
||||
use crate::values::specified::{AllowQuirks, Integer, LengthPercentage};
|
||||
|
@ -262,6 +263,18 @@ impl<S: Parse> PositionComponent<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> GenericPositionComponent for PositionComponent<S> {
|
||||
fn is_center(&self) -> bool {
|
||||
match *self {
|
||||
PositionComponent::Center => true,
|
||||
PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5,
|
||||
// 50% from any side is still the center.
|
||||
PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> PositionComponent<S> {
|
||||
/// `0%`
|
||||
pub fn zero() -> Self {
|
||||
|
@ -295,10 +308,8 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
|
|||
},
|
||||
PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
|
||||
let length = length.to_computed_value(context);
|
||||
let p = Percentage(1. - length.percentage());
|
||||
let l = -length.unclamped_length();
|
||||
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
||||
ComputedLengthPercentage::new_calc(l, Some(p), AllowedNumericType::All)
|
||||
ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)
|
||||
},
|
||||
PositionComponent::Side(_, Some(ref length)) |
|
||||
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
||||
|
@ -350,66 +361,25 @@ impl Side for VerticalPositionKeyword {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
/// Auto-placement algorithm Option
|
||||
pub enum AutoFlow {
|
||||
/// The auto-placement algorithm places items by filling each row in turn,
|
||||
/// adding new rows as necessary.
|
||||
Row,
|
||||
/// The auto-placement algorithm places items by filling each column in turn,
|
||||
/// adding new columns as necessary.
|
||||
Column,
|
||||
}
|
||||
|
||||
/// If `dense` is specified, `row` is implied.
|
||||
fn is_row_dense(autoflow: &AutoFlow, dense: &bool) -> bool {
|
||||
*autoflow == AutoFlow::Row && *dense
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
/// Controls how the auto-placement algorithm works
|
||||
/// specifying exactly how auto-placed items get flowed into the grid
|
||||
pub struct GridAutoFlow {
|
||||
/// Specifiy how auto-placement algorithm fills each `row` or `column` in turn
|
||||
#[css(contextual_skip_if = "is_row_dense")]
|
||||
pub autoflow: AutoFlow,
|
||||
/// Specify use `dense` packing algorithm or not
|
||||
#[css(represents_keyword)]
|
||||
pub dense: bool,
|
||||
}
|
||||
|
||||
impl GridAutoFlow {
|
||||
#[inline]
|
||||
/// Get default `grid-auto-flow` as `row`
|
||||
pub fn row() -> GridAutoFlow {
|
||||
GridAutoFlow {
|
||||
autoflow: AutoFlow::Row,
|
||||
dense: false,
|
||||
}
|
||||
bitflags! {
|
||||
/// Controls how the auto-placement algorithm works
|
||||
/// specifying exactly how auto-placed items get flowed into the grid
|
||||
#[derive(
|
||||
MallocSizeOf,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem
|
||||
)]
|
||||
#[value_info(other_values = "row,column,dense")]
|
||||
#[repr(C)]
|
||||
pub struct GridAutoFlow: u8 {
|
||||
/// 'row' - mutually exclusive with 'column'
|
||||
const ROW = 1 << 0;
|
||||
/// 'column' - mutually exclusive with 'row'
|
||||
const COLUMN = 1 << 1;
|
||||
/// 'dense'
|
||||
const DENSE = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,26 +389,26 @@ impl Parse for GridAutoFlow {
|
|||
_context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<GridAutoFlow, ParseError<'i>> {
|
||||
let mut value = None;
|
||||
let mut dense = false;
|
||||
let mut track = None;
|
||||
let mut dense = GridAutoFlow::empty();
|
||||
|
||||
while !input.is_exhausted() {
|
||||
let location = input.current_source_location();
|
||||
let ident = input.expect_ident()?;
|
||||
let success = match_ignore_ascii_case! { &ident,
|
||||
"row" if value.is_none() => {
|
||||
value = Some(AutoFlow::Row);
|
||||
"row" if track.is_none() => {
|
||||
track = Some(GridAutoFlow::ROW);
|
||||
true
|
||||
},
|
||||
"column" if value.is_none() => {
|
||||
value = Some(AutoFlow::Column);
|
||||
"column" if track.is_none() => {
|
||||
track = Some(GridAutoFlow::COLUMN);
|
||||
true
|
||||
},
|
||||
"dense" if !dense => {
|
||||
dense = true;
|
||||
"dense" if dense.is_empty() => {
|
||||
dense = GridAutoFlow::DENSE;
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
_ => false,
|
||||
};
|
||||
if !success {
|
||||
return Err(location
|
||||
|
@ -446,47 +416,37 @@ impl Parse for GridAutoFlow {
|
|||
}
|
||||
}
|
||||
|
||||
if value.is_some() || dense {
|
||||
Ok(GridAutoFlow {
|
||||
autoflow: value.unwrap_or(AutoFlow::Row),
|
||||
dense: dense,
|
||||
})
|
||||
if track.is_some() || !dense.is_empty() {
|
||||
Ok(track.unwrap_or(GridAutoFlow::ROW) | dense)
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u8> for GridAutoFlow {
|
||||
fn from(bits: u8) -> GridAutoFlow {
|
||||
use crate::gecko_bindings::structs;
|
||||
|
||||
GridAutoFlow {
|
||||
autoflow: if bits & structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8 != 0 {
|
||||
AutoFlow::Row
|
||||
} else {
|
||||
AutoFlow::Column
|
||||
},
|
||||
dense: bits & structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8 != 0,
|
||||
impl ToCss for GridAutoFlow {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
if *self == GridAutoFlow::ROW {
|
||||
return dest.write_str("row");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<GridAutoFlow> for u8 {
|
||||
fn from(v: GridAutoFlow) -> u8 {
|
||||
use crate::gecko_bindings::structs;
|
||||
|
||||
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;
|
||||
if *self == GridAutoFlow::COLUMN {
|
||||
return dest.write_str("column");
|
||||
}
|
||||
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 {
|
||||
areas: areas.into(),
|
||||
strings: strings.into(),
|
||||
width: width,
|
||||
width,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -642,7 +602,16 @@ impl Parse for TemplateAreasArc {
|
|||
/// A range of rows or columns. Using this instead of std::ops::Range for FFI
|
||||
/// purposes.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub struct UnsignedRange {
|
||||
/// The start of the range.
|
||||
pub start: u32,
|
||||
|
@ -650,7 +619,16 @@ pub struct UnsignedRange {
|
|||
pub end: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(C)]
|
||||
/// Not associated with any particular grid item, but can be referenced from the
|
||||
/// grid-placement properties.
|
||||
|
|
|
@ -21,13 +21,13 @@ use style_traits::{StyleParseErrorKind, ToCss};
|
|||
pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;
|
||||
|
||||
/// <length> | <percentage> | <number> | context-value
|
||||
pub type SVGLength = generic::SVGLength<LengthPercentage>;
|
||||
pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
|
||||
|
||||
/// A non-negative version of SVGLength.
|
||||
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
|
||||
pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
|
||||
|
||||
/// [ <length> | <percentage> | <number> ]# | context-value
|
||||
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;
|
||||
pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
|
||||
|
||||
/// Whether the `context-value` value is enabled.
|
||||
#[cfg(feature = "gecko")]
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue