1use super::background::{BackgroundRepeat, BackgroundSize};
4use super::border_image::{BorderImage, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice};
5use super::PropertyId;
6use crate::context::PropertyHandlerContext;
7use crate::declaration::{DeclarationBlock, DeclarationList};
8use crate::error::{ParserError, PrinterError};
9use crate::macros::{define_list_shorthand, define_shorthand, enum_property, property_bitflags};
10use crate::prefixes::Feature;
11use crate::printer::Printer;
12use crate::properties::Property;
13use crate::targets::{Browsers, Targets};
14use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss};
15use crate::values::image::ImageFallback;
16use crate::values::length::LengthOrNumber;
17use crate::values::rect::Rect;
18use crate::values::{image::Image, position::Position, shape::BasicShape, url::Url};
19use crate::vendor_prefix::VendorPrefix;
20#[cfg(feature = "visitor")]
21use crate::visitor::Visit;
22use cssparser::*;
23use itertools::izip;
24use smallvec::SmallVec;
25
26enum_property! {
27 pub enum MaskType {
29 Luminance,
31 Alpha,
33 }
34}
35
36enum_property! {
37 pub enum MaskMode {
39 Luminance,
41 Alpha,
43 MatchSource,
45 }
46}
47
48impl Default for MaskMode {
49 fn default() -> MaskMode {
50 MaskMode::MatchSource
51 }
52}
53
54enum_property! {
55 pub enum WebKitMaskSourceType {
60 Auto,
62 Luminance,
64 Alpha,
66 }
67}
68
69impl From<MaskMode> for WebKitMaskSourceType {
70 fn from(mode: MaskMode) -> WebKitMaskSourceType {
71 match mode {
72 MaskMode::Luminance => WebKitMaskSourceType::Luminance,
73 MaskMode::Alpha => WebKitMaskSourceType::Alpha,
74 MaskMode::MatchSource => WebKitMaskSourceType::Auto,
75 }
76 }
77}
78
79enum_property! {
80 pub enum GeometryBox {
83 BorderBox,
85 PaddingBox,
87 ContentBox,
89 MarginBox,
91 FillBox,
93 StrokeBox,
95 ViewBox,
97 }
98}
99
100impl Default for GeometryBox {
101 fn default() -> GeometryBox {
102 GeometryBox::BorderBox
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
108#[cfg_attr(feature = "visitor", derive(Visit))]
109#[cfg_attr(
110 feature = "serde",
111 derive(serde::Serialize, serde::Deserialize),
112 serde(tag = "type", content = "value", rename_all = "kebab-case")
113)]
114#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
115#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
116pub enum MaskClip {
117 GeometryBox(GeometryBox),
119 NoClip,
121}
122
123impl IsCompatible for MaskClip {
124 fn is_compatible(&self, browsers: Browsers) -> bool {
125 match self {
126 MaskClip::GeometryBox(g) => g.is_compatible(browsers),
127 MaskClip::NoClip => true,
128 }
129 }
130}
131
132impl Into<MaskClip> for GeometryBox {
133 fn into(self) -> MaskClip {
134 MaskClip::GeometryBox(self.clone())
135 }
136}
137
138impl IsCompatible for GeometryBox {
139 fn is_compatible(&self, _browsers: Browsers) -> bool {
140 true
141 }
142}
143
144enum_property! {
145 pub enum MaskComposite {
147 Add,
149 Subtract,
151 Intersect,
153 Exclude,
155 }
156}
157
158impl Default for MaskComposite {
159 fn default() -> MaskComposite {
160 MaskComposite::Add
161 }
162}
163
164enum_property! {
165 #[allow(missing_docs)]
170 pub enum WebKitMaskComposite {
171 Clear,
172 Copy,
173 SourceOver,
175 SourceIn,
177 SourceOut,
179 SourceAtop,
180 DestinationOver,
181 DestinationIn,
182 DestinationOut,
183 DestinationAtop,
184 Xor,
186 }
187}
188
189impl From<MaskComposite> for WebKitMaskComposite {
190 fn from(composite: MaskComposite) -> WebKitMaskComposite {
191 match composite {
192 MaskComposite::Add => WebKitMaskComposite::SourceOver,
193 MaskComposite::Subtract => WebKitMaskComposite::SourceOut,
194 MaskComposite::Intersect => WebKitMaskComposite::SourceIn,
195 MaskComposite::Exclude => WebKitMaskComposite::Xor,
196 }
197 }
198}
199
200define_list_shorthand! {
201 pub struct Mask<'i>(VendorPrefix) {
203 #[cfg_attr(feature = "serde", serde(borrow))]
205 image: MaskImage(Image<'i>, VendorPrefix),
206 position: MaskPosition(Position, VendorPrefix),
208 size: MaskSize(BackgroundSize, VendorPrefix),
210 repeat: MaskRepeat(BackgroundRepeat, VendorPrefix),
212 clip: MaskClip(MaskClip, VendorPrefix),
214 origin: MaskOrigin(GeometryBox, VendorPrefix),
216 composite: MaskComposite(MaskComposite),
218 mode: MaskMode(MaskMode),
220 }
221}
222
223impl<'i> Parse<'i> for Mask<'i> {
224 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
225 let mut image: Option<Image> = None;
226 let mut position: Option<Position> = None;
227 let mut size: Option<BackgroundSize> = None;
228 let mut repeat: Option<BackgroundRepeat> = None;
229 let mut clip: Option<MaskClip> = None;
230 let mut origin: Option<GeometryBox> = None;
231 let mut composite: Option<MaskComposite> = None;
232 let mut mode: Option<MaskMode> = None;
233
234 loop {
235 if image.is_none() {
236 if let Ok(value) = input.try_parse(Image::parse) {
237 image = Some(value);
238 continue;
239 }
240 }
241
242 if position.is_none() {
243 if let Ok(value) = input.try_parse(Position::parse) {
244 position = Some(value);
245 size = input
246 .try_parse(|input| {
247 input.expect_delim('/')?;
248 BackgroundSize::parse(input)
249 })
250 .ok();
251 continue;
252 }
253 }
254
255 if repeat.is_none() {
256 if let Ok(value) = input.try_parse(BackgroundRepeat::parse) {
257 repeat = Some(value);
258 continue;
259 }
260 }
261
262 if origin.is_none() {
263 if let Ok(value) = input.try_parse(GeometryBox::parse) {
264 origin = Some(value);
265 continue;
266 }
267 }
268
269 if clip.is_none() {
270 if let Ok(value) = input.try_parse(MaskClip::parse) {
271 clip = Some(value);
272 continue;
273 }
274 }
275
276 if composite.is_none() {
277 if let Ok(value) = input.try_parse(MaskComposite::parse) {
278 composite = Some(value);
279 continue;
280 }
281 }
282
283 if mode.is_none() {
284 if let Ok(value) = input.try_parse(MaskMode::parse) {
285 mode = Some(value);
286 continue;
287 }
288 }
289
290 break;
291 }
292
293 if clip.is_none() {
294 if let Some(origin) = origin {
295 clip = Some(origin.into());
296 }
297 }
298
299 Ok(Mask {
300 image: image.unwrap_or_default(),
301 position: position.unwrap_or_default(),
302 repeat: repeat.unwrap_or_default(),
303 size: size.unwrap_or_default(),
304 origin: origin.unwrap_or(GeometryBox::BorderBox),
305 clip: clip.unwrap_or(GeometryBox::BorderBox.into()),
306 composite: composite.unwrap_or(MaskComposite::Add),
307 mode: mode.unwrap_or(MaskMode::MatchSource),
308 })
309 }
310}
311
312impl<'i> ToCss for Mask<'i> {
313 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
314 where
315 W: std::fmt::Write,
316 {
317 self.image.to_css(dest)?;
318
319 if self.position != Position::default() || self.size != BackgroundSize::default() {
320 dest.write_char(' ')?;
321 self.position.to_css(dest)?;
322
323 if self.size != BackgroundSize::default() {
324 dest.delim('/', true)?;
325 self.size.to_css(dest)?;
326 }
327 }
328
329 if self.repeat != BackgroundRepeat::default() {
330 dest.write_char(' ')?;
331 self.repeat.to_css(dest)?;
332 }
333
334 if self.origin != GeometryBox::BorderBox || self.clip != GeometryBox::BorderBox.into() {
335 dest.write_char(' ')?;
336 self.origin.to_css(dest)?;
337
338 if self.clip != self.origin.into() {
339 dest.write_char(' ')?;
340 self.clip.to_css(dest)?;
341 }
342 }
343
344 if self.composite != MaskComposite::default() {
345 dest.write_char(' ')?;
346 self.composite.to_css(dest)?;
347 }
348
349 if self.mode != MaskMode::default() {
350 dest.write_char(' ')?;
351 self.mode.to_css(dest)?;
352 }
353
354 Ok(())
355 }
356}
357
358impl<'i> ImageFallback<'i> for Mask<'i> {
360 #[inline]
361 fn get_image(&self) -> &Image<'i> {
362 &self.image
363 }
364
365 #[inline]
366 fn with_image(&self, image: Image<'i>) -> Self {
367 Mask { image, ..self.clone() }
368 }
369}
370
371#[derive(Debug, Clone, PartialEq)]
373#[cfg_attr(feature = "visitor", derive(Visit))]
374#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
375#[cfg_attr(
376 feature = "serde",
377 derive(serde::Serialize, serde::Deserialize),
378 serde(tag = "type", rename_all = "kebab-case")
379)]
380#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
381pub enum ClipPath<'i> {
382 None,
384 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::serialization::ValueWrapper::<Url>"))]
386 Url(Url<'i>),
387 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
389 Shape {
390 shape: Box<BasicShape>,
392 reference_box: GeometryBox,
394 },
395 #[cfg_attr(feature = "serde", serde(with = "crate::serialization::ValueWrapper::<GeometryBox>"))]
397 Box(GeometryBox),
398}
399
400impl<'i> Parse<'i> for ClipPath<'i> {
401 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
402 if let Ok(url) = input.try_parse(Url::parse) {
403 return Ok(ClipPath::Url(url));
404 }
405
406 if let Ok(shape) = input.try_parse(BasicShape::parse) {
407 let b = input.try_parse(GeometryBox::parse).unwrap_or_default();
408 return Ok(ClipPath::Shape {
409 shape: Box::new(shape),
410 reference_box: b,
411 });
412 }
413
414 if let Ok(b) = input.try_parse(GeometryBox::parse) {
415 if let Ok(shape) = input.try_parse(BasicShape::parse) {
416 return Ok(ClipPath::Shape {
417 shape: Box::new(shape),
418 reference_box: b,
419 });
420 }
421 return Ok(ClipPath::Box(b));
422 }
423
424 input.expect_ident_matching("none")?;
425 Ok(ClipPath::None)
426 }
427}
428
429impl<'i> ToCss for ClipPath<'i> {
430 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
431 where
432 W: std::fmt::Write,
433 {
434 match self {
435 ClipPath::None => dest.write_str("none"),
436 ClipPath::Url(url) => url.to_css(dest),
437 ClipPath::Shape {
438 shape,
439 reference_box: b,
440 } => {
441 shape.to_css(dest)?;
442 if *b != GeometryBox::default() {
443 dest.write_char(' ')?;
444 b.to_css(dest)?;
445 }
446 Ok(())
447 }
448 ClipPath::Box(b) => b.to_css(dest),
449 }
450 }
451}
452
453enum_property! {
454 pub enum MaskBorderMode {
456 Luminance,
458 Alpha,
460 }
461}
462
463impl Default for MaskBorderMode {
464 fn default() -> MaskBorderMode {
465 MaskBorderMode::Alpha
466 }
467}
468
469define_shorthand! {
470 #[derive(Default)]
472 pub struct MaskBorder<'i> {
473 #[cfg_attr(feature = "serde", serde(borrow))]
475 source: MaskBorderSource(Image<'i>),
476 slice: MaskBorderSlice(BorderImageSlice),
478 width: MaskBorderWidth(Rect<BorderImageSideWidth>),
480 outset: MaskBorderOutset(Rect<LengthOrNumber>),
482 repeat: MaskBorderRepeat(BorderImageRepeat),
484 mode: MaskBorderMode(MaskBorderMode),
486 }
487}
488
489impl<'i> Parse<'i> for MaskBorder<'i> {
490 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
491 let mut mode: Option<MaskBorderMode> = None;
492 let border_image = BorderImage::parse_with_callback(input, |input| {
493 if mode.is_none() {
494 if let Ok(value) = input.try_parse(MaskBorderMode::parse) {
495 mode = Some(value);
496 return true;
497 }
498 }
499 false
500 });
501
502 if border_image.is_ok() || mode.is_some() {
503 let border_image = border_image.unwrap_or_default();
504 Ok(MaskBorder {
505 source: border_image.source,
506 slice: border_image.slice,
507 width: border_image.width,
508 outset: border_image.outset,
509 repeat: border_image.repeat,
510 mode: mode.unwrap_or_default(),
511 })
512 } else {
513 Err(input.new_custom_error(ParserError::InvalidDeclaration))
514 }
515 }
516}
517
518impl<'i> ToCss for MaskBorder<'i> {
519 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
520 where
521 W: std::fmt::Write,
522 {
523 BorderImage::to_css_internal(&self.source, &self.slice, &self.width, &self.outset, &self.repeat, dest)?;
524 if self.mode != MaskBorderMode::default() {
525 dest.write_char(' ')?;
526 self.mode.to_css(dest)?;
527 }
528 Ok(())
529 }
530}
531
532impl<'i> FallbackValues for MaskBorder<'i> {
533 fn get_fallbacks(&mut self, targets: Targets) -> Vec<Self> {
534 self
535 .source
536 .get_fallbacks(targets)
537 .into_iter()
538 .map(|source| MaskBorder { source, ..self.clone() })
539 .collect()
540 }
541}
542
543impl<'i> Into<BorderImage<'i>> for MaskBorder<'i> {
544 fn into(self) -> BorderImage<'i> {
545 BorderImage {
546 source: self.source,
547 slice: self.slice,
548 width: self.width,
549 outset: self.outset,
550 repeat: self.repeat,
551 }
552 }
553}
554
555property_bitflags! {
556 #[derive(Default, Debug)]
557 struct MaskProperty: u16 {
558 const MaskImage(_vp) = 1 << 0;
559 const MaskPosition(_vp) = 1 << 1;
560 const MaskSize(_vp) = 1 << 2;
561 const MaskRepeat(_vp) = 1 << 3;
562 const MaskClip(_vp) = 1 << 4;
563 const MaskOrigin(_vp) = 1 << 5;
564 const MaskComposite = 1 << 6;
565 const MaskMode = 1 << 7;
566 const Mask(_vp) = Self::MaskImage.bits() | Self::MaskPosition.bits() | Self::MaskSize.bits() | Self::MaskRepeat.bits() | Self::MaskClip.bits() | Self::MaskOrigin.bits() | Self::MaskComposite.bits() | Self::MaskMode.bits();
567
568 const MaskBorderSource = 1 << 7;
569 const MaskBorderMode = 1 << 8;
570 const MaskBorderSlice = 1 << 9;
571 const MaskBorderWidth = 1 << 10;
572 const MaskBorderOutset = 1 << 11;
573 const MaskBorderRepeat = 1 << 12;
574 const MaskBorder = Self::MaskBorderSource.bits() | Self::MaskBorderMode.bits() | Self::MaskBorderSlice.bits() | Self::MaskBorderWidth.bits() | Self::MaskBorderOutset.bits() | Self::MaskBorderRepeat.bits();
575 }
576}
577
578#[derive(Default)]
579pub(crate) struct MaskHandler<'i> {
580 images: Option<(SmallVec<[Image<'i>; 1]>, VendorPrefix)>,
581 positions: Option<(SmallVec<[Position; 1]>, VendorPrefix)>,
582 sizes: Option<(SmallVec<[BackgroundSize; 1]>, VendorPrefix)>,
583 repeats: Option<(SmallVec<[BackgroundRepeat; 1]>, VendorPrefix)>,
584 clips: Option<(SmallVec<[MaskClip; 1]>, VendorPrefix)>,
585 origins: Option<(SmallVec<[GeometryBox; 1]>, VendorPrefix)>,
586 composites: Option<SmallVec<[MaskComposite; 1]>>,
587 modes: Option<SmallVec<[MaskMode; 1]>>,
588 border_source: Option<(Image<'i>, VendorPrefix)>,
589 border_mode: Option<MaskBorderMode>,
590 border_slice: Option<(BorderImageSlice, VendorPrefix)>,
591 border_width: Option<(Rect<BorderImageSideWidth>, VendorPrefix)>,
592 border_outset: Option<(Rect<LengthOrNumber>, VendorPrefix)>,
593 border_repeat: Option<(BorderImageRepeat, VendorPrefix)>,
594 flushed_properties: MaskProperty,
595 has_any: bool,
596}
597
598impl<'i> PropertyHandler<'i> for MaskHandler<'i> {
599 fn handle_property(
600 &mut self,
601 property: &Property<'i>,
602 dest: &mut DeclarationList<'i>,
603 context: &mut PropertyHandlerContext<'i, '_>,
604 ) -> bool {
605 macro_rules! maybe_flush {
606 ($prop: ident, $val: expr, $vp: expr) => {{
607 if let Some((val, prefixes)) = &self.$prop {
610 if val != $val && !prefixes.contains(*$vp) {
611 self.flush(dest, context);
612 }
613 }
614
615 if self.$prop.is_some() && matches!(context.targets.browsers, Some(targets) if !$val.is_compatible(targets)) {
616 self.flush(dest, context);
617 }
618 }};
619 }
620
621 macro_rules! property {
622 ($prop: ident, $val: expr, $vp: expr) => {{
623 maybe_flush!($prop, $val, $vp);
624
625 if let Some((val, prefixes)) = &mut self.$prop {
627 *val = $val.clone();
628 *prefixes |= *$vp;
629 } else {
630 self.$prop = Some(($val.clone(), *$vp));
631 self.has_any = true;
632 }
633 }};
634 }
635
636 macro_rules! border_shorthand {
637 ($val: expr, $vp: expr) => {
638 let source = $val.source.clone();
639 maybe_flush!(border_source, &source, &$vp);
640
641 let slice = $val.slice.clone();
642 maybe_flush!(border_slice, &slice, &$vp);
643
644 let width = $val.width.clone();
645 maybe_flush!(border_width, &width, &$vp);
646
647 let outset = $val.outset.clone();
648 maybe_flush!(border_outset, &outset, &$vp);
649
650 let repeat = $val.repeat.clone();
651 maybe_flush!(border_repeat, &repeat, &$vp);
652
653 property!(border_source, &source, &$vp);
654 property!(border_slice, &slice, &$vp);
655 property!(border_width, &width, &$vp);
656 property!(border_outset, &outset, &$vp);
657 property!(border_repeat, &repeat, &$vp);
658 };
659 }
660
661 match property {
662 Property::MaskImage(val, vp) => property!(images, val, vp),
663 Property::MaskPosition(val, vp) => property!(positions, val, vp),
664 Property::MaskSize(val, vp) => property!(sizes, val, vp),
665 Property::MaskRepeat(val, vp) => property!(repeats, val, vp),
666 Property::MaskClip(val, vp) => property!(clips, val, vp),
667 Property::MaskOrigin(val, vp) => property!(origins, val, vp),
668 Property::MaskComposite(val) => self.composites = Some(val.clone()),
669 Property::MaskMode(val) => self.modes = Some(val.clone()),
670 Property::Mask(val, prefix) => {
671 let images = val.iter().map(|b| b.image.clone()).collect();
672 maybe_flush!(images, &images, prefix);
673
674 let positions = val.iter().map(|b| b.position.clone()).collect();
675 maybe_flush!(positions, &positions, prefix);
676
677 let sizes = val.iter().map(|b| b.size.clone()).collect();
678 maybe_flush!(sizes, &sizes, prefix);
679
680 let repeats = val.iter().map(|b| b.repeat.clone()).collect();
681 maybe_flush!(repeats, &repeats, prefix);
682
683 let clips = val.iter().map(|b| b.clip.clone()).collect();
684 maybe_flush!(clips, &clips, prefix);
685
686 let origins = val.iter().map(|b| b.origin.clone()).collect();
687 maybe_flush!(origins, &origins, prefix);
688
689 self.composites = Some(val.iter().map(|b| b.composite.clone()).collect());
690 self.modes = Some(val.iter().map(|b| b.mode.clone()).collect());
691
692 property!(images, &images, prefix);
693 property!(positions, &positions, prefix);
694 property!(sizes, &sizes, prefix);
695 property!(repeats, &repeats, prefix);
696 property!(clips, &clips, prefix);
697 property!(origins, &origins, prefix);
698 }
699 Property::Unparsed(val) if is_mask_property(&val.property_id) => {
700 self.flush(dest, context);
701 let mut unparsed = val.get_prefixed(context.targets, Feature::Mask);
702 context.add_unparsed_fallbacks(&mut unparsed);
703 self
704 .flushed_properties
705 .insert(MaskProperty::try_from(&val.property_id).unwrap());
706 dest.push(Property::Unparsed(unparsed));
707 }
708 Property::MaskBorderSource(val) => property!(border_source, val, &VendorPrefix::None),
709 Property::WebKitMaskBoxImageSource(val, _) => property!(border_source, val, &VendorPrefix::WebKit),
710 Property::MaskBorderMode(val) => self.border_mode = Some(val.clone()),
711 Property::MaskBorderSlice(val) => property!(border_slice, val, &VendorPrefix::None),
712 Property::WebKitMaskBoxImageSlice(val, _) => property!(border_slice, val, &VendorPrefix::WebKit),
713 Property::MaskBorderWidth(val) => property!(border_width, val, &VendorPrefix::None),
714 Property::WebKitMaskBoxImageWidth(val, _) => property!(border_width, val, &VendorPrefix::WebKit),
715 Property::MaskBorderOutset(val) => property!(border_outset, val, &VendorPrefix::None),
716 Property::WebKitMaskBoxImageOutset(val, _) => property!(border_outset, val, &VendorPrefix::WebKit),
717 Property::MaskBorderRepeat(val) => property!(border_repeat, val, &VendorPrefix::None),
718 Property::WebKitMaskBoxImageRepeat(val, _) => property!(border_repeat, val, &VendorPrefix::WebKit),
719 Property::MaskBorder(val) => {
720 border_shorthand!(val, VendorPrefix::None);
721 self.border_mode = Some(val.mode.clone());
722 }
723 Property::WebKitMaskBoxImage(val, _) => {
724 border_shorthand!(val, VendorPrefix::WebKit);
725 }
726 Property::Unparsed(val) if is_mask_border_property(&val.property_id) => {
727 self.flush(dest, context);
728 let mut val = val.clone();
730 let prefix = context
731 .targets
732 .prefixes(val.property_id.prefix().or_none(), Feature::MaskBorder);
733 if prefix.contains(VendorPrefix::WebKit) {
734 if let Some(property_id) = get_webkit_mask_property(&val.property_id) {
735 let mut clone = val.clone();
736 clone.property_id = property_id;
737 context.add_unparsed_fallbacks(&mut clone);
738 dest.push(Property::Unparsed(clone));
739 }
740 }
741
742 context.add_unparsed_fallbacks(&mut val);
743 self
744 .flushed_properties
745 .insert(MaskProperty::try_from(&val.property_id).unwrap());
746 dest.push(Property::Unparsed(val));
747 }
748 _ => return false,
749 }
750
751 self.has_any = true;
752 true
753 }
754
755 fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
756 self.flush(dest, context);
757 self.flushed_properties = MaskProperty::empty();
758 }
759}
760
761impl<'i> MaskHandler<'i> {
762 fn flush(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
763 if !self.has_any {
764 return;
765 }
766
767 self.has_any = false;
768
769 self.flush_mask(dest, context);
770 self.flush_mask_border(dest, context);
771 }
772
773 fn flush_mask(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
774 let mut images = std::mem::take(&mut self.images);
775 let mut positions = std::mem::take(&mut self.positions);
776 let mut sizes = std::mem::take(&mut self.sizes);
777 let mut repeats = std::mem::take(&mut self.repeats);
778 let mut clips = std::mem::take(&mut self.clips);
779 let mut origins = std::mem::take(&mut self.origins);
780 let mut composites = std::mem::take(&mut self.composites);
781 let mut modes = std::mem::take(&mut self.modes);
782
783 if let (
784 Some((images, images_vp)),
785 Some((positions, positions_vp)),
786 Some((sizes, sizes_vp)),
787 Some((repeats, repeats_vp)),
788 Some((clips, clips_vp)),
789 Some((origins, origins_vp)),
790 Some(composites_val),
791 Some(mode_vals),
792 ) = (
793 &mut images,
794 &mut positions,
795 &mut sizes,
796 &mut repeats,
797 &mut clips,
798 &mut origins,
799 &mut composites,
800 &mut modes,
801 ) {
802 let len = images.len();
804 let intersection = *images_vp & *positions_vp & *sizes_vp & *repeats_vp & *clips_vp & *origins_vp;
805 if !intersection.is_empty()
806 && positions.len() == len
807 && sizes.len() == len
808 && repeats.len() == len
809 && clips.len() == len
810 && origins.len() == len
811 && composites_val.len() == len
812 && mode_vals.len() == len
813 {
814 let mut masks: SmallVec<[Mask<'i>; 1]> = izip!(
815 images.drain(..),
816 positions.drain(..),
817 sizes.drain(..),
818 repeats.drain(..),
819 clips.drain(..),
820 origins.drain(..),
821 composites_val.drain(..),
822 mode_vals.drain(..)
823 )
824 .map(|(image, position, size, repeat, clip, origin, composite, mode)| Mask {
825 image,
826 position,
827 size,
828 repeat,
829 clip,
830 origin,
831 composite,
832 mode,
833 })
834 .collect();
835
836 let mut prefix = context.targets.prefixes(intersection, Feature::Mask);
837 if !self.flushed_properties.intersects(MaskProperty::Mask) {
838 for fallback in masks.get_fallbacks(context.targets) {
839 let mut p = fallback
843 .iter()
844 .fold(VendorPrefix::empty(), |p, mask| p | mask.image.get_vendor_prefix())
845 - VendorPrefix::None
846 & prefix;
847 if p.is_empty() {
848 p = prefix;
849 }
850 self.flush_mask_shorthand(fallback, p, dest);
851 }
852
853 let p = masks
854 .iter()
855 .fold(VendorPrefix::empty(), |p, mask| p | mask.image.get_vendor_prefix())
856 - VendorPrefix::None
857 & prefix;
858 if !p.is_empty() {
859 prefix = p;
860 }
861 }
862
863 self.flush_mask_shorthand(masks, prefix, dest);
864 self.flushed_properties.insert(MaskProperty::Mask);
865
866 images_vp.remove(intersection);
867 positions_vp.remove(intersection);
868 sizes_vp.remove(intersection);
869 repeats_vp.remove(intersection);
870 clips_vp.remove(intersection);
871 origins_vp.remove(intersection);
872 composites = None;
873 modes = None;
874 }
875 }
876
877 macro_rules! prop {
878 ($var: ident, $property: ident) => {
879 if let Some((val, vp)) = $var {
880 if !vp.is_empty() {
881 let prefix = context.targets.prefixes(vp, Feature::$property);
882 dest.push(Property::$property(val, prefix));
883 self.flushed_properties.insert(MaskProperty::$property);
884 }
885 }
886 };
887 }
888
889 if let Some((mut images, vp)) = images {
890 if !vp.is_empty() {
891 let mut prefix = vp;
892 if !self.flushed_properties.contains(MaskProperty::MaskImage) {
893 prefix = context.targets.prefixes(prefix, Feature::MaskImage);
894 for fallback in images.get_fallbacks(context.targets) {
895 let mut p = fallback
899 .iter()
900 .fold(VendorPrefix::empty(), |p, image| p | image.get_vendor_prefix())
901 - VendorPrefix::None
902 & prefix;
903 if p.is_empty() {
904 p = prefix;
905 }
906 dest.push(Property::MaskImage(fallback, p))
907 }
908
909 let p = images
910 .iter()
911 .fold(VendorPrefix::empty(), |p, image| p | image.get_vendor_prefix())
912 - VendorPrefix::None
913 & prefix;
914 if !p.is_empty() {
915 prefix = p;
916 }
917 }
918
919 dest.push(Property::MaskImage(images, prefix));
920 self.flushed_properties.insert(MaskProperty::MaskImage);
921 }
922 }
923
924 prop!(positions, MaskPosition);
925 prop!(sizes, MaskSize);
926 prop!(repeats, MaskRepeat);
927 prop!(clips, MaskClip);
928 prop!(origins, MaskOrigin);
929
930 if let Some(composites) = composites {
931 let prefix = context.targets.prefixes(VendorPrefix::None, Feature::MaskComposite);
932 if prefix.contains(VendorPrefix::WebKit) {
933 dest.push(Property::WebKitMaskComposite(
934 composites.iter().map(|c| (*c).into()).collect(),
935 ));
936 }
937
938 dest.push(Property::MaskComposite(composites));
939 self.flushed_properties.insert(MaskProperty::MaskComposite);
940 }
941
942 if let Some(modes) = modes {
943 let prefix = context.targets.prefixes(VendorPrefix::None, Feature::Mask);
944 if prefix.contains(VendorPrefix::WebKit) {
945 dest.push(Property::WebKitMaskSourceType(
946 modes.iter().map(|c| (*c).into()).collect(),
947 VendorPrefix::WebKit,
948 ));
949 }
950
951 dest.push(Property::MaskMode(modes));
952 self.flushed_properties.insert(MaskProperty::MaskMode);
953 }
954 }
955
956 fn flush_mask_shorthand(
957 &self,
958 masks: SmallVec<[Mask<'i>; 1]>,
959 prefix: VendorPrefix,
960 dest: &mut DeclarationList<'i>,
961 ) {
962 if prefix.contains(VendorPrefix::WebKit)
963 && masks
964 .iter()
965 .any(|mask| mask.composite != MaskComposite::default() || mask.mode != MaskMode::default())
966 {
967 let mut webkit = masks.clone();
971 let mut composites: SmallVec<[WebKitMaskComposite; 1]> = SmallVec::new();
972 let mut modes: SmallVec<[WebKitMaskSourceType; 1]> = SmallVec::new();
973 let mut needs_composites = false;
974 let mut needs_modes = false;
975 for mask in &mut webkit {
976 let composite = std::mem::take(&mut mask.composite);
977 if composite != MaskComposite::default() {
978 needs_composites = true;
979 }
980 composites.push(composite.into());
981
982 let mode = std::mem::take(&mut mask.mode);
983 if mode != MaskMode::default() {
984 needs_modes = true;
985 }
986 modes.push(mode.into());
987 }
988
989 dest.push(Property::Mask(webkit, VendorPrefix::WebKit));
990 if needs_composites {
991 dest.push(Property::WebKitMaskComposite(composites));
992 }
993 if needs_modes {
994 dest.push(Property::WebKitMaskSourceType(modes, VendorPrefix::WebKit));
995 }
996
997 let prefix = prefix - VendorPrefix::WebKit;
998 if !prefix.is_empty() {
999 dest.push(Property::Mask(masks, prefix));
1000 }
1001 } else {
1002 dest.push(Property::Mask(masks, prefix));
1003 }
1004 }
1005
1006 fn flush_mask_border(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
1007 let mut source = std::mem::take(&mut self.border_source);
1008 let mut slice = std::mem::take(&mut self.border_slice);
1009 let mut width = std::mem::take(&mut self.border_width);
1010 let mut outset = std::mem::take(&mut self.border_outset);
1011 let mut repeat = std::mem::take(&mut self.border_repeat);
1012 let mut mode = std::mem::take(&mut self.border_mode);
1013
1014 if let (
1015 Some((source, source_vp)),
1016 Some((slice, slice_vp)),
1017 Some((width, width_vp)),
1018 Some((outset, outset_vp)),
1019 Some((repeat, repeat_vp)),
1020 ) = (&mut source, &mut slice, &mut width, &mut outset, &mut repeat)
1021 {
1022 let intersection = *source_vp & *slice_vp & *width_vp & *outset_vp & *repeat_vp;
1023 if !intersection.is_empty() && (!intersection.contains(VendorPrefix::None) || mode.is_some()) {
1024 let mut mask_border = MaskBorder {
1025 source: source.clone(),
1026 slice: slice.clone(),
1027 width: width.clone(),
1028 outset: outset.clone(),
1029 repeat: repeat.clone(),
1030 mode: mode.unwrap_or_default(),
1031 };
1032
1033 let mut prefix = context.targets.prefixes(intersection, Feature::MaskBorder);
1034 if !self.flushed_properties.intersects(MaskProperty::MaskBorder) {
1035 let fallbacks = mask_border.get_fallbacks(context.targets);
1037 for fallback in fallbacks {
1038 let mut p = fallback.source.get_vendor_prefix() - VendorPrefix::None & prefix;
1039 if p.is_empty() {
1040 p = prefix;
1041 }
1042
1043 if p.contains(VendorPrefix::WebKit) {
1044 dest.push(Property::WebKitMaskBoxImage(
1045 fallback.clone().into(),
1046 VendorPrefix::WebKit,
1047 ));
1048 }
1049
1050 if p.contains(VendorPrefix::None) {
1051 dest.push(Property::MaskBorder(fallback));
1052 }
1053 }
1054 }
1055
1056 let p = mask_border.source.get_vendor_prefix() - VendorPrefix::None & prefix;
1057 if !p.is_empty() {
1058 prefix = p;
1059 }
1060
1061 if prefix.contains(VendorPrefix::WebKit) {
1062 dest.push(Property::WebKitMaskBoxImage(
1063 mask_border.clone().into(),
1064 VendorPrefix::WebKit,
1065 ));
1066 }
1067
1068 if prefix.contains(VendorPrefix::None) {
1069 dest.push(Property::MaskBorder(mask_border));
1070 self.flushed_properties.insert(MaskProperty::MaskBorder);
1071 mode = None;
1072 }
1073
1074 source_vp.remove(intersection);
1075 slice_vp.remove(intersection);
1076 width_vp.remove(intersection);
1077 outset_vp.remove(intersection);
1078 repeat_vp.remove(intersection);
1079 }
1080 }
1081
1082 if let Some((mut source, mut prefix)) = source {
1083 prefix = context.targets.prefixes(prefix, Feature::MaskBorderSource);
1084
1085 if !self.flushed_properties.contains(MaskProperty::MaskBorderSource) {
1086 let fallbacks = source.get_fallbacks(context.targets);
1088 for fallback in fallbacks {
1089 if prefix.contains(VendorPrefix::WebKit) {
1090 dest.push(Property::WebKitMaskBoxImageSource(
1091 fallback.clone(),
1092 VendorPrefix::WebKit,
1093 ));
1094 }
1095
1096 if prefix.contains(VendorPrefix::None) {
1097 dest.push(Property::MaskBorderSource(fallback));
1098 }
1099 }
1100 }
1101
1102 if prefix.contains(VendorPrefix::WebKit) {
1103 dest.push(Property::WebKitMaskBoxImageSource(source.clone(), VendorPrefix::WebKit));
1104 }
1105
1106 if prefix.contains(VendorPrefix::None) {
1107 dest.push(Property::MaskBorderSource(source));
1108 self.flushed_properties.insert(MaskProperty::MaskBorderSource);
1109 }
1110 }
1111
1112 macro_rules! prop {
1113 ($val: expr, $prop: ident, $webkit: ident) => {
1114 if let Some((val, mut prefix)) = $val {
1115 prefix = context.targets.prefixes(prefix, Feature::$prop);
1116 if prefix.contains(VendorPrefix::WebKit) {
1117 dest.push(Property::$webkit(val.clone(), VendorPrefix::WebKit));
1118 }
1119
1120 if prefix.contains(VendorPrefix::None) {
1121 dest.push(Property::$prop(val));
1122 }
1123 self.flushed_properties.insert(MaskProperty::$prop);
1124 }
1125 };
1126 }
1127
1128 prop!(slice, MaskBorderSlice, WebKitMaskBoxImageSlice);
1129 prop!(width, MaskBorderWidth, WebKitMaskBoxImageWidth);
1130 prop!(outset, MaskBorderOutset, WebKitMaskBoxImageOutset);
1131 prop!(repeat, MaskBorderRepeat, WebKitMaskBoxImageRepeat);
1132
1133 if let Some(mode) = mode {
1134 dest.push(Property::MaskBorderMode(mode));
1135 self.flushed_properties.insert(MaskProperty::MaskBorderMode);
1136 }
1137 }
1138}
1139
1140#[inline]
1141fn is_mask_property(property_id: &PropertyId) -> bool {
1142 match property_id {
1143 PropertyId::MaskImage(_)
1144 | PropertyId::MaskPosition(_)
1145 | PropertyId::MaskSize(_)
1146 | PropertyId::MaskRepeat(_)
1147 | PropertyId::MaskClip(_)
1148 | PropertyId::MaskOrigin(_)
1149 | PropertyId::MaskComposite
1150 | PropertyId::MaskMode
1151 | PropertyId::Mask(_) => true,
1152 _ => false,
1153 }
1154}
1155
1156#[inline]
1157fn is_mask_border_property(property_id: &PropertyId) -> bool {
1158 match property_id {
1159 PropertyId::MaskBorderSource
1160 | PropertyId::MaskBorderSlice
1161 | PropertyId::MaskBorderWidth
1162 | PropertyId::MaskBorderOutset
1163 | PropertyId::MaskBorderRepeat
1164 | PropertyId::MaskBorderMode
1165 | PropertyId::MaskBorder => true,
1166 _ => false,
1167 }
1168}
1169
1170#[inline]
1171pub(crate) fn get_webkit_mask_property(property_id: &PropertyId) -> Option<PropertyId<'static>> {
1172 Some(match property_id {
1173 PropertyId::MaskBorderSource => PropertyId::WebKitMaskBoxImageSource(VendorPrefix::WebKit),
1174 PropertyId::MaskBorderSlice => PropertyId::WebKitMaskBoxImageSlice(VendorPrefix::WebKit),
1175 PropertyId::MaskBorderWidth => PropertyId::WebKitMaskBoxImageWidth(VendorPrefix::WebKit),
1176 PropertyId::MaskBorderOutset => PropertyId::WebKitMaskBoxImageOutset(VendorPrefix::WebKit),
1177 PropertyId::MaskBorderRepeat => PropertyId::WebKitMaskBoxImageRepeat(VendorPrefix::WebKit),
1178 PropertyId::MaskBorder => PropertyId::WebKitMaskBoxImage(VendorPrefix::WebKit),
1179 PropertyId::MaskComposite => PropertyId::WebKitMaskComposite,
1180 PropertyId::MaskMode => PropertyId::WebKitMaskSourceType(VendorPrefix::WebKit),
1181 _ => return None,
1182 })
1183}