use crate::parser::{Parse, ParserContext};
use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
use crate::values::generics::box_::{
GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
VerticalAlignKeyword,
};
use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};
use crate::values::CustomIdent;
use cssparser::Parser;
use num_traits::FromPrimitive;
use std::fmt::{self, Debug, Write};
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
#[cfg(not(feature = "servo"))]
fn flexbox_enabled() -> bool {
true
}
#[cfg(not(feature = "servo"))]
fn grid_enabled() -> bool {
true
}
#[cfg(feature = "servo")]
fn flexbox_enabled() -> bool {
style_config::get_bool("layout.flexbox.enabled")
}
#[cfg(feature = "servo")]
fn grid_enabled() -> bool {
style_config::get_bool("layout.grid.enabled")
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayOutside {
None = 0,
Inline,
Block,
TableCaption,
InternalTable,
#[cfg(feature = "gecko")]
InternalRuby,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayInside {
None = 0,
Contents,
Flow,
FlowRoot,
Flex,
Grid,
Table,
TableRowGroup,
TableColumn,
TableColumnGroup,
TableHeaderGroup,
TableFooterGroup,
TableRow,
TableCell,
#[cfg(feature = "gecko")]
Ruby,
#[cfg(feature = "gecko")]
RubyBase,
#[cfg(feature = "gecko")]
RubyBaseContainer,
#[cfg(feature = "gecko")]
RubyText,
#[cfg(feature = "gecko")]
RubyTextContainer,
#[cfg(feature = "gecko")]
WebkitBox,
}
impl DisplayInside {
fn is_valid_for_list_item(self) -> bool {
match self {
DisplayInside::Flow => true,
#[cfg(feature = "gecko")]
DisplayInside::FlowRoot => true,
_ => false,
}
}
fn default_display_outside(self) -> DisplayOutside {
match self {
#[cfg(feature = "gecko")]
DisplayInside::Ruby => DisplayOutside::Inline,
_ => DisplayOutside::Block,
}
}
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
PartialEq,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct Display(u16);
#[allow(missing_docs)]
#[allow(non_upper_case_globals)]
impl Display {
pub const LIST_ITEM_MASK: u16 = 0b1000000000000000;
pub const OUTSIDE_MASK: u16 = 0b0111111100000000;
pub const INSIDE_MASK: u16 = 0b0000000011111111;
pub const OUTSIDE_SHIFT: u16 = 8;
pub const None: Self =
Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16);
pub const Contents: Self = Self(
((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16,
);
pub const Inline: Self =
Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
pub const InlineBlock: Self = Self(
((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
);
pub const Block: Self =
Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
#[cfg(feature = "gecko")]
pub const FlowRoot: Self = Self(
((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
);
pub const Flex: Self =
Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
pub const InlineFlex: Self =
Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
pub const Grid: Self =
Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
pub const InlineGrid: Self =
Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
pub const Table: Self =
Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16);
pub const InlineTable: Self = Self(
((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16,
);
pub const TableCaption: Self = Self(
((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16,
);
#[cfg(feature = "gecko")]
pub const Ruby: Self =
Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Ruby as u16);
#[cfg(feature = "gecko")]
pub const WebkitBox: Self = Self(
((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
);
#[cfg(feature = "gecko")]
pub const WebkitInlineBox: Self = Self(
((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
);
pub const TableRowGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableRowGroup as u16,
);
pub const TableHeaderGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableHeaderGroup as u16,
);
pub const TableFooterGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableFooterGroup as u16,
);
pub const TableColumn: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableColumn as u16,
);
pub const TableColumnGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableColumnGroup as u16,
);
pub const TableRow: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableRow as u16,
);
pub const TableCell: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableCell as u16,
);
#[cfg(feature = "gecko")]
pub const RubyBase: Self = Self(
((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::RubyBase as u16,
);
#[cfg(feature = "gecko")]
pub const RubyBaseContainer: Self = Self(
((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::RubyBaseContainer as u16,
);
#[cfg(feature = "gecko")]
pub const RubyText: Self = Self(
((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::RubyText as u16,
);
#[cfg(feature = "gecko")]
pub const RubyTextContainer: Self = Self(
((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::RubyTextContainer as u16,
);
#[inline]
const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
Self((outside as u16) << Self::OUTSIDE_SHIFT | inside as u16)
}
#[inline]
fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
let v = Self::new(outside, inside);
if !list_item {
return v;
}
Self(v.0 | Self::LIST_ITEM_MASK)
}
#[inline]
pub fn inside(&self) -> DisplayInside {
DisplayInside::from_u16(self.0 & Self::INSIDE_MASK).unwrap()
}
#[inline]
pub fn outside(&self) -> DisplayOutside {
DisplayOutside::from_u16((self.0 & Self::OUTSIDE_MASK) >> Self::OUTSIDE_SHIFT).unwrap()
}
#[inline]
pub const fn to_u16(&self) -> u16 {
self.0
}
#[inline]
pub fn is_inline_flow(&self) -> bool {
self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
}
#[inline]
pub const fn is_list_item(&self) -> bool {
(self.0 & Self::LIST_ITEM_MASK) != 0
}
pub fn is_ruby_level_container(&self) -> bool {
match *self {
#[cfg(feature = "gecko")]
Display::RubyBaseContainer | Display::RubyTextContainer => true,
_ => false,
}
}
pub fn is_ruby_type(&self) -> bool {
match self.inside() {
#[cfg(feature = "gecko")]
DisplayInside::Ruby |
DisplayInside::RubyBase |
DisplayInside::RubyText |
DisplayInside::RubyBaseContainer |
DisplayInside::RubyTextContainer => true,
_ => false,
}
}
}
impl Display {
#[inline]
pub fn inline() -> Self {
Display::Inline
}
#[cfg(feature = "servo")]
#[inline]
pub fn is_atomic_inline_level(&self) -> bool {
match *self {
Display::InlineBlock | Display::InlineFlex => true,
Display::InlineTable => true,
_ => false,
}
}
pub fn is_item_container(&self) -> bool {
match self.inside() {
DisplayInside::Flex => true,
DisplayInside::Grid => true,
_ => false,
}
}
pub fn is_line_participant(&self) -> bool {
if self.is_inline_flow() {
return true;
}
match *self {
#[cfg(feature = "gecko")]
Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
_ => false,
}
}
pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
{
if _is_root_element && (self.is_contents() || self.is_list_item()) {
return Display::Block;
}
}
match self.outside() {
DisplayOutside::Inline => {
let inside = match self.inside() {
DisplayInside::FlowRoot => DisplayInside::Flow,
inside => inside,
};
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
},
DisplayOutside::Block | DisplayOutside::None => *self,
_ => Display::Block,
}
}
#[cfg(feature = "gecko")]
pub fn inlinify(&self) -> Self {
match self.outside() {
DisplayOutside::Block => {
let inside = match self.inside() {
DisplayInside::Flow => DisplayInside::FlowRoot,
inside => inside,
};
Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
},
_ => *self,
}
}
#[inline]
pub fn is_contents(&self) -> bool {
match *self {
Display::Contents => true,
_ => false,
}
}
#[inline]
pub fn is_none(&self) -> bool {
*self == Display::None
}
}
enum DisplayKeyword {
Full(Display),
Inside(DisplayInside),
Outside(DisplayOutside),
ListItem,
}
impl DisplayKeyword {
fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
use self::DisplayKeyword::*;
Ok(try_match_ident_ignore_ascii_case! { input,
"none" => Full(Display::None),
"contents" => Full(Display::Contents),
"inline-block" => Full(Display::InlineBlock),
"inline-table" => Full(Display::InlineTable),
"-webkit-flex" if flexbox_enabled() => Full(Display::Flex),
"inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Full(Display::InlineFlex),
"inline-grid" if grid_enabled() => Full(Display::InlineGrid),
"table-caption" => Full(Display::TableCaption),
"table-row-group" => Full(Display::TableRowGroup),
"table-header-group" => Full(Display::TableHeaderGroup),
"table-footer-group" => Full(Display::TableFooterGroup),
"table-column" => Full(Display::TableColumn),
"table-column-group" => Full(Display::TableColumnGroup),
"table-row" => Full(Display::TableRow),
"table-cell" => Full(Display::TableCell),
#[cfg(feature = "gecko")]
"ruby-base" => Full(Display::RubyBase),
#[cfg(feature = "gecko")]
"ruby-base-container" => Full(Display::RubyBaseContainer),
#[cfg(feature = "gecko")]
"ruby-text" => Full(Display::RubyText),
#[cfg(feature = "gecko")]
"ruby-text-container" => Full(Display::RubyTextContainer),
#[cfg(feature = "gecko")]
"-webkit-box" => Full(Display::WebkitBox),
#[cfg(feature = "gecko")]
"-webkit-inline-box" => Full(Display::WebkitInlineBox),
"block" => Outside(DisplayOutside::Block),
"inline" => Outside(DisplayOutside::Inline),
"list-item" => ListItem,
"flow" => Inside(DisplayInside::Flow),
"flex" if flexbox_enabled() => Inside(DisplayInside::Flex),
"flow-root" => Inside(DisplayInside::FlowRoot),
"table" => Inside(DisplayInside::Table),
"grid" if grid_enabled() => Inside(DisplayInside::Grid),
#[cfg(feature = "gecko")]
"ruby" => Inside(DisplayInside::Ruby),
})
}
}
impl ToCss for Display {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let outside = self.outside();
let inside = self.inside();
match *self {
Display::Block | Display::Inline => outside.to_css(dest),
Display::InlineBlock => dest.write_str("inline-block"),
#[cfg(feature = "gecko")]
Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
Display::TableCaption => dest.write_str("table-caption"),
_ => match (outside, inside) {
(DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
(DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
(DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
#[cfg(feature = "gecko")]
(DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
(_, inside) => {
if self.is_list_item() {
if outside != DisplayOutside::Block {
outside.to_css(dest)?;
dest.write_char(' ')?;
}
if inside != DisplayInside::Flow {
inside.to_css(dest)?;
dest.write_char(' ')?;
}
dest.write_str("list-item")
} else {
inside.to_css(dest)
}
},
},
}
}
}
impl Parse for Display {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Display, ParseError<'i>> {
let mut got_list_item = false;
let mut inside = None;
let mut outside = None;
match DisplayKeyword::parse(input)? {
DisplayKeyword::Full(d) => return Ok(d),
DisplayKeyword::Outside(o) => {
outside = Some(o);
},
DisplayKeyword::Inside(i) => {
inside = Some(i);
},
DisplayKeyword::ListItem => {
got_list_item = true;
},
};
while let Ok(kw) = input.try_parse(DisplayKeyword::parse) {
match kw {
DisplayKeyword::ListItem if !got_list_item => {
got_list_item = true;
},
DisplayKeyword::Outside(o) if outside.is_none() => {
outside = Some(o);
},
DisplayKeyword::Inside(i) if inside.is_none() => {
inside = Some(i);
},
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
}
let inside = inside.unwrap_or(DisplayInside::Flow);
let outside = outside.unwrap_or_else(|| inside.default_display_outside());
if got_list_item && !inside.is_valid_for_list_item() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
return Ok(Display::from3(outside, inside, got_list_item));
}
}
impl SpecifiedValueInfo for Display {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&[
"block",
"contents",
"flex",
"flow-root",
"flow-root list-item",
"grid",
"inline",
"inline-block",
"inline-flex",
"inline-grid",
"inline-table",
"inline list-item",
"inline flow-root list-item",
"list-item",
"none",
"block ruby",
"ruby",
"ruby-base",
"ruby-base-container",
"ruby-text",
"ruby-text-container",
"table",
"table-caption",
"table-cell",
"table-column",
"table-column-group",
"table-footer-group",
"table-header-group",
"table-row",
"table-row-group",
"-webkit-box",
"-webkit-inline-box",
]);
}
}
pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
pub type LineClamp = GenericLineClamp<Integer>;
pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
impl Parse for VerticalAlign {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(lp) =
input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
{
return Ok(GenericVerticalAlign::Length(lp));
}
Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(
input,
)?))
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToShmem,
ToComputedValue,
ToResolvedValue,
)]
#[repr(u8)]
pub enum BaselineSource {
Auto,
First,
Last,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ScrollSnapAxis {
X,
Y,
Block,
Inline,
Both,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ScrollSnapStrictness {
#[css(skip)]
None, Mandatory,
Proximity,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct ScrollSnapType {
axis: ScrollSnapAxis,
strictness: ScrollSnapStrictness,
}
impl ScrollSnapType {
#[inline]
pub fn none() -> Self {
Self {
axis: ScrollSnapAxis::Both,
strictness: ScrollSnapStrictness::None,
}
}
}
impl Parse for ScrollSnapType {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
return Ok(ScrollSnapType::none());
}
let axis = ScrollSnapAxis::parse(input)?;
let strictness = input
.try_parse(ScrollSnapStrictness::parse)
.unwrap_or(ScrollSnapStrictness::Proximity);
Ok(Self { axis, strictness })
}
}
impl ToCss for ScrollSnapType {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if self.strictness == ScrollSnapStrictness::None {
return dest.write_str("none");
}
self.axis.to_css(dest)?;
if self.strictness != ScrollSnapStrictness::Proximity {
dest.write_char(' ')?;
self.strictness.to_css(dest)?;
}
Ok(())
}
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ScrollSnapAlignKeyword {
None,
Start,
End,
Center,
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct ScrollSnapAlign {
block: ScrollSnapAlignKeyword,
inline: ScrollSnapAlignKeyword,
}
impl ScrollSnapAlign {
#[inline]
pub fn none() -> Self {
ScrollSnapAlign {
block: ScrollSnapAlignKeyword::None,
inline: ScrollSnapAlignKeyword::None,
}
}
}
impl Parse for ScrollSnapAlign {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<ScrollSnapAlign, ParseError<'i>> {
let block = ScrollSnapAlignKeyword::parse(input)?;
let inline = input
.try_parse(ScrollSnapAlignKeyword::parse)
.unwrap_or(block);
Ok(ScrollSnapAlign { block, inline })
}
}
impl ToCss for ScrollSnapAlign {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.block.to_css(dest)?;
if self.block != self.inline {
dest.write_char(' ')?;
self.inline.to_css(dest)?;
}
Ok(())
}
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ScrollSnapStop {
Normal,
Always,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum OverscrollBehavior {
Auto,
Contain,
None,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum OverflowAnchor {
Auto,
None,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum OverflowClipBox {
PaddingBox,
ContentBox,
}
#[derive(
Clone,
Debug,
Default,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(comma)]
#[repr(C)]
pub struct WillChange {
#[css(iterable, if_empty = "auto")]
features: crate::OwnedSlice<CustomIdent>,
#[css(skip)]
bits: WillChangeBits,
}
impl WillChange {
#[inline]
pub fn auto() -> Self {
Self::default()
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct WillChangeBits(u16);
bitflags! {
impl WillChangeBits: u16 {
const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
const TRANSFORM = 1 << 1;
const SCROLL = 1 << 2;
const CONTAIN = 1 << 3;
const OPACITY = 1 << 4;
const PERSPECTIVE = 1 << 5;
const Z_INDEX = 1 << 6;
const FIXPOS_CB_NON_SVG = 1 << 7;
const POSITION = 1 << 8;
}
}
#[cfg(feature="servo")]
fn change_bits_for_longhand(_longhand: LonghandId) -> WillChangeBits { WillChangeBits::empty() }
#[cfg(feature = "gecko")]
fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
match longhand {
LonghandId::Opacity => WillChangeBits::OPACITY,
LonghandId::Contain => WillChangeBits::CONTAIN,
LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
LonghandId::Position => {
WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
},
LonghandId::ZIndex => WillChangeBits::Z_INDEX,
LonghandId::Transform |
LonghandId::TransformStyle |
LonghandId::Translate |
LonghandId::Rotate |
LonghandId::Scale |
LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
LonghandId::BackdropFilter | LonghandId::Filter => {
WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::FIXPOS_CB_NON_SVG
},
LonghandId::MixBlendMode |
LonghandId::Isolation |
LonghandId::MaskImage |
LonghandId::ClipPath => WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL,
_ => WillChangeBits::empty(),
}
}
fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
Ok(id) => id,
Err(..) => return WillChangeBits::empty(),
};
match id.as_shorthand() {
Ok(shorthand) => shorthand
.longhands()
.fold(WillChangeBits::empty(), |flags, p| {
flags | change_bits_for_longhand(p)
}),
Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
}
}
impl Parse for WillChange {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input
.try_parse(|input| input.expect_ident_matching("auto"))
.is_ok()
{
return Ok(Self::default());
}
let mut bits = WillChangeBits::empty();
let custom_idents = input.parse_comma_separated(|i| {
let location = i.current_source_location();
let parser_ident = i.expect_ident()?;
let ident = CustomIdent::from_ident(
location,
parser_ident,
&["will-change", "none", "all", "auto"],
)?;
if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
} else if ident.0 == atom!("scroll-position") {
bits |= WillChangeBits::SCROLL;
} else {
bits |= change_bits_for_maybe_property(&parser_ident, context);
}
Ok(ident)
})?;
Ok(Self {
features: custom_idents.into(),
bits,
})
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
#[repr(C)]
pub struct TouchAction(u8);
bitflags! {
impl TouchAction: u8 {
const NONE = 1 << 0;
const AUTO = 1 << 1;
const PAN_X = 1 << 2;
const PAN_Y = 1 << 3;
const MANIPULATION = 1 << 4;
const PINCH_ZOOM = 1 << 5;
}
}
impl TouchAction {
#[inline]
pub fn auto() -> TouchAction {
TouchAction::AUTO
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(bitflags(
single = "none,strict,content",
mixed = "size,layout,style,paint,inline-size",
overlapping_bits
))]
#[repr(C)]
pub struct Contain(u8);
bitflags! {
impl Contain: u8 {
const NONE = 0;
const INLINE_SIZE = 1 << 0;
const BLOCK_SIZE = 1 << 1;
const LAYOUT = 1 << 2;
const STYLE = 1 << 3;
const PAINT = 1 << 4;
const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
}
}
impl Parse for ContainIntrinsicSize {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
return Ok(Self::Length(l));
}
if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(Self::AutoNone);
}
let l = NonNegativeLength::parse(context, input)?;
return Ok(Self::AutoLength(l));
}
input.expect_ident_matching("none")?;
Ok(Self::None)
}
}
impl Parse for LineClamp {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(i) =
input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
{
return Ok(Self(i.0));
}
input.expect_ident_matching("none")?;
Ok(Self::none())
}
}
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ContentVisibility {
Auto,
Hidden,
Visible,
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
MallocSizeOf,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
Parse,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum ContainerType {
Normal,
InlineSize,
Size,
}
impl ContainerType {
pub fn is_normal(self) -> bool {
self == Self::Normal
}
pub fn is_size_container_type(self) -> bool {
!self.is_normal()
}
}
#[repr(transparent)]
#[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
impl ContainerName {
pub fn none() -> Self {
Self(Default::default())
}
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
fn parse_internal<'i>(
input: &mut Parser<'i, '_>,
for_query: bool,
) -> Result<Self, ParseError<'i>> {
let mut idents = vec![];
let location = input.current_source_location();
let first = input.expect_ident()?;
if !for_query && first.eq_ignore_ascii_case("none") {
return Ok(Self::none());
}
const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
idents.push(CustomIdent::from_ident(
location,
first,
DISALLOWED_CONTAINER_NAMES,
)?);
if !for_query {
while let Ok(name) =
input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
{
idents.push(name);
}
}
Ok(ContainerName(idents.into()))
}
pub fn parse_for_query<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Self::parse_internal(input, true)
}
}
impl Parse for ContainerName {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Self::parse_internal(input, false)
}
}
pub type Perspective = GenericPerspective<NonNegativeLength>;
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)]
pub enum Float {
Left,
Right,
None,
InlineStart,
InlineEnd,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)]
pub enum Clear {
None,
Left,
Right,
Both,
InlineStart,
InlineEnd,
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)]
pub enum Resize {
None,
Both,
Horizontal,
Vertical,
Inline,
Block,
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum Appearance {
None,
Auto,
Searchfield,
Textarea,
Checkbox,
Radio,
Menulist,
Listbox,
Meter,
ProgressBar,
Button,
Textfield,
MenulistButton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowDown,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowNext,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowPrevious,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowUp,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Dualbutton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Menupopup,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Meterchunk,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozMenulistArrowButton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
NumberInput,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
PasswordInput,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Progresschunk,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Range,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
RangeThumb,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarHorizontal,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarVertical,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarbuttonUp,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarbuttonDown,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarbuttonLeft,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarbuttonRight,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarthumbHorizontal,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ScrollbarthumbVertical,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Scrollcorner,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Separator,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Spinner,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
SpinnerUpbutton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
SpinnerDownbutton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
SpinnerTextfield,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Splitter,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Statusbar,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tab,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tabpanel,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tabpanels,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
TabScrollArrowBack,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
TabScrollArrowForward,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Toolbarbutton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ToolbarbuttonDropdown,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tooltip,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozSidebar,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozMacHelpButton,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozMacWindow,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowButtonBox,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowButtonClose,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowButtonMaximize,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowButtonMinimize,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowButtonRestore,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowTitlebar,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowTitlebarMaximized,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozWindowDecorations,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozMacDisclosureButtonClosed,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
MozMacDisclosureButtonOpen,
#[css(skip)]
FocusOutline,
#[css(skip)]
Count,
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum BreakBetween {
Always,
Auto,
Page,
Avoid,
Left,
Right,
}
impl BreakBetween {
#[cfg_attr(feature = "servo", allow(unused))]
#[inline]
pub(crate) fn parse_legacy<'i>(
_: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Self, ParseError<'i>> {
let break_value = BreakBetween::parse(input)?;
match break_value {
BreakBetween::Always => Ok(BreakBetween::Page),
BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
Ok(break_value)
},
BreakBetween::Page => {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
},
}
}
#[cfg_attr(feature = "servo", allow(unused))]
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
self.to_css(dest)
},
BreakBetween::Page => dest.write_str("always"),
BreakBetween::Always => Ok(()),
}
}
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum BreakWithin {
Auto,
Avoid,
AvoidPage,
AvoidColumn,
}
impl BreakWithin {
#[cfg_attr(feature = "servo", allow(unused))]
#[inline]
pub(crate) fn parse_legacy<'i>(
_: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Self, ParseError<'i>> {
let break_value = BreakWithin::parse(input)?;
match break_value {
BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
},
}
}
#[cfg_attr(feature = "servo", allow(unused))]
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
}
}
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum Overflow {
Visible,
Hidden,
Scroll,
Auto,
#[cfg(feature = "gecko")]
Clip,
}
impl Parse for Overflow {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"visible" => Self::Visible,
"hidden" => Self::Hidden,
"scroll" => Self::Scroll,
"auto" | "overlay" => Self::Auto,
#[cfg(feature = "gecko")]
"clip" => Self::Clip,
#[cfg(feature = "gecko")]
"-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
Overflow::Clip
},
})
}
}
impl Overflow {
#[inline]
pub fn is_scrollable(&self) -> bool {
matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
}
#[inline]
pub fn to_scrollable(&self) -> Self {
match *self {
Self::Hidden | Self::Scroll | Self::Auto => *self,
Self::Visible => Self::Auto,
#[cfg(feature = "gecko")]
Self::Clip => Self::Hidden,
}
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
#[css(bitflags(
single = "auto",
mixed = "stable,both-edges",
validate_mixed = "Self::has_stable"
))]
pub struct ScrollbarGutter(u8);
bitflags! {
impl ScrollbarGutter: u8 {
const AUTO = 0;
const STABLE = 1 << 0;
const BOTH_EDGES = 1 << 1;
}
}
impl ScrollbarGutter {
#[inline]
fn has_stable(&self) -> bool {
self.intersects(Self::STABLE)
}
}
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem,
)]
#[allow(missing_docs)]
pub enum Zoom {
Normal,
#[parse(condition = "ParserContext::in_ua_sheet")]
Document,
Value(NonNegativeNumberOrPercentage),
}
impl Zoom {
#[inline]
pub fn new_number(n: f32) -> Self {
Self::Value(NonNegativeNumberOrPercentage::new_number(n))
}
}