diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 019899ecde1..abcffe68e95 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -505,6 +505,7 @@ class Longhand(Property): "RubyPosition", "SVGOpacity", "SVGPaintOrder", + "ScrollbarGutter", "ScrollSnapAlign", "ScrollSnapAxis", "ScrollSnapStrictness", diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 3c3532bea7a..2bfd4015334 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -706,3 +706,13 @@ ${helpers.predefined_type( animation_value_type="Integer", spec="https://drafts.csswg.org/css-overflow-3/#line-clamp", )} + +${helpers.predefined_type( + "scrollbar-gutter", + "ScrollbarGutter", + "computed::ScrollbarGutter::AUTO", + engines="gecko", + gecko_pref="layout.css.scrollbar-gutter.enabled", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property", +)} diff --git a/components/style/values/computed/box.rs b/components/style/values/computed/box.rs index ce467343005..2364ac69045 100644 --- a/components/style/values/computed/box.rs +++ b/components/style/values/computed/box.rs @@ -15,7 +15,7 @@ pub use crate::values::specified::box_::{ AnimationName, AnimationTimeline, Appearance, BreakBetween, BreakWithin, Clear as SpecifiedClear, Contain, Display, Float as SpecifiedFloat, Overflow, OverflowAnchor, OverflowClipBox, OverscrollBehavior, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, - ScrollSnapType, TouchAction, TransitionProperty, WillChange, + ScrollSnapType, ScrollbarGutter, TouchAction, TransitionProperty, WillChange, }; /// A computed value for the `vertical-align` property. diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index ad7663804be..9beab014ff5 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -47,7 +47,7 @@ pub use self::border::{BorderImageSlice, BorderImageWidth}; pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain}; pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float}; pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty}; -pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; +pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType}; pub use self::box_::{TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme}; diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 67e90c0a8a2..1628b396730 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -2095,3 +2095,73 @@ impl Overflow { } } } + +bitflags! { + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] + #[value_info(other_values = "auto,stable,both-edges")] + #[repr(C)] + /// Values for scrollbar-gutter: + /// + pub struct ScrollbarGutter: u8 { + /// `auto` variant. Just for convenience if there is no flag set. + const AUTO = 0; + /// `stable` variant. + const STABLE = 1 << 0; + /// `both-edges` variant. + const BOTH_EDGES = 1 << 1; + } +} + +impl ToCss for ScrollbarGutter { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.is_empty() { + return dest.write_str("auto"); + } + + debug_assert!( + self.contains(ScrollbarGutter::STABLE), + "We failed to parse the syntax!" + ); + dest.write_str("stable")?; + if self.contains(ScrollbarGutter::BOTH_EDGES) { + dest.write_str(" both-edges")?; + } + + Ok(()) + } +} + +impl Parse for ScrollbarGutter { + /// auto | stable && both-edges? + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(ScrollbarGutter::AUTO); + } + + let mut result = ScrollbarGutter::empty(); + while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { + let flag = match_ignore_ascii_case! { &ident, + "stable" => Some(ScrollbarGutter::STABLE), + "both-edges" => Some(ScrollbarGutter::BOTH_EDGES), + _ => None + }; + + match flag { + Some(flag) if !result.contains(flag) => result.insert(flag), + _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + } + + if result.contains(ScrollbarGutter::STABLE) { + Ok(result) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + } +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 7cb34ca9a23..3c3c9b8d1ce 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -39,7 +39,7 @@ pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing, BorderStyle pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain, Display}; pub use self::box_::{Appearance, BreakBetween, BreakWithin}; pub use self::box_::{Clear, Float, Overflow, OverflowAnchor}; -pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; +pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType}; pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};