1use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use cssparser::Parser;
12use std::fmt::{self, Write};
13use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, ToCss};
14
15#[derive(
17 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
18)]
19#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
20#[repr(C)]
21pub struct AlignFlags(u8);
22bitflags! {
23 impl AlignFlags: u8 {
24 const AUTO = 0;
27 const NORMAL = 1;
29 const START = 2;
31 const END = 3;
33 const FLEX_START = 4;
35 const FLEX_END = 5;
37 const CENTER = 6;
39 const LEFT = 7;
41 const RIGHT = 8;
43 const BASELINE = 9;
45 const LAST_BASELINE = 10;
47 const STRETCH = 11;
49 const SELF_START = 12;
51 const SELF_END = 13;
53 const SPACE_BETWEEN = 14;
55 const SPACE_AROUND = 15;
57 const SPACE_EVENLY = 16;
59 const ANCHOR_CENTER = 17;
61
62 const LEGACY = 1 << 5;
65 const SAFE = 1 << 6;
67 const UNSAFE = 1 << 7;
69
70 const FLAG_BITS = 0b11100000;
72 }
73}
74
75impl AlignFlags {
76 #[inline]
78 pub fn value(&self) -> Self {
79 *self & !AlignFlags::FLAG_BITS
80 }
81
82 #[inline]
84 pub fn with_value(&self, value: AlignFlags) -> Self {
85 debug_assert!(!value.intersects(Self::FLAG_BITS));
86 value | self.flags()
87 }
88
89 #[inline]
91 pub fn flags(&self) -> Self {
92 *self & AlignFlags::FLAG_BITS
93 }
94}
95
96impl ToCss for AlignFlags {
97 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
98 where
99 W: Write,
100 {
101 let flags = self.flags();
102 let value = self.value();
103 match flags {
104 AlignFlags::LEGACY => {
105 dest.write_str("legacy")?;
106 if value.is_empty() {
107 return Ok(());
108 }
109 dest.write_char(' ')?;
110 },
111 AlignFlags::SAFE => dest.write_str("safe ")?,
112 AlignFlags::UNSAFE => dest.write_str("unsafe ")?,
113 _ => {
114 debug_assert_eq!(flags, AlignFlags::empty());
115 },
116 }
117
118 dest.write_str(match value {
119 AlignFlags::AUTO => "auto",
120 AlignFlags::NORMAL => "normal",
121 AlignFlags::START => "start",
122 AlignFlags::END => "end",
123 AlignFlags::FLEX_START => "flex-start",
124 AlignFlags::FLEX_END => "flex-end",
125 AlignFlags::CENTER => "center",
126 AlignFlags::LEFT => "left",
127 AlignFlags::RIGHT => "right",
128 AlignFlags::BASELINE => "baseline",
129 AlignFlags::LAST_BASELINE => "last baseline",
130 AlignFlags::STRETCH => "stretch",
131 AlignFlags::SELF_START => "self-start",
132 AlignFlags::SELF_END => "self-end",
133 AlignFlags::SPACE_BETWEEN => "space-between",
134 AlignFlags::SPACE_AROUND => "space-around",
135 AlignFlags::SPACE_EVENLY => "space-evenly",
136 AlignFlags::ANCHOR_CENTER => "anchor-center",
137 _ => unreachable!(),
138 })
139 }
140}
141
142#[derive(Clone, Copy, PartialEq)]
145pub enum AxisDirection {
146 Block,
148 Inline,
150}
151
152#[derive(
157 Clone,
158 Copy,
159 Debug,
160 Eq,
161 MallocSizeOf,
162 PartialEq,
163 ToComputedValue,
164 ToCss,
165 ToResolvedValue,
166 ToShmem,
167 ToTyped,
168)]
169#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
170#[repr(C)]
171#[typed(todo_derive_fields)]
172pub struct ContentDistribution {
173 primary: AlignFlags,
174 }
177
178impl ContentDistribution {
179 #[inline]
181 pub fn normal() -> Self {
182 Self::new(AlignFlags::NORMAL)
183 }
184
185 #[inline]
187 pub fn start() -> Self {
188 Self::new(AlignFlags::START)
189 }
190
191 #[inline]
193 pub fn new(primary: AlignFlags) -> Self {
194 Self { primary }
195 }
196
197 pub fn is_baseline_position(&self) -> bool {
199 matches!(
200 self.primary.value(),
201 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
202 )
203 }
204
205 #[inline]
207 pub fn primary(self) -> AlignFlags {
208 self.primary
209 }
210
211 pub fn parse_block<'i>(
213 _: &ParserContext,
214 input: &mut Parser<'i, '_>,
215 ) -> Result<Self, ParseError<'i>> {
216 Self::parse(input, AxisDirection::Block)
217 }
218
219 pub fn parse_inline<'i>(
221 _: &ParserContext,
222 input: &mut Parser<'i, '_>,
223 ) -> Result<Self, ParseError<'i>> {
224 Self::parse(input, AxisDirection::Inline)
225 }
226
227 fn parse<'i, 't>(
228 input: &mut Parser<'i, 't>,
229 axis: AxisDirection,
230 ) -> Result<Self, ParseError<'i>> {
231 if input
236 .try_parse(|i| i.expect_ident_matching("normal"))
237 .is_ok()
238 {
239 return Ok(ContentDistribution::normal());
240 }
241
242 if axis == AxisDirection::Block {
244 if let Ok(value) = input.try_parse(parse_baseline) {
245 return Ok(ContentDistribution::new(value));
246 }
247 }
248
249 if let Ok(value) = input.try_parse(parse_content_distribution) {
251 return Ok(ContentDistribution::new(value));
252 }
253
254 let overflow_position = input
256 .try_parse(parse_overflow_position)
257 .unwrap_or(AlignFlags::empty());
258
259 let content_position = try_match_ident_ignore_ascii_case! { input,
260 "start" => AlignFlags::START,
261 "end" => AlignFlags::END,
262 "flex-start" => AlignFlags::FLEX_START,
263 "flex-end" => AlignFlags::FLEX_END,
264 "center" => AlignFlags::CENTER,
265 "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
266 "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
267 };
268
269 Ok(ContentDistribution::new(
270 content_position | overflow_position,
271 ))
272 }
273}
274
275impl SpecifiedValueInfo for ContentDistribution {
276 fn collect_completion_keywords(f: KeywordsCollectFn) {
277 f(&["normal"]);
278 list_baseline_keywords(f); list_content_distribution_keywords(f);
280 list_overflow_position_keywords(f);
281 f(&["start", "end", "flex-start", "flex-end", "center"]);
282 f(&["left", "right"]); }
284}
285
286#[derive(
291 Clone,
292 Copy,
293 Debug,
294 Deref,
295 Eq,
296 MallocSizeOf,
297 PartialEq,
298 ToComputedValue,
299 ToCss,
300 ToResolvedValue,
301 ToShmem,
302 ToTyped,
303)]
304#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
305#[repr(C)]
306#[typed(todo_derive_fields)]
307pub struct SelfAlignment(pub AlignFlags);
308
309impl SelfAlignment {
310 #[inline]
312 pub fn auto() -> Self {
313 SelfAlignment(AlignFlags::AUTO)
314 }
315
316 pub fn is_valid_on_both_axes(&self) -> bool {
318 match self.0.value() {
319 AlignFlags::LEFT | AlignFlags::RIGHT => false,
321
322 _ => true,
323 }
324 }
325
326 pub fn parse_block<'i, 't>(
328 _: &ParserContext,
329 input: &mut Parser<'i, 't>,
330 ) -> Result<Self, ParseError<'i>> {
331 Self::parse(input, AxisDirection::Block)
332 }
333
334 pub fn parse_inline<'i, 't>(
336 _: &ParserContext,
337 input: &mut Parser<'i, 't>,
338 ) -> Result<Self, ParseError<'i>> {
339 Self::parse(input, AxisDirection::Inline)
340 }
341
342 fn parse<'i, 't>(
344 input: &mut Parser<'i, 't>,
345 axis: AxisDirection,
346 ) -> Result<Self, ParseError<'i>> {
347 if let Ok(value) = input.try_parse(parse_baseline) {
355 return Ok(SelfAlignment(value));
356 }
357
358 if let Ok(value) = input.try_parse(parse_auto_normal_stretch) {
360 return Ok(SelfAlignment(value));
361 }
362
363 let overflow_position = input
365 .try_parse(parse_overflow_position)
366 .unwrap_or(AlignFlags::empty());
367 let self_position = parse_self_position(input, axis)?;
368 Ok(SelfAlignment(overflow_position | self_position))
369 }
370
371 fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
372 list_baseline_keywords(f);
373 list_auto_normal_stretch(f);
374 list_overflow_position_keywords(f);
375 list_self_position_keywords(f, axis);
376 }
377
378 pub fn flip_position(self) -> Self {
381 let flipped_value = match self.0.value() {
382 AlignFlags::START => AlignFlags::END,
383 AlignFlags::END => AlignFlags::START,
384 AlignFlags::FLEX_START => AlignFlags::FLEX_END,
385 AlignFlags::FLEX_END => AlignFlags::FLEX_START,
386 AlignFlags::LEFT => AlignFlags::RIGHT,
387 AlignFlags::RIGHT => AlignFlags::LEFT,
388 AlignFlags::SELF_START => AlignFlags::SELF_END,
389 AlignFlags::SELF_END => AlignFlags::SELF_START,
390
391 AlignFlags::AUTO
392 | AlignFlags::NORMAL
393 | AlignFlags::BASELINE
394 | AlignFlags::LAST_BASELINE
395 | AlignFlags::STRETCH
396 | AlignFlags::CENTER
397 | AlignFlags::SPACE_BETWEEN
398 | AlignFlags::SPACE_AROUND
399 | AlignFlags::SPACE_EVENLY
400 | AlignFlags::ANCHOR_CENTER => return self,
401 _ => {
402 debug_assert!(false, "Unexpected alignment enumeration value");
403 return self;
404 },
405 };
406 self.with_value(flipped_value)
407 }
408
409 #[inline]
411 pub fn with_value(self, value: AlignFlags) -> Self {
412 Self(self.0.with_value(value))
413 }
414}
415
416impl SpecifiedValueInfo for SelfAlignment {
417 fn collect_completion_keywords(f: KeywordsCollectFn) {
418 Self::list_keywords(f, AxisDirection::Block);
421 }
422}
423
424#[derive(
429 Clone,
430 Copy,
431 Debug,
432 Deref,
433 Eq,
434 MallocSizeOf,
435 PartialEq,
436 ToComputedValue,
437 ToCss,
438 ToResolvedValue,
439 ToShmem,
440 ToTyped,
441)]
442#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
443#[repr(C)]
444#[typed(todo_derive_fields)]
445pub struct ItemPlacement(pub AlignFlags);
446
447impl ItemPlacement {
448 #[inline]
450 pub fn normal() -> Self {
451 Self(AlignFlags::NORMAL)
452 }
453}
454
455impl ItemPlacement {
456 pub fn parse_block<'i>(
458 _: &ParserContext,
459 input: &mut Parser<'i, '_>,
460 ) -> Result<Self, ParseError<'i>> {
461 Self::parse(input, AxisDirection::Block)
462 }
463
464 pub fn parse_inline<'i>(
466 _: &ParserContext,
467 input: &mut Parser<'i, '_>,
468 ) -> Result<Self, ParseError<'i>> {
469 Self::parse(input, AxisDirection::Inline)
470 }
471
472 fn parse<'i, 't>(
473 input: &mut Parser<'i, 't>,
474 axis: AxisDirection,
475 ) -> Result<Self, ParseError<'i>> {
476 if let Ok(baseline) = input.try_parse(parse_baseline) {
481 return Ok(Self(baseline));
482 }
483
484 if let Ok(value) = input.try_parse(parse_normal_stretch) {
486 return Ok(Self(value));
487 }
488
489 if axis == AxisDirection::Inline {
490 if let Ok(value) = input.try_parse(parse_legacy) {
492 return Ok(Self(value));
493 }
494 }
495
496 let overflow = input
498 .try_parse(parse_overflow_position)
499 .unwrap_or(AlignFlags::empty());
500 let self_position = parse_self_position(input, axis)?;
501 Ok(ItemPlacement(self_position | overflow))
502 }
503}
504
505impl SpecifiedValueInfo for ItemPlacement {
506 fn collect_completion_keywords(f: KeywordsCollectFn) {
507 list_baseline_keywords(f);
508 list_normal_stretch(f);
509 list_overflow_position_keywords(f);
510 list_self_position_keywords(f, AxisDirection::Block);
511 }
512}
513
514#[derive(
518 Clone, Copy, Debug, Deref, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem, ToTyped,
519)]
520#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
521#[repr(C)]
522pub struct JustifyItems(pub ItemPlacement);
523
524impl JustifyItems {
525 #[inline]
527 pub fn legacy() -> Self {
528 Self(ItemPlacement(AlignFlags::LEGACY))
529 }
530
531 #[inline]
533 pub fn normal() -> Self {
534 Self(ItemPlacement::normal())
535 }
536}
537
538impl Parse for JustifyItems {
539 fn parse<'i, 't>(
540 context: &ParserContext,
541 input: &mut Parser<'i, 't>,
542 ) -> Result<Self, ParseError<'i>> {
543 ItemPlacement::parse_inline(context, input).map(Self)
544 }
545}
546
547impl SpecifiedValueInfo for JustifyItems {
548 fn collect_completion_keywords(f: KeywordsCollectFn) {
549 ItemPlacement::collect_completion_keywords(f);
550 list_legacy_keywords(f); }
552}
553
554fn parse_auto_normal_stretch<'i, 't>(
556 input: &mut Parser<'i, 't>,
557) -> Result<AlignFlags, ParseError<'i>> {
558 try_match_ident_ignore_ascii_case! { input,
561 "auto" => Ok(AlignFlags::AUTO),
562 "normal" => Ok(AlignFlags::NORMAL),
563 "stretch" => Ok(AlignFlags::STRETCH),
564 }
565}
566
567fn list_auto_normal_stretch(f: KeywordsCollectFn) {
568 f(&["auto", "normal", "stretch"]);
569}
570
571fn parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
573 try_match_ident_ignore_ascii_case! { input,
576 "normal" => Ok(AlignFlags::NORMAL),
577 "stretch" => Ok(AlignFlags::STRETCH),
578 }
579}
580
581fn list_normal_stretch(f: KeywordsCollectFn) {
582 f(&["normal", "stretch"]);
583}
584
585fn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
587 try_match_ident_ignore_ascii_case! { input,
590 "baseline" => Ok(AlignFlags::BASELINE),
591 "first" => {
592 input.expect_ident_matching("baseline")?;
593 Ok(AlignFlags::BASELINE)
594 },
595 "last" => {
596 input.expect_ident_matching("baseline")?;
597 Ok(AlignFlags::LAST_BASELINE)
598 },
599 }
600}
601
602fn list_baseline_keywords(f: KeywordsCollectFn) {
603 f(&["baseline", "first baseline", "last baseline"]);
604}
605
606fn parse_content_distribution<'i, 't>(
608 input: &mut Parser<'i, 't>,
609) -> Result<AlignFlags, ParseError<'i>> {
610 try_match_ident_ignore_ascii_case! { input,
613 "stretch" => Ok(AlignFlags::STRETCH),
614 "space-between" => Ok(AlignFlags::SPACE_BETWEEN),
615 "space-around" => Ok(AlignFlags::SPACE_AROUND),
616 "space-evenly" => Ok(AlignFlags::SPACE_EVENLY),
617 }
618}
619
620fn list_content_distribution_keywords(f: KeywordsCollectFn) {
621 f(&["stretch", "space-between", "space-around", "space-evenly"]);
622}
623
624fn parse_overflow_position<'i, 't>(
626 input: &mut Parser<'i, 't>,
627) -> Result<AlignFlags, ParseError<'i>> {
628 try_match_ident_ignore_ascii_case! { input,
631 "safe" => Ok(AlignFlags::SAFE),
632 "unsafe" => Ok(AlignFlags::UNSAFE),
633 }
634}
635
636fn list_overflow_position_keywords(f: KeywordsCollectFn) {
637 f(&["safe", "unsafe"]);
638}
639
640fn parse_self_position<'i, 't>(
642 input: &mut Parser<'i, 't>,
643 axis: AxisDirection,
644) -> Result<AlignFlags, ParseError<'i>> {
645 Ok(try_match_ident_ignore_ascii_case! { input,
648 "start" => AlignFlags::START,
649 "end" => AlignFlags::END,
650 "flex-start" => AlignFlags::FLEX_START,
651 "flex-end" => AlignFlags::FLEX_END,
652 "center" => AlignFlags::CENTER,
653 "self-start" => AlignFlags::SELF_START,
654 "self-end" => AlignFlags::SELF_END,
655 "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
656 "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
657 "anchor-center" if static_prefs::pref!("layout.css.anchor-positioning.enabled") => AlignFlags::ANCHOR_CENTER,
658 })
659}
660
661fn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
662 f(&[
663 "start",
664 "end",
665 "flex-start",
666 "flex-end",
667 "center",
668 "self-start",
669 "self-end",
670 ]);
671
672 if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
673 f(&["anchor-center"]);
674 }
675
676 if axis == AxisDirection::Inline {
677 f(&["left", "right"]);
678 }
679}
680
681fn parse_left_right_center<'i, 't>(
682 input: &mut Parser<'i, 't>,
683) -> Result<AlignFlags, ParseError<'i>> {
684 Ok(try_match_ident_ignore_ascii_case! { input,
687 "left" => AlignFlags::LEFT,
688 "right" => AlignFlags::RIGHT,
689 "center" => AlignFlags::CENTER,
690 })
691}
692
693fn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
695 let flags = try_match_ident_ignore_ascii_case! { input,
698 "legacy" => {
699 let flags = input.try_parse(parse_left_right_center)
700 .unwrap_or(AlignFlags::empty());
701
702 return Ok(AlignFlags::LEGACY | flags)
703 },
704 "left" => AlignFlags::LEFT,
705 "right" => AlignFlags::RIGHT,
706 "center" => AlignFlags::CENTER,
707 };
708
709 input.expect_ident_matching("legacy")?;
710 Ok(AlignFlags::LEGACY | flags)
711}
712
713fn list_legacy_keywords(f: KeywordsCollectFn) {
714 f(&["legacy", "left", "right", "center"]);
715}