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,
18 Copy,
19 Debug,
20 Deserialize,
21 Eq,
22 MallocSizeOf,
23 PartialEq,
24 Serialize,
25 ToComputedValue,
26 ToResolvedValue,
27 ToShmem,
28)]
29#[repr(C)]
30pub struct AlignFlags(u8);
31bitflags! {
32 impl AlignFlags: u8 {
33 const AUTO = 0;
36 const NORMAL = 1;
38 const START = 2;
40 const END = 3;
42 const FLEX_START = 4;
44 const FLEX_END = 5;
46 const CENTER = 6;
48 const LEFT = 7;
50 const RIGHT = 8;
52 const BASELINE = 9;
54 const LAST_BASELINE = 10;
56 const STRETCH = 11;
58 const SELF_START = 12;
60 const SELF_END = 13;
62 const SPACE_BETWEEN = 14;
64 const SPACE_AROUND = 15;
66 const SPACE_EVENLY = 16;
68 const ANCHOR_CENTER = 17;
70
71 const LEGACY = 1 << 5;
74 const SAFE = 1 << 6;
76 const UNSAFE = 1 << 7;
78
79 const FLAG_BITS = 0b11100000;
81 }
82}
83
84impl AlignFlags {
85 #[inline]
87 pub fn value(&self) -> Self {
88 *self & !AlignFlags::FLAG_BITS
89 }
90
91 #[inline]
93 pub fn with_value(&self, value: AlignFlags) -> Self {
94 debug_assert!(!value.intersects(Self::FLAG_BITS));
95 value | self.flags()
96 }
97
98 #[inline]
100 pub fn flags(&self) -> Self {
101 *self & AlignFlags::FLAG_BITS
102 }
103}
104
105impl ToCss for AlignFlags {
106 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
107 where
108 W: Write,
109 {
110 let flags = self.flags();
111 let value = self.value();
112 match flags {
113 AlignFlags::LEGACY => {
114 dest.write_str("legacy")?;
115 if value.is_empty() {
116 return Ok(());
117 }
118 dest.write_char(' ')?;
119 },
120 AlignFlags::SAFE => dest.write_str("safe ")?,
121 AlignFlags::UNSAFE => dest.write_str("unsafe ")?,
122 _ => {
123 debug_assert_eq!(flags, AlignFlags::empty());
124 },
125 }
126
127 dest.write_str(match value {
128 AlignFlags::AUTO => "auto",
129 AlignFlags::NORMAL => "normal",
130 AlignFlags::START => "start",
131 AlignFlags::END => "end",
132 AlignFlags::FLEX_START => "flex-start",
133 AlignFlags::FLEX_END => "flex-end",
134 AlignFlags::CENTER => "center",
135 AlignFlags::LEFT => "left",
136 AlignFlags::RIGHT => "right",
137 AlignFlags::BASELINE => "baseline",
138 AlignFlags::LAST_BASELINE => "last baseline",
139 AlignFlags::STRETCH => "stretch",
140 AlignFlags::SELF_START => "self-start",
141 AlignFlags::SELF_END => "self-end",
142 AlignFlags::SPACE_BETWEEN => "space-between",
143 AlignFlags::SPACE_AROUND => "space-around",
144 AlignFlags::SPACE_EVENLY => "space-evenly",
145 AlignFlags::ANCHOR_CENTER => "anchor-center",
146 _ => unreachable!(),
147 })
148 }
149}
150
151#[derive(Clone, Copy, PartialEq)]
154pub enum AxisDirection {
155 Block,
157 Inline,
159}
160
161#[derive(
166 Clone,
167 Copy,
168 Debug,
169 Deserialize,
170 Eq,
171 MallocSizeOf,
172 PartialEq,
173 Serialize,
174 ToComputedValue,
175 ToCss,
176 ToResolvedValue,
177 ToShmem,
178 ToTyped,
179)]
180#[repr(C)]
181#[typed(todo_derive_fields)]
182pub struct ContentDistribution {
183 primary: AlignFlags,
184 }
187
188impl ContentDistribution {
189 #[inline]
191 pub fn normal() -> Self {
192 Self::new(AlignFlags::NORMAL)
193 }
194
195 #[inline]
197 pub fn start() -> Self {
198 Self::new(AlignFlags::START)
199 }
200
201 #[inline]
203 pub fn new(primary: AlignFlags) -> Self {
204 Self { primary }
205 }
206
207 pub fn is_baseline_position(&self) -> bool {
209 matches!(
210 self.primary.value(),
211 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
212 )
213 }
214
215 #[inline]
217 pub fn primary(self) -> AlignFlags {
218 self.primary
219 }
220
221 pub fn parse_block(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
223 Self::parse(input, AxisDirection::Block)
224 }
225
226 pub fn parse_inline(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
228 Self::parse(input, AxisDirection::Inline)
229 }
230
231 fn parse(input: &mut Parser, axis: AxisDirection) -> Result<Self, ParseError> {
232 if input
237 .try_parse(|i| i.expect_ident_matching("normal"))
238 .is_ok()
239 {
240 return Ok(ContentDistribution::normal());
241 }
242
243 if axis == AxisDirection::Block {
245 if let Ok(value) = input.try_parse(parse_baseline) {
246 return Ok(ContentDistribution::new(value));
247 }
248 }
249
250 if let Ok(value) = input.try_parse(parse_content_distribution) {
252 return Ok(ContentDistribution::new(value));
253 }
254
255 let overflow_position = input
257 .try_parse(parse_overflow_position)
258 .unwrap_or(AlignFlags::empty());
259
260 let content_position = try_match_ident_ignore_ascii_case! { input,
261 "start" => AlignFlags::START,
262 "end" => AlignFlags::END,
263 "flex-start" => AlignFlags::FLEX_START,
264 "flex-end" => AlignFlags::FLEX_END,
265 "center" => AlignFlags::CENTER,
266 "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
267 "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
268 };
269
270 Ok(ContentDistribution::new(
271 content_position | overflow_position,
272 ))
273 }
274}
275
276impl SpecifiedValueInfo for ContentDistribution {
277 fn collect_completion_keywords(f: KeywordsCollectFn) {
278 f(&["normal"]);
279 list_baseline_keywords(f); list_content_distribution_keywords(f);
281 list_overflow_position_keywords(f);
282 f(&["start", "end", "flex-start", "flex-end", "center"]);
283 f(&["left", "right"]); }
285}
286
287#[derive(
292 Clone,
293 Copy,
294 Debug,
295 Deref,
296 Deserialize,
297 Eq,
298 MallocSizeOf,
299 PartialEq,
300 Serialize,
301 ToComputedValue,
302 ToCss,
303 ToResolvedValue,
304 ToShmem,
305 ToTyped,
306)]
307#[repr(C)]
308#[typed(todo_derive_fields)]
309pub struct SelfAlignment(pub AlignFlags);
310
311impl SelfAlignment {
312 #[inline]
314 pub fn auto() -> Self {
315 SelfAlignment(AlignFlags::AUTO)
316 }
317
318 pub fn is_valid_on_both_axes(&self) -> bool {
320 match self.0.value() {
321 AlignFlags::LEFT | AlignFlags::RIGHT => false,
323
324 _ => true,
325 }
326 }
327
328 pub fn parse_block(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
330 Self::parse(input, AxisDirection::Block)
331 }
332
333 pub fn parse_inline(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
335 Self::parse(input, AxisDirection::Inline)
336 }
337
338 fn parse(input: &mut Parser, axis: AxisDirection) -> Result<Self, ParseError> {
340 if let Ok(value) = input.try_parse(parse_baseline) {
348 return Ok(SelfAlignment(value));
349 }
350
351 if let Ok(value) = input.try_parse(parse_auto_normal_stretch) {
353 return Ok(SelfAlignment(value));
354 }
355
356 let overflow_position = input
358 .try_parse(parse_overflow_position)
359 .unwrap_or(AlignFlags::empty());
360 let self_position = parse_self_position(input, axis, AllowAnchorCenter::Yes)?;
361 Ok(SelfAlignment(overflow_position | self_position))
362 }
363
364 fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
365 list_baseline_keywords(f);
366 list_auto_normal_stretch(f);
367 list_overflow_position_keywords(f);
368 list_self_position_keywords(f, axis);
369 }
370
371 pub fn flip_position(self) -> Self {
374 let flipped_value = match self.0.value() {
375 AlignFlags::START => AlignFlags::END,
376 AlignFlags::END => AlignFlags::START,
377 AlignFlags::FLEX_START => AlignFlags::FLEX_END,
378 AlignFlags::FLEX_END => AlignFlags::FLEX_START,
379 AlignFlags::LEFT => AlignFlags::RIGHT,
380 AlignFlags::RIGHT => AlignFlags::LEFT,
381 AlignFlags::SELF_START => AlignFlags::SELF_END,
382 AlignFlags::SELF_END => AlignFlags::SELF_START,
383
384 AlignFlags::AUTO
385 | AlignFlags::NORMAL
386 | AlignFlags::BASELINE
387 | AlignFlags::LAST_BASELINE
388 | AlignFlags::STRETCH
389 | AlignFlags::CENTER
390 | AlignFlags::SPACE_BETWEEN
391 | AlignFlags::SPACE_AROUND
392 | AlignFlags::SPACE_EVENLY
393 | AlignFlags::ANCHOR_CENTER => return self,
394 _ => {
395 debug_assert!(false, "Unexpected alignment enumeration value");
396 return self;
397 },
398 };
399 self.with_value(flipped_value)
400 }
401
402 #[inline]
404 pub fn with_value(self, value: AlignFlags) -> Self {
405 Self(self.0.with_value(value))
406 }
407}
408
409impl SpecifiedValueInfo for SelfAlignment {
410 fn collect_completion_keywords(f: KeywordsCollectFn) {
411 Self::list_keywords(f, AxisDirection::Block);
414 }
415}
416
417#[derive(
422 Clone,
423 Copy,
424 Debug,
425 Deref,
426 Deserialize,
427 Eq,
428 MallocSizeOf,
429 PartialEq,
430 Serialize,
431 ToComputedValue,
432 ToCss,
433 ToResolvedValue,
434 ToShmem,
435 ToTyped,
436)]
437#[repr(C)]
438#[typed(todo_derive_fields)]
439pub struct ItemPlacement(pub AlignFlags);
440
441impl ItemPlacement {
442 #[inline]
444 pub fn normal() -> Self {
445 Self(AlignFlags::NORMAL)
446 }
447}
448
449impl ItemPlacement {
450 pub fn parse_block(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
452 Self::parse(input, AxisDirection::Block)
453 }
454
455 pub fn parse_inline(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
457 Self::parse(input, AxisDirection::Inline)
458 }
459
460 fn parse(input: &mut Parser, axis: AxisDirection) -> Result<Self, ParseError> {
461 if let Ok(baseline) = input.try_parse(parse_baseline) {
466 return Ok(Self(baseline));
467 }
468
469 if let Ok(value) = input.try_parse(parse_normal_stretch) {
471 return Ok(Self(value));
472 }
473
474 if axis == AxisDirection::Inline {
475 if let Ok(value) = input.try_parse(parse_legacy) {
477 return Ok(Self(value));
478 }
479 }
480
481 let overflow = input
483 .try_parse(parse_overflow_position)
484 .unwrap_or(AlignFlags::empty());
485 let self_position = parse_self_position(input, axis, AllowAnchorCenter::No)?;
486 Ok(ItemPlacement(self_position | overflow))
487 }
488}
489
490impl SpecifiedValueInfo for ItemPlacement {
491 fn collect_completion_keywords(f: KeywordsCollectFn) {
492 list_baseline_keywords(f);
493 list_normal_stretch(f);
494 list_overflow_position_keywords(f);
495 list_self_position_keywords(f, AxisDirection::Block);
496 }
497}
498
499#[derive(
503 Clone,
504 Copy,
505 Debug,
506 Deref,
507 Deserialize,
508 Eq,
509 MallocSizeOf,
510 PartialEq,
511 Serialize,
512 ToCss,
513 ToResolvedValue,
514 ToShmem,
515 ToTyped,
516)]
517#[repr(C)]
518pub struct JustifyItems(pub ItemPlacement);
519
520impl JustifyItems {
521 #[inline]
523 pub fn legacy() -> Self {
524 Self(ItemPlacement(AlignFlags::LEGACY))
525 }
526
527 #[inline]
529 pub fn normal() -> Self {
530 Self(ItemPlacement::normal())
531 }
532}
533
534impl Parse for JustifyItems {
535 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
536 ItemPlacement::parse_inline(context, input).map(Self)
537 }
538}
539
540impl SpecifiedValueInfo for JustifyItems {
541 fn collect_completion_keywords(f: KeywordsCollectFn) {
542 ItemPlacement::collect_completion_keywords(f);
543 list_legacy_keywords(f); }
545}
546
547fn parse_auto_normal_stretch(input: &mut Parser) -> Result<AlignFlags, ParseError> {
549 try_match_ident_ignore_ascii_case! { input,
552 "auto" => Ok(AlignFlags::AUTO),
553 "normal" => Ok(AlignFlags::NORMAL),
554 "stretch" => Ok(AlignFlags::STRETCH),
555 }
556}
557
558fn list_auto_normal_stretch(f: KeywordsCollectFn) {
559 f(&["auto", "normal", "stretch"]);
560}
561
562fn parse_normal_stretch(input: &mut Parser) -> Result<AlignFlags, ParseError> {
564 try_match_ident_ignore_ascii_case! { input,
567 "normal" => Ok(AlignFlags::NORMAL),
568 "stretch" => Ok(AlignFlags::STRETCH),
569 }
570}
571
572fn list_normal_stretch(f: KeywordsCollectFn) {
573 f(&["normal", "stretch"]);
574}
575
576fn parse_baseline(input: &mut Parser) -> Result<AlignFlags, ParseError> {
578 try_match_ident_ignore_ascii_case! { input,
581 "baseline" => Ok(AlignFlags::BASELINE),
582 "first" => {
583 input.expect_ident_matching("baseline")?;
584 Ok(AlignFlags::BASELINE)
585 },
586 "last" => {
587 input.expect_ident_matching("baseline")?;
588 Ok(AlignFlags::LAST_BASELINE)
589 },
590 }
591}
592
593fn list_baseline_keywords(f: KeywordsCollectFn) {
594 f(&["baseline", "first baseline", "last baseline"]);
595}
596
597fn parse_content_distribution(input: &mut Parser) -> Result<AlignFlags, ParseError> {
599 try_match_ident_ignore_ascii_case! { input,
602 "stretch" => Ok(AlignFlags::STRETCH),
603 "space-between" => Ok(AlignFlags::SPACE_BETWEEN),
604 "space-around" => Ok(AlignFlags::SPACE_AROUND),
605 "space-evenly" => Ok(AlignFlags::SPACE_EVENLY),
606 }
607}
608
609fn list_content_distribution_keywords(f: KeywordsCollectFn) {
610 f(&["stretch", "space-between", "space-around", "space-evenly"]);
611}
612
613fn parse_overflow_position(input: &mut Parser) -> Result<AlignFlags, ParseError> {
615 try_match_ident_ignore_ascii_case! { input,
618 "safe" => Ok(AlignFlags::SAFE),
619 "unsafe" => Ok(AlignFlags::UNSAFE),
620 }
621}
622
623fn list_overflow_position_keywords(f: KeywordsCollectFn) {
624 f(&["safe", "unsafe"]);
625}
626
627enum AllowAnchorCenter {
628 No,
629 Yes,
630}
631
632fn parse_self_position(
634 input: &mut Parser,
635 axis: AxisDirection,
636 allow_anchor_center: AllowAnchorCenter,
637) -> Result<AlignFlags, ParseError> {
638 Ok(try_match_ident_ignore_ascii_case! { input,
641 "start" => AlignFlags::START,
642 "end" => AlignFlags::END,
643 "flex-start" => AlignFlags::FLEX_START,
644 "flex-end" => AlignFlags::FLEX_END,
645 "center" => AlignFlags::CENTER,
646 "self-start" => AlignFlags::SELF_START,
647 "self-end" => AlignFlags::SELF_END,
648 "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
649 "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
650 "anchor-center"
651 if matches!(allow_anchor_center, AllowAnchorCenter::Yes)
652 && crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) =>
653 {
654 AlignFlags::ANCHOR_CENTER
655 },
656 })
657}
658
659fn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
660 f(&[
661 "start",
662 "end",
663 "flex-start",
664 "flex-end",
665 "center",
666 "self-start",
667 "self-end",
668 ]);
669
670 if crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) {
671 f(&["anchor-center"]);
672 }
673
674 if axis == AxisDirection::Inline {
675 f(&["left", "right"]);
676 }
677}
678
679fn parse_left_right_center(input: &mut Parser) -> Result<AlignFlags, ParseError> {
680 Ok(try_match_ident_ignore_ascii_case! { input,
683 "left" => AlignFlags::LEFT,
684 "right" => AlignFlags::RIGHT,
685 "center" => AlignFlags::CENTER,
686 })
687}
688
689fn parse_legacy(input: &mut Parser) -> Result<AlignFlags, ParseError> {
691 let flags = try_match_ident_ignore_ascii_case! { input,
694 "legacy" => {
695 let flags = input.try_parse(parse_left_right_center)
696 .unwrap_or(AlignFlags::empty());
697
698 return Ok(AlignFlags::LEGACY | flags)
699 },
700 "left" => AlignFlags::LEFT,
701 "right" => AlignFlags::RIGHT,
702 "center" => AlignFlags::CENTER,
703 };
704
705 input.expect_ident_matching("legacy")?;
706 Ok(AlignFlags::LEGACY | flags)
707}
708
709fn list_legacy_keywords(f: KeywordsCollectFn) {
710 f(&["legacy", "left", "right", "center"]);
711}