swc_css_prefixer/
prefixer.rs

1#![allow(clippy::match_like_matches_macro)]
2
3use core::f64::consts::PI;
4use std::{mem::take, sync::Arc};
5
6use once_cell::sync::Lazy;
7use preset_env_base::{query::targets_to_versions, version::Version, BrowserData, Versions};
8use rustc_hash::FxHashMap;
9use swc_atoms::{atom, Atom};
10use swc_common::{EqIgnoreSpan, DUMMY_SP};
11use swc_css_ast::*;
12use swc_css_utils::{
13    replace_function_name, replace_ident, replace_pseudo_class_selector_name,
14    replace_pseudo_class_selector_on_pseudo_element_selector, replace_pseudo_element_selector_name,
15};
16use swc_css_visit::{VisitMut, VisitMutWith};
17
18use crate::options::Options;
19
20static PREFIXES_AND_BROWSERS: Lazy<FxHashMap<String, [BrowserData<Option<Version>>; 2]>> =
21    Lazy::new(|| {
22        let map: FxHashMap<String, [BrowserData<Option<Version>>; 2]> =
23            serde_json::from_str(include_str!("../data/prefixes_and_browsers.json"))
24                .expect("failed to parse json");
25
26        map.into_iter()
27            .map(|(property, versions)| {
28                (
29                    property,
30                    [
31                        versions[0].map_value(|version| version),
32                        versions[1].map_value(|version| version),
33                    ],
34                )
35            })
36            .collect()
37    });
38
39fn should_enable(
40    target: &Versions,
41    low_versions: &Versions,
42    high_versions: &Versions,
43    default: bool,
44) -> bool {
45    let mut iter = target
46        .iter()
47        .zip(low_versions.iter().zip(high_versions.iter()));
48
49    if iter.all(|((_, target_version), ((_, l), (_, h)))| {
50        target_version.is_none() && l.is_none() && h.is_none()
51    }) {
52        return default;
53    }
54
55    let mut iter = target
56        .iter()
57        .zip(low_versions.iter().zip(high_versions.iter()));
58
59    iter.any(
60        |(
61            (target_name, maybe_target_version),
62            ((_, maybe_low_version), (_, maybe_high_version)),
63        )| {
64            maybe_target_version.is_some_and(|target_version| {
65                let low_or_fallback_version = maybe_low_version.or_else(|| match target_name {
66                    // Fall back to Chrome versions if Android browser data
67                    // is missing from the feature data. It appears the
68                    // Android browser has aligned its versioning with Chrome.
69                    "android" => low_versions.chrome,
70                    _ => None,
71                });
72
73                if let Some(low_or_fallback_version) = low_or_fallback_version {
74                    if target_version >= low_or_fallback_version {
75                        let high_or_fallback_version = maybe_high_version.or(match target_name {
76                            // Fall back to Chrome versions if Android browser data
77                            // is missing from the feature data. It appears the
78                            // Android browser has aligned its versioning with Chrome.
79                            "android" => high_versions.chrome,
80                            _ => None,
81                        });
82
83                        if let Some(high_or_fallback_version) = high_or_fallback_version {
84                            target_version <= high_or_fallback_version
85                        } else {
86                            true
87                        }
88                    } else {
89                        false
90                    }
91                } else {
92                    false
93                }
94            })
95        },
96    )
97}
98
99pub fn should_prefix(property: &str, target: &Versions, default: bool) -> bool {
100    if target.is_any_target() {
101        return true;
102    }
103
104    let versions = PREFIXES_AND_BROWSERS.get(property);
105
106    if let Some(versions) = versions {
107        return should_enable(target, &versions[0], &versions[1], false);
108    }
109
110    default
111}
112
113pub fn prefixer(options: Options) -> impl VisitMut {
114    let env = targets_to_versions(options.env, None).expect("failed to parse targets");
115
116    Prefixer {
117        env,
118        ..Default::default()
119    }
120}
121
122struct CrossFadeFunctionReplacerOnLegacyVariant<'a> {
123    from: &'a str,
124    to: &'a str,
125}
126
127impl VisitMut for CrossFadeFunctionReplacerOnLegacyVariant<'_> {
128    fn visit_mut_function(&mut self, n: &mut Function) {
129        n.visit_mut_children_with(self);
130
131        if n.name == *self.from {
132            let mut transparency_values = Vec::new();
133
134            for group in n.value.split_mut(|n| {
135                matches!(
136                    n,
137                    ComponentValue::Delimiter(delimiter) if matches!(&**delimiter, Delimiter {
138                        value: DelimiterValue::Comma,
139                        ..
140                    })
141                )
142            }) {
143                if transparency_values.len() >= 2 {
144                    return;
145                }
146
147                let mut transparency_value = None;
148
149                for n in group {
150                    match n {
151                        ComponentValue::Percentage(percentage) => {
152                            if transparency_value.is_some() {
153                                return;
154                            }
155
156                            transparency_value = Some(percentage.value.value / 100.0);
157                        }
158                        ComponentValue::Number(number) => {
159                            if transparency_value.is_some() {
160                                return;
161                            }
162
163                            transparency_value = Some(number.value);
164                        }
165                        ComponentValue::Integer(integer) => {
166                            if transparency_value.is_some() {
167                                return;
168                            }
169
170                            transparency_value = Some((integer.value) as f64);
171                        }
172                        _ => {}
173                    }
174                }
175
176                transparency_values.push(transparency_value);
177            }
178
179            if transparency_values.len() != 2 {
180                return;
181            }
182
183            let transparency_value = match (transparency_values[0], transparency_values[1]) {
184                (None, None) => 0.5,
185                (Some(number), None) => number,
186                (None, Some(number)) => 1.0 - number,
187                (Some(first), Some(second)) if first + second == 1.0 => first,
188                _ => {
189                    return;
190                }
191            };
192
193            let mut new_value: Vec<ComponentValue> = n
194                .value
195                .iter()
196                .filter(|n| {
197                    !matches!(
198                        n,
199                        ComponentValue::Percentage(_)
200                            | ComponentValue::Number(_)
201                            | ComponentValue::Integer(_)
202                    )
203                })
204                .cloned()
205                .collect();
206
207            new_value.extend(vec![
208                ComponentValue::Delimiter(Box::new(Delimiter {
209                    span: DUMMY_SP,
210                    value: DelimiterValue::Comma,
211                })),
212                ComponentValue::Number(Box::new(Number {
213                    span: DUMMY_SP,
214                    value: transparency_value,
215                    raw: None,
216                })),
217            ]);
218
219            n.value = new_value;
220
221            match &mut n.name {
222                FunctionName::Ident(name) => {
223                    name.value = self.to.into();
224                    name.raw = None;
225                }
226                FunctionName::DashedIdent(name) => {
227                    name.value = self.to.into();
228                    name.raw = None;
229                }
230            }
231        }
232    }
233}
234
235fn replace_cross_fade_function_on_legacy_variant<N>(node: &mut N, from: &str, to: &str)
236where
237    N: for<'aa> VisitMutWith<CrossFadeFunctionReplacerOnLegacyVariant<'aa>>,
238{
239    node.visit_mut_with(&mut CrossFadeFunctionReplacerOnLegacyVariant { from, to });
240}
241
242struct ImageSetFunctionReplacerOnLegacyVariant<'a> {
243    from: &'a str,
244    to: &'a str,
245    in_function: bool,
246}
247
248impl VisitMut for ImageSetFunctionReplacerOnLegacyVariant<'_> {
249    fn visit_mut_component_value(&mut self, n: &mut ComponentValue) {
250        n.visit_mut_children_with(self);
251
252        if !self.in_function {
253            return;
254        }
255
256        if let ComponentValue::Str(node) = n {
257            *n = ComponentValue::Url(Box::new(Url {
258                span: node.span,
259                name: Ident {
260                    span: DUMMY_SP,
261                    value: atom!("url"),
262                    raw: None,
263                },
264                value: Some(Box::new(UrlValue::Str(Str {
265                    span: DUMMY_SP,
266                    value: node.value.as_ref().into(),
267                    raw: None,
268                }))),
269                modifiers: Some(Vec::new()),
270            }))
271        }
272    }
273
274    fn visit_mut_function(&mut self, n: &mut Function) {
275        let old_in_function = self.in_function;
276
277        self.in_function = n.name == *self.from;
278
279        n.visit_mut_children_with(self);
280
281        if n.name == *self.from {
282            match &mut n.name {
283                FunctionName::Ident(name) => {
284                    name.value = self.to.into();
285                    name.raw = None;
286                }
287                FunctionName::DashedIdent(name) => {
288                    name.value = self.to.into();
289                    name.raw = None;
290                }
291            }
292        }
293
294        self.in_function = old_in_function;
295    }
296}
297
298fn replace_image_set_function_on_legacy_variant<N>(node: &mut N, from: &str, to: &str)
299where
300    N: for<'aa> VisitMutWith<ImageSetFunctionReplacerOnLegacyVariant<'aa>>,
301{
302    node.visit_mut_with(&mut ImageSetFunctionReplacerOnLegacyVariant {
303        from,
304        to,
305        in_function: false,
306    });
307}
308
309struct LinearGradientFunctionReplacerOnLegacyVariant<'a> {
310    from: &'a str,
311    to: &'a str,
312}
313
314// TODO ` -webkit-mask-image` need duplicate with original property for better
315// TODO improve for very old browsers https://github.com/postcss/autoprefixer/blob/main/lib/hacks/gradient.js#L233
316impl VisitMut for LinearGradientFunctionReplacerOnLegacyVariant<'_> {
317    fn visit_mut_function(&mut self, n: &mut Function) {
318        n.visit_mut_children_with(self);
319
320        if n.name == *self.from {
321            match &mut n.name {
322                FunctionName::Ident(name) => {
323                    name.value = self.to.into();
324                    name.raw = None;
325                }
326                FunctionName::DashedIdent(name) => {
327                    name.value = self.to.into();
328                    name.raw = None;
329                }
330            }
331
332            let first = n.value.first();
333
334            match first {
335                Some(ComponentValue::Ident(ident))
336                    if ident.value.as_ref().eq_ignore_ascii_case("to") =>
337                {
338                    fn get_old_direction(direction: &str) -> Option<&str> {
339                        match direction {
340                            "top" => Some("bottom"),
341                            "left" => Some("right"),
342                            "bottom" => Some("top"),
343                            "right" => Some("left"),
344                            _ => None,
345                        }
346                    }
347
348                    match (n.value.get(1), n.value.get(2)) {
349                        (
350                            Some(ComponentValue::Ident(first)),
351                            Some(ComponentValue::Ident(second)),
352                        ) => {
353                            let Ident {
354                                value: first_value,
355                                span: first_span,
356                                ..
357                            } = &**first;
358                            let Ident {
359                                value: second_value,
360                                span: second_span,
361                                ..
362                            } = &**second;
363                            if let (Some(new_first_direction), Some(new_second_direction)) = (
364                                get_old_direction(first_value),
365                                get_old_direction(second_value),
366                            ) {
367                                let new_value = vec![
368                                    ComponentValue::Ident(Box::new(Ident {
369                                        span: *first_span,
370                                        value: new_first_direction.into(),
371                                        raw: None,
372                                    })),
373                                    ComponentValue::Ident(Box::new(Ident {
374                                        span: *second_span,
375                                        value: new_second_direction.into(),
376                                        raw: None,
377                                    })),
378                                ];
379
380                                n.value.splice(0..3, new_value);
381                            }
382                        }
383                        (Some(ComponentValue::Ident(ident)), Some(_)) => {
384                            let Ident { value, span, .. } = &**ident;
385                            if let Some(new_direction) = get_old_direction(value) {
386                                let new_value = vec![ComponentValue::Ident(Box::new(Ident {
387                                    span: *span,
388                                    value: new_direction.into(),
389                                    raw: None,
390                                }))];
391
392                                n.value.splice(0..2, new_value);
393                            }
394                        }
395                        _ => {}
396                    }
397                }
398                Some(ComponentValue::Dimension(dimension)) => {
399                    if let Dimension::Angle(Angle {
400                        value, unit, span, ..
401                    }) = &**dimension
402                    {
403                        let angle = match &*unit.value {
404                            "deg" => (value.value % 360.0 + 360.0) % 360.0,
405                            "grad" => value.value * 180.0 / 200.0,
406                            "rad" => value.value * 180.0 / PI,
407                            "turn" => value.value * 360.0,
408                            _ => {
409                                return;
410                            }
411                        };
412
413                        if angle == 0.0 {
414                            n.value[0] = ComponentValue::Ident(Box::new(Ident {
415                                span: *span,
416                                value: atom!("bottom"),
417                                raw: None,
418                            }));
419                        } else if angle == 90.0 {
420                            n.value[0] = ComponentValue::Ident(Box::new(Ident {
421                                span: *span,
422                                value: atom!("left"),
423                                raw: None,
424                            }));
425                        } else if angle == 180.0 {
426                            n.value[0] = ComponentValue::Ident(Box::new(Ident {
427                                span: *span,
428                                value: atom!("top"),
429                                raw: None,
430                            }));
431                        } else if angle == 270.0 {
432                            n.value[0] = ComponentValue::Ident(Box::new(Ident {
433                                span: *span,
434                                value: atom!("right"),
435                                raw: None,
436                            }));
437                        } else {
438                            let new_value =
439                                ((450.0 - angle).abs() % 360.0 * 1000.0).round() / 1000.0;
440
441                            n.value[0] =
442                                ComponentValue::Dimension(Box::new(Dimension::Angle(Angle {
443                                    span: *span,
444                                    value: Number {
445                                        span: value.span,
446                                        value: new_value,
447                                        raw: None,
448                                    },
449                                    unit: Ident {
450                                        span: unit.span,
451                                        value: atom!("deg"),
452                                        raw: None,
453                                    },
454                                })));
455                        }
456                    }
457                }
458                Some(_) | None => {}
459            }
460
461            if matches!(self.from, "radial-gradient" | "repeating-radial-gradient") {
462                let at_index = n.value.iter().position(|n| matches!(n, ComponentValue::Ident(ident) if ident.value.as_ref().eq_ignore_ascii_case("at")));
463                let first_comma_index = n.value.iter().position(|n| {
464                    matches!(
465                        n,
466                        ComponentValue::Delimiter(delimiter) if matches!(&**delimiter, Delimiter {
467                            value: DelimiterValue::Comma,
468                            ..
469                        })
470                    )
471                });
472
473                if let (Some(at_index), Some(first_comma_index)) = (at_index, first_comma_index) {
474                    let mut new_value = Vec::new();
475
476                    new_value.append(&mut n.value[at_index + 1..first_comma_index].to_vec());
477                    new_value.append(&mut vec![ComponentValue::Delimiter(Box::new(Delimiter {
478                        span: DUMMY_SP,
479                        value: DelimiterValue::Comma,
480                    }))]);
481                    new_value.append(&mut n.value[0..at_index].to_vec());
482
483                    n.value.splice(0..first_comma_index, new_value);
484                }
485            }
486        }
487    }
488}
489
490fn replace_gradient_function_on_legacy_variant<N>(node: &mut N, from: &str, to: &str)
491where
492    N: for<'aa> VisitMutWith<LinearGradientFunctionReplacerOnLegacyVariant<'aa>>,
493{
494    node.visit_mut_with(&mut LinearGradientFunctionReplacerOnLegacyVariant { from, to });
495}
496
497struct MediaFeatureResolutionReplacerOnLegacyVariant<'a> {
498    from: &'a str,
499    to: &'a str,
500}
501
502impl VisitMut for MediaFeatureResolutionReplacerOnLegacyVariant<'_> {
503    fn visit_mut_media_feature_plain(&mut self, n: &mut MediaFeaturePlain) {
504        n.visit_mut_children_with(self);
505
506        if let MediaFeatureValue::Dimension(Dimension::Resolution(Resolution {
507            value: resolution_value,
508            unit: resolution_unit,
509            ..
510        })) = &*n.value
511        {
512            let MediaFeatureName::Ident(Ident {
513                value: feature_name_value,
514                span: feature_name_span,
515                ..
516            }) = &n.name
517            else {
518                return;
519            };
520
521            if feature_name_value == self.from {
522                n.name = MediaFeatureName::Ident(Ident {
523                    span: *feature_name_span,
524                    value: self.to.into(),
525                    raw: None,
526                });
527
528                let left = match &*resolution_unit.value {
529                    "dpi" => (resolution_value.value / 96.0 * 100.0).round() / 100.0,
530                    "dpcm" => (((resolution_value.value * 2.54) / 96.0) * 100.0).round() / 100.0,
531                    _ => resolution_value.value,
532                };
533
534                n.value = Box::new(MediaFeatureValue::Number(Number {
535                    span: resolution_value.span,
536                    value: left,
537                    raw: None,
538                }));
539            }
540        }
541    }
542}
543
544fn replace_media_feature_resolution_on_legacy_variant<N>(node: &mut N, from: &str, to: &str)
545where
546    N: for<'aa> VisitMutWith<MediaFeatureResolutionReplacerOnLegacyVariant<'aa>>,
547{
548    node.visit_mut_with(&mut MediaFeatureResolutionReplacerOnLegacyVariant { from, to });
549}
550
551struct CalcReplacer<'a> {
552    inside_calc: bool,
553    to: Option<&'a str>,
554}
555
556impl VisitMut for CalcReplacer<'_> {
557    fn visit_mut_function(&mut self, n: &mut Function) {
558        let old_inside_calc = self.inside_calc;
559
560        let is_webkit_calc = n.name == "-webkit-calc";
561        let is_moz_calc = n.name == "-moz-calc";
562
563        if self.to.is_none() && (is_webkit_calc || is_moz_calc) {
564            return;
565        }
566
567        if (is_webkit_calc && self.to == Some("-moz-calc"))
568            || (is_moz_calc && self.to == Some("-webkit-calc"))
569        {
570            return;
571        }
572
573        let is_calc = n.name.as_str().eq_ignore_ascii_case("calc");
574
575        self.inside_calc = is_calc || is_webkit_calc || is_moz_calc;
576
577        n.visit_mut_children_with(self);
578
579        if is_calc {
580            if let Some(to) = self.to {
581                match &mut n.name {
582                    FunctionName::Ident(name) => {
583                        name.value = to.into();
584                        name.raw = None;
585                    }
586                    FunctionName::DashedIdent(name) => {
587                        name.value = to.into();
588                        name.raw = None;
589                    }
590                }
591            }
592        }
593
594        self.inside_calc = old_inside_calc;
595    }
596
597    fn visit_mut_calc_value(&mut self, n: &mut CalcValue) {
598        n.visit_mut_children_with(self);
599
600        if !self.inside_calc {
601            return;
602        }
603
604        if let CalcValue::Function(function) = n {
605            if matches_eq!(function.name, "calc", "-webkit-calc", "-moz-calc") {
606                let calc_sum = match function.value.first() {
607                    Some(ComponentValue::CalcSum(calc_sum)) => *calc_sum.clone(),
608                    _ => return,
609                };
610
611                *n = CalcValue::Sum(CalcSum {
612                    span: function.span,
613                    expressions: calc_sum.expressions,
614                });
615            }
616        }
617    }
618}
619
620fn replace_calc<N>(node: &mut N, to: Option<&str>)
621where
622    N: for<'aa> VisitMutWith<CalcReplacer<'aa>>,
623{
624    node.visit_mut_with(&mut CalcReplacer {
625        inside_calc: false,
626        to,
627    });
628}
629
630struct FontFaceFormatOldSyntax {}
631
632impl VisitMut for FontFaceFormatOldSyntax {
633    fn visit_mut_function(&mut self, n: &mut Function) {
634        n.visit_mut_children_with(self);
635
636        if !(n.name == "format") {
637            return;
638        }
639
640        if n.value.len() != 1 {
641            return;
642        }
643
644        if let Some(ComponentValue::Ident(ident)) = n.value.first() {
645            let new_value: Atom = ident.value.to_ascii_lowercase();
646            let new_value = match &*new_value {
647                "woff" | "truetype" | "opentype" | "woff2" | "embedded-opentype" | "collection"
648                | "svg" => new_value,
649                _ => {
650                    return;
651                }
652            };
653
654            let new_value = Str {
655                value: new_value,
656                span: ident.span,
657                raw: None,
658            };
659
660            n.value = vec![ComponentValue::Str(Box::new(new_value))];
661        }
662    }
663}
664
665fn font_face_format_old_syntax<N>(node: &mut N)
666where
667    N: VisitMutWith<FontFaceFormatOldSyntax>,
668{
669    node.visit_mut_with(&mut FontFaceFormatOldSyntax {});
670}
671
672struct ClampReplacer {}
673
674impl VisitMut for ClampReplacer {
675    fn visit_mut_function(&mut self, n: &mut Function) {
676        n.visit_mut_children_with(self);
677
678        if n.name != "clamp" {
679            return;
680        }
681
682        let min_function = if let (
683            Some(middle @ ComponentValue::CalcSum(_)),
684            Some(comma),
685            Some(right @ ComponentValue::CalcSum(_)),
686        ) = (n.value.get(2), n.value.get(3), n.value.get(4))
687        {
688            Function {
689                span: n.span,
690                name: FunctionName::Ident(Ident {
691                    span: n.span,
692                    value: atom!("min"),
693                    raw: None,
694                }),
695                value: vec![middle.clone(), comma.clone(), right.clone()],
696            }
697        } else {
698            return;
699        };
700
701        if let (Some(left), Some(comma)) = (n.value.first(), n.value.get(1)) {
702            *n = Function {
703                span: n.span,
704                name: FunctionName::Ident(Ident {
705                    span: n.span,
706                    value: atom!("max"),
707                    raw: None,
708                }),
709                value: vec![
710                    left.clone(),
711                    comma.clone(),
712                    ComponentValue::CalcSum(Box::new(CalcSum {
713                        span: DUMMY_SP,
714                        expressions: vec![CalcProductOrOperator::Product(CalcProduct {
715                            span: DUMMY_SP,
716                            expressions: vec![CalcValueOrOperator::Value(CalcValue::Function(
717                                min_function,
718                            ))],
719                        })],
720                    })),
721                ],
722            };
723        }
724    }
725}
726
727fn replace_clamp<N>(node: &mut N)
728where
729    N: VisitMutWith<ClampReplacer>,
730{
731    node.visit_mut_with(&mut ClampReplacer {});
732}
733
734macro_rules! to_ident {
735    ($val:expr) => {{
736        ComponentValue::Ident(Box::new(Ident {
737            span: DUMMY_SP,
738            value: $val.into(),
739            raw: None,
740        }))
741    }};
742}
743
744macro_rules! to_integer {
745    ($val:expr) => {{
746        ComponentValue::Integer(Box::new(Integer {
747            span: DUMMY_SP,
748            value: $val,
749            raw: None,
750        }))
751    }};
752}
753
754macro_rules! to_number {
755    ($val:expr) => {{
756        ComponentValue::Number(Box::new(Number {
757            span: DUMMY_SP,
758            value: $val,
759            raw: None,
760        }))
761    }};
762}
763
764#[derive(Debug, PartialEq, Eq, Clone, Copy)]
765pub enum Prefix {
766    Webkit,
767    Moz,
768    O,
769    Ms,
770}
771
772#[derive(Default)]
773struct Prefixer {
774    env: Arc<Versions>,
775    in_keyframe_block: bool,
776    supports_condition: Option<SupportsCondition>,
777    simple_block: Option<SimpleBlock>,
778    rule_prefix: Option<Prefix>,
779    added_top_rules: Vec<(Prefix, Rule)>,
780    added_at_rules: Vec<(Prefix, Box<AtRule>)>,
781    added_qualified_rules: Vec<(Prefix, Box<QualifiedRule>)>,
782    added_declarations: Vec<Box<Declaration>>,
783}
784
785impl Prefixer {
786    fn add_at_rule(&mut self, prefix: Prefix, at_rule: &AtRule) {
787        if self.simple_block.is_none() {
788            self.added_top_rules
789                .push((prefix, Rule::AtRule(Box::new(at_rule.clone()))));
790        } else {
791            self.added_at_rules
792                .push((prefix, Box::new(at_rule.clone())));
793        }
794    }
795
796    fn is_duplicate(&self, name: &str) -> bool {
797        if let Some(simple_block) = &self.simple_block {
798            for n in simple_block.value.iter() {
799                if let ComponentValue::Declaration(declaration) = n {
800                    if declaration.name == *name {
801                        return true;
802                    }
803                }
804            }
805        }
806
807        false
808    }
809
810    fn add_declaration2<'a>(
811        &mut self,
812        n: &Declaration,
813        property: &str,
814        value: Option<Box<dyn 'a + Fn() -> Vec<ComponentValue>>>,
815    ) {
816        if should_prefix(property, &self.env, true) && !self.is_duplicate(property) {
817            let name = DeclarationName::Ident(Ident {
818                span: DUMMY_SP,
819                value: property.into(),
820                raw: None,
821            });
822
823            if let Some(value) = value {
824                let mut declaration = Declaration {
825                    span: n.span,
826                    name,
827                    value: value(),
828                    important: n.important.clone(),
829                };
830
831                // TODO should we handle with prefix?
832                declaration.visit_mut_with(self);
833
834                self.added_declarations.push(Box::new(declaration));
835            } else {
836                let mut declaration = Declaration {
837                    span: n.span,
838                    name,
839                    value: n.value.clone(),
840                    important: n.important.clone(),
841                };
842
843                declaration.visit_mut_with(self);
844
845                self.added_declarations.push(Box::new(declaration));
846            }
847        }
848    }
849
850    fn add_declaration3<'a>(
851        &mut self,
852        n: &Declaration,
853        prefix: Prefix,
854        property: &str,
855        value: Box<dyn 'a + Fn() -> Vec<ComponentValue>>,
856    ) {
857        if should_prefix(property, &self.env, true) {
858            // Use only specific prefix in prefixed at-rules or rule, i.e.
859            // don't use `-moz` prefix for properties in `@-webkit-keyframes` at-rule
860            if self.rule_prefix == Some(prefix) || self.rule_prefix.is_none() {
861                // Check we don't have prefixed property
862                if !self.is_duplicate(property) {
863                    let name = DeclarationName::Ident(Ident {
864                        span: DUMMY_SP,
865                        value: property.into(),
866                        raw: None,
867                    });
868
869                    self.added_declarations.push(Box::new(Declaration {
870                        span: n.span,
871                        name,
872                        value: value(),
873                        important: n.important.clone(),
874                    }));
875                }
876            }
877        }
878    }
879}
880
881impl VisitMut for Prefixer {
882    fn visit_mut_stylesheet(&mut self, stylesheet: &mut Stylesheet) {
883        let mut new_rules = Vec::with_capacity(stylesheet.rules.len());
884
885        for mut rule in take(&mut stylesheet.rules) {
886            rule.visit_mut_children_with(self);
887
888            for mut added_rule in take(&mut self.added_top_rules) {
889                let need_skip = new_rules
890                    .iter()
891                    .any(|existing_rule| added_rule.1.eq_ignore_span(existing_rule));
892
893                if need_skip {
894                    continue;
895                }
896
897                let old_rule_prefix = self.rule_prefix.take();
898
899                self.rule_prefix = Some(added_rule.0);
900
901                added_rule.1.visit_mut_children_with(self);
902
903                new_rules.push(added_rule.1);
904
905                self.rule_prefix = old_rule_prefix;
906            }
907
908            new_rules.push(rule);
909        }
910
911        stylesheet.rules = new_rules;
912    }
913
914    // TODO `selector()` supports
915    fn visit_mut_at_rule(&mut self, at_rule: &mut AtRule) {
916        let original_simple_block = at_rule.block.clone();
917
918        at_rule.visit_mut_children_with(self);
919
920        match &at_rule.name {
921            AtRuleName::Ident(Ident { span, value, .. }) if value == "viewport" => {
922                if should_prefix("@-o-viewport", &self.env, false) {
923                    self.add_at_rule(
924                        Prefix::Ms,
925                        &AtRule {
926                            span: at_rule.span,
927                            name: AtRuleName::Ident(Ident {
928                                span: *span,
929                                value: atom!("-ms-viewport"),
930                                raw: None,
931                            }),
932                            prelude: at_rule.prelude.clone(),
933                            block: original_simple_block.clone(),
934                        },
935                    );
936                }
937
938                if should_prefix("@-ms-viewport", &self.env, false) {
939                    self.add_at_rule(
940                        Prefix::O,
941                        &AtRule {
942                            span: at_rule.span,
943                            name: AtRuleName::Ident(Ident {
944                                span: *span,
945                                value: atom!("-o-viewport"),
946                                raw: None,
947                            }),
948                            prelude: at_rule.prelude.clone(),
949                            block: original_simple_block,
950                        },
951                    );
952                }
953            }
954            AtRuleName::Ident(Ident { span, value, .. }) if value == "keyframes" => {
955                if should_prefix("@-webkit-keyframes", &self.env, false) {
956                    self.add_at_rule(
957                        Prefix::Webkit,
958                        &AtRule {
959                            span: at_rule.span,
960                            name: AtRuleName::Ident(Ident {
961                                span: *span,
962                                value: atom!("-webkit-keyframes"),
963                                raw: None,
964                            }),
965                            prelude: at_rule.prelude.clone(),
966                            block: original_simple_block.clone(),
967                        },
968                    );
969                }
970
971                if should_prefix("@-moz-keyframes", &self.env, false) {
972                    self.add_at_rule(
973                        Prefix::Moz,
974                        &AtRule {
975                            span: at_rule.span,
976                            name: AtRuleName::Ident(Ident {
977                                span: *span,
978                                value: atom!("-moz-keyframes"),
979                                raw: None,
980                            }),
981                            prelude: at_rule.prelude.clone(),
982                            block: original_simple_block.clone(),
983                        },
984                    );
985                }
986
987                if should_prefix("@-o-keyframes", &self.env, false) {
988                    self.add_at_rule(
989                        Prefix::O,
990                        &AtRule {
991                            span: at_rule.span,
992                            name: AtRuleName::Ident(Ident {
993                                span: DUMMY_SP,
994                                value: atom!("-o-keyframes"),
995                                raw: None,
996                            }),
997                            prelude: at_rule.prelude.clone(),
998                            block: original_simple_block,
999                        },
1000                    );
1001                }
1002            }
1003            _ => {}
1004        }
1005    }
1006
1007    fn visit_mut_import_conditions(&mut self, import_conditions: &mut ImportConditions) {
1008        import_conditions.visit_mut_children_with(self);
1009
1010        // ComponentValue::Declaration(declaration)
1011        if !self.added_declarations.is_empty() {
1012            if let Some(supports) = import_conditions.supports.take() {
1013                let mut conditions = Vec::with_capacity(1 + self.added_declarations.len());
1014
1015                if let Some(ComponentValue::Declaration(declaration)) = supports.value.first() {
1016                    conditions.push(SupportsConditionType::SupportsInParens(
1017                        SupportsInParens::Feature(SupportsFeature::Declaration(
1018                            declaration.clone(),
1019                        )),
1020                    ));
1021                }
1022
1023                for n in take(&mut self.added_declarations) {
1024                    let supports_condition_type = SupportsConditionType::Or(SupportsOr {
1025                        span: DUMMY_SP,
1026                        keyword: None,
1027                        condition: Box::new(SupportsInParens::Feature(
1028                            SupportsFeature::Declaration(n),
1029                        )),
1030                    });
1031
1032                    conditions.push(supports_condition_type);
1033                }
1034
1035                import_conditions.supports = Some(Box::new(Function {
1036                    span: supports.span,
1037                    name: supports.name,
1038                    value: vec![ComponentValue::SupportsCondition(Box::new(
1039                        SupportsCondition {
1040                            span: DUMMY_SP,
1041                            conditions,
1042                        },
1043                    ))],
1044                }));
1045            }
1046        }
1047    }
1048
1049    fn visit_mut_supports_condition(&mut self, supports_condition: &mut SupportsCondition) {
1050        let old_supports_condition = self.supports_condition.take();
1051
1052        self.supports_condition = Some(supports_condition.clone());
1053
1054        supports_condition.visit_mut_children_with(self);
1055
1056        self.supports_condition = old_supports_condition;
1057    }
1058
1059    fn visit_mut_supports_in_parens(&mut self, supports_in_parens: &mut SupportsInParens) {
1060        supports_in_parens.visit_mut_children_with(self);
1061
1062        if let Some(supports_condition) = &self.supports_condition {
1063            match supports_in_parens {
1064                SupportsInParens::Feature(_) if !self.added_declarations.is_empty() => {
1065                    let mut conditions = Vec::with_capacity(1 + self.added_declarations.len());
1066
1067                    conditions.push(swc_css_ast::SupportsConditionType::SupportsInParens(
1068                        supports_in_parens.clone(),
1069                    ));
1070
1071                    for n in take(&mut self.added_declarations) {
1072                        let supports_condition_type = SupportsConditionType::Or(SupportsOr {
1073                            span: DUMMY_SP,
1074                            keyword: None,
1075                            condition: Box::new(SupportsInParens::Feature(
1076                                SupportsFeature::Declaration(n),
1077                            )),
1078                        });
1079
1080                        let need_skip =
1081                            supports_condition
1082                                .conditions
1083                                .iter()
1084                                .any(|existing_condition_type| {
1085                                    supports_condition_type.eq_ignore_span(existing_condition_type)
1086                                });
1087
1088                        if need_skip {
1089                            continue;
1090                        }
1091
1092                        conditions.push(supports_condition_type);
1093                    }
1094
1095                    if conditions.len() > 1 {
1096                        *supports_in_parens =
1097                            SupportsInParens::SupportsCondition(SupportsCondition {
1098                                span: DUMMY_SP,
1099                                conditions,
1100                            });
1101                    }
1102                }
1103                _ => {}
1104            }
1105        }
1106    }
1107
1108    fn visit_mut_media_query_list(&mut self, media_query_list: &mut MediaQueryList) {
1109        media_query_list.visit_mut_children_with(self);
1110
1111        let mut new_queries = Vec::new();
1112
1113        for n in &media_query_list.queries {
1114            if should_prefix("-webkit-min-device-pixel-ratio", &self.env, false) {
1115                let mut new_media_query = n.clone();
1116
1117                replace_media_feature_resolution_on_legacy_variant(
1118                    &mut new_media_query,
1119                    "min-resolution",
1120                    "-webkit-min-device-pixel-ratio",
1121                );
1122                replace_media_feature_resolution_on_legacy_variant(
1123                    &mut new_media_query,
1124                    "max-resolution",
1125                    "-webkit-max-device-pixel-ratio",
1126                );
1127
1128                let need_skip = media_query_list.queries.iter().any(|existing_media_query| {
1129                    new_media_query.eq_ignore_span(existing_media_query)
1130                });
1131
1132                if !need_skip {
1133                    new_queries.push(new_media_query);
1134                }
1135            }
1136
1137            if should_prefix("min--moz-device-pixel-ratio", &self.env, false) {
1138                let mut new_media_query = n.clone();
1139
1140                replace_media_feature_resolution_on_legacy_variant(
1141                    &mut new_media_query,
1142                    "min-resolution",
1143                    "min--moz-device-pixel-ratio",
1144                );
1145                replace_media_feature_resolution_on_legacy_variant(
1146                    &mut new_media_query,
1147                    "max-resolution",
1148                    "max--moz-device-pixel-ratio",
1149                );
1150
1151                let need_skip = media_query_list.queries.iter().any(|existing_media_query| {
1152                    new_media_query.eq_ignore_span(existing_media_query)
1153                });
1154
1155                if !need_skip {
1156                    new_queries.push(new_media_query);
1157                }
1158            }
1159
1160            // TODO opera support
1161        }
1162
1163        media_query_list.queries.extend(new_queries);
1164    }
1165
1166    fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) {
1167        let original_simple_block = n.block.clone();
1168
1169        n.visit_mut_children_with(self);
1170
1171        if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
1172            let mut new_webkit_prelude = n.prelude.clone();
1173
1174            if should_prefix(":-webkit-autofill", &self.env, false) {
1175                replace_pseudo_class_selector_name(
1176                    &mut new_webkit_prelude,
1177                    "autofill",
1178                    "-webkit-autofill",
1179                );
1180            }
1181
1182            if should_prefix(":-webkit-any-link", &self.env, false) {
1183                replace_pseudo_class_selector_name(
1184                    &mut new_webkit_prelude,
1185                    "any-link",
1186                    "-webkit-any-link",
1187                );
1188            }
1189
1190            if should_prefix(":-webkit-full-screen", &self.env, false) {
1191                replace_pseudo_class_selector_name(
1192                    &mut new_webkit_prelude,
1193                    "fullscreen",
1194                    "-webkit-full-screen",
1195                );
1196            }
1197
1198            if should_prefix("::-webkit-file-upload-button", &self.env, false) {
1199                replace_pseudo_element_selector_name(
1200                    &mut new_webkit_prelude,
1201                    "file-selector-button",
1202                    "-webkit-file-upload-button",
1203                );
1204            }
1205
1206            if should_prefix("::-webkit-backdrop", &self.env, false) {
1207                replace_pseudo_element_selector_name(
1208                    &mut new_webkit_prelude,
1209                    "backdrop",
1210                    "-webkit-backdrop",
1211                );
1212            }
1213
1214            if should_prefix("::-webkit-file-upload-button", &self.env, false) {
1215                replace_pseudo_element_selector_name(
1216                    &mut new_webkit_prelude,
1217                    "placeholder",
1218                    "-webkit-input-placeholder",
1219                );
1220            }
1221
1222            if !n.prelude.eq_ignore_span(&new_webkit_prelude) {
1223                let qualified_rule = Box::new(QualifiedRule {
1224                    span: DUMMY_SP,
1225                    prelude: new_webkit_prelude,
1226                    block: original_simple_block.clone(),
1227                });
1228
1229                if self.simple_block.is_none() {
1230                    self.added_top_rules
1231                        .push((Prefix::Webkit, Rule::QualifiedRule(qualified_rule)));
1232                } else {
1233                    self.added_qualified_rules
1234                        .push((Prefix::Webkit, qualified_rule));
1235                }
1236            }
1237        }
1238
1239        if self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none() {
1240            let mut new_moz_prelude = n.prelude.clone();
1241
1242            if should_prefix(":-moz-read-only", &self.env, false) {
1243                replace_pseudo_class_selector_name(
1244                    &mut new_moz_prelude,
1245                    "read-only",
1246                    "-moz-read-only",
1247                );
1248            }
1249
1250            if should_prefix(":-moz-read-write", &self.env, false) {
1251                replace_pseudo_class_selector_name(
1252                    &mut new_moz_prelude,
1253                    "read-write",
1254                    "-moz-read-write",
1255                );
1256            }
1257
1258            if should_prefix(":-moz-any-link", &self.env, false) {
1259                replace_pseudo_class_selector_name(
1260                    &mut new_moz_prelude,
1261                    "any-link",
1262                    "-moz-any-link",
1263                );
1264            }
1265
1266            if should_prefix(":-moz-full-screen", &self.env, false) {
1267                replace_pseudo_class_selector_name(
1268                    &mut new_moz_prelude,
1269                    "fullscreen",
1270                    "-moz-full-screen",
1271                );
1272            }
1273
1274            if should_prefix("::-moz-selection", &self.env, false) {
1275                replace_pseudo_element_selector_name(
1276                    &mut new_moz_prelude,
1277                    "selection",
1278                    "-moz-selection",
1279                );
1280            }
1281
1282            if should_prefix(":-moz-placeholder", &self.env, false) {
1283                let mut new_moz_prelude_with_previous = new_moz_prelude.clone();
1284
1285                replace_pseudo_class_selector_on_pseudo_element_selector(
1286                    &mut new_moz_prelude_with_previous,
1287                    "placeholder",
1288                    "-moz-placeholder",
1289                );
1290
1291                if !new_moz_prelude_with_previous.eq_ignore_span(&new_moz_prelude) {
1292                    let qualified_rule = Box::new(QualifiedRule {
1293                        span: DUMMY_SP,
1294                        prelude: new_moz_prelude_with_previous,
1295                        block: original_simple_block.clone(),
1296                    });
1297
1298                    if self.simple_block.is_none() {
1299                        self.added_top_rules
1300                            .push((Prefix::Moz, Rule::QualifiedRule(qualified_rule)));
1301                    } else {
1302                        self.added_qualified_rules
1303                            .push((Prefix::Moz, qualified_rule));
1304                    }
1305                }
1306            }
1307
1308            if should_prefix("::-moz-placeholder", &self.env, false) {
1309                replace_pseudo_element_selector_name(
1310                    &mut new_moz_prelude,
1311                    "placeholder",
1312                    "-moz-placeholder",
1313                );
1314            }
1315
1316            if !n.prelude.eq_ignore_span(&new_moz_prelude) {
1317                let qualified_rule = QualifiedRule {
1318                    span: DUMMY_SP,
1319                    prelude: new_moz_prelude,
1320                    block: original_simple_block.clone(),
1321                };
1322
1323                if self.simple_block.is_none() {
1324                    self.added_top_rules
1325                        .push((Prefix::Moz, Rule::QualifiedRule(Box::new(qualified_rule))));
1326                } else {
1327                    self.added_qualified_rules
1328                        .push((Prefix::Moz, Box::new(qualified_rule)));
1329                }
1330            }
1331        }
1332
1333        if self.rule_prefix == Some(Prefix::Ms) || self.rule_prefix.is_none() {
1334            let mut new_ms_prelude = n.prelude.clone();
1335
1336            if should_prefix(":-ms-fullscreen", &self.env, false) {
1337                replace_pseudo_class_selector_name(
1338                    &mut new_ms_prelude,
1339                    "fullscreen",
1340                    "-ms-fullscreen",
1341                );
1342            }
1343
1344            if should_prefix(":-ms-input-placeholder", &self.env, false) {
1345                replace_pseudo_class_selector_name(
1346                    &mut new_ms_prelude,
1347                    "placeholder-shown",
1348                    "-ms-input-placeholder",
1349                );
1350            }
1351
1352            if should_prefix("::-ms-browse", &self.env, false) {
1353                replace_pseudo_element_selector_name(
1354                    &mut new_ms_prelude,
1355                    "file-selector-button",
1356                    "-ms-browse",
1357                );
1358            }
1359
1360            if should_prefix("::-ms-backdrop", &self.env, false) {
1361                replace_pseudo_element_selector_name(
1362                    &mut new_ms_prelude,
1363                    "backdrop",
1364                    "-ms-backdrop",
1365                );
1366            }
1367
1368            if should_prefix(":-ms-input-placeholder", &self.env, false) {
1369                let mut new_ms_prelude_with_previous = new_ms_prelude.clone();
1370
1371                replace_pseudo_class_selector_on_pseudo_element_selector(
1372                    &mut new_ms_prelude_with_previous,
1373                    "placeholder",
1374                    "-ms-input-placeholder",
1375                );
1376
1377                if !new_ms_prelude_with_previous.eq_ignore_span(&new_ms_prelude) {
1378                    let qualified_rule = Box::new(QualifiedRule {
1379                        span: DUMMY_SP,
1380                        prelude: new_ms_prelude_with_previous,
1381                        block: original_simple_block.clone(),
1382                    });
1383
1384                    if self.simple_block.is_none() {
1385                        self.added_top_rules
1386                            .push((Prefix::Ms, Rule::QualifiedRule(qualified_rule)));
1387                    } else {
1388                        self.added_qualified_rules
1389                            .push((Prefix::Ms, qualified_rule));
1390                    }
1391                }
1392            }
1393
1394            if should_prefix("::-ms-input-placeholder", &self.env, false) {
1395                replace_pseudo_element_selector_name(
1396                    &mut new_ms_prelude,
1397                    "placeholder",
1398                    "-ms-input-placeholder",
1399                );
1400            }
1401
1402            if !n.prelude.eq_ignore_span(&new_ms_prelude) {
1403                let qualified_rule = Box::new(QualifiedRule {
1404                    span: DUMMY_SP,
1405                    prelude: new_ms_prelude,
1406                    block: original_simple_block,
1407                });
1408
1409                if self.simple_block.is_none() {
1410                    self.added_top_rules
1411                        .push((Prefix::Ms, Rule::QualifiedRule(qualified_rule)));
1412                } else {
1413                    self.added_qualified_rules
1414                        .push((Prefix::Ms, qualified_rule));
1415                }
1416            }
1417        }
1418    }
1419
1420    fn visit_mut_keyframe_block(&mut self, n: &mut KeyframeBlock) {
1421        let old_in_keyframe_block = self.in_keyframe_block;
1422
1423        self.in_keyframe_block = true;
1424
1425        n.visit_mut_children_with(self);
1426
1427        self.in_keyframe_block = old_in_keyframe_block;
1428    }
1429
1430    fn visit_mut_simple_block(&mut self, simple_block: &mut SimpleBlock) {
1431        let old_simple_block = self.simple_block.take();
1432
1433        self.simple_block = Some(simple_block.clone());
1434
1435        let mut new = Vec::with_capacity(simple_block.value.len());
1436
1437        for mut n in take(&mut simple_block.value) {
1438            n.visit_mut_children_with(self);
1439
1440            match n {
1441                ComponentValue::Declaration(_) => {
1442                    new.extend(
1443                        self.added_declarations
1444                            .drain(..)
1445                            .map(ComponentValue::Declaration),
1446                    );
1447
1448                    for mut n in take(&mut self.added_at_rules) {
1449                        let old_rule_prefix = self.rule_prefix.take();
1450
1451                        self.rule_prefix = Some(n.0);
1452
1453                        n.1.visit_mut_children_with(self);
1454
1455                        new.push(ComponentValue::AtRule(n.1));
1456
1457                        self.rule_prefix = old_rule_prefix;
1458                    }
1459                }
1460                ComponentValue::QualifiedRule(_) | ComponentValue::AtRule(_) => {
1461                    new.extend(
1462                        self.added_declarations
1463                            .drain(..)
1464                            .map(ComponentValue::Declaration),
1465                    );
1466
1467                    for mut n in take(&mut self.added_qualified_rules) {
1468                        let old_rule_prefix = self.rule_prefix.take();
1469
1470                        self.rule_prefix = Some(n.0);
1471
1472                        n.1.visit_mut_children_with(self);
1473
1474                        new.push(ComponentValue::QualifiedRule(n.1));
1475
1476                        self.rule_prefix = old_rule_prefix;
1477                    }
1478
1479                    for mut n in take(&mut self.added_at_rules) {
1480                        let old_rule_prefix = self.rule_prefix.take();
1481
1482                        self.rule_prefix = Some(n.0);
1483
1484                        n.1.visit_mut_children_with(self);
1485
1486                        new.push(ComponentValue::AtRule(n.1));
1487
1488                        self.rule_prefix = old_rule_prefix;
1489                    }
1490                }
1491                _ => {}
1492            }
1493
1494            new.push(n);
1495        }
1496
1497        simple_block.value = new;
1498
1499        self.simple_block = old_simple_block;
1500    }
1501
1502    fn visit_mut_declaration(&mut self, n: &mut Declaration) {
1503        n.visit_mut_children_with(self);
1504
1505        if n.value.is_empty() {
1506            return;
1507        }
1508
1509        let is_dashed_ident = match n.name {
1510            DeclarationName::Ident(_) => false,
1511            DeclarationName::DashedIdent(_) => true,
1512        };
1513
1514        if is_dashed_ident {
1515            return;
1516        }
1517
1518        let name = match &n.name {
1519            DeclarationName::Ident(ident) => &ident.value,
1520            _ => {
1521                unreachable!();
1522            }
1523        };
1524
1525        // TODO make it lazy?
1526        let mut webkit_value = n.value.clone();
1527
1528        if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
1529            if should_prefix("-webkit-filter()", &self.env, false) {
1530                replace_function_name(&mut webkit_value, "filter", "-webkit-filter");
1531            }
1532
1533            if should_prefix("-webkit-image-set()", &self.env, false) {
1534                replace_image_set_function_on_legacy_variant(
1535                    &mut webkit_value,
1536                    "image-set",
1537                    "-webkit-image-set",
1538                );
1539            }
1540
1541            if should_prefix("-webkit-calc()", &self.env, false) {
1542                replace_calc(&mut webkit_value, Some("-webkit-calc"));
1543            }
1544
1545            if should_prefix("-webkit-cross-fade()", &self.env, false) {
1546                replace_cross_fade_function_on_legacy_variant(
1547                    &mut webkit_value,
1548                    "cross-fade",
1549                    "-webkit-cross-fade",
1550                );
1551            }
1552
1553            if should_prefix("-webkit-linear-gradient()", &self.env, false) {
1554                replace_gradient_function_on_legacy_variant(
1555                    &mut webkit_value,
1556                    "linear-gradient",
1557                    "-webkit-linear-gradient",
1558                );
1559            }
1560
1561            if should_prefix("-webkit-repeating-linear-gradient()", &self.env, false) {
1562                replace_gradient_function_on_legacy_variant(
1563                    &mut webkit_value,
1564                    "repeating-linear-gradient",
1565                    "-webkit-repeating-linear-gradient",
1566                );
1567            }
1568
1569            if should_prefix("-webkit-radial-gradient()", &self.env, false) {
1570                replace_gradient_function_on_legacy_variant(
1571                    &mut webkit_value,
1572                    "radial-gradient",
1573                    "-webkit-radial-gradient",
1574                );
1575            }
1576
1577            if should_prefix("-webkit-repeating-radial-gradient()", &self.env, false) {
1578                replace_gradient_function_on_legacy_variant(
1579                    &mut webkit_value,
1580                    "repeating-radial-gradient",
1581                    "-webkit-repeating-radial-gradient",
1582                );
1583            }
1584
1585            if should_prefix("clamp()", &self.env, true) {
1586                replace_clamp(&mut webkit_value);
1587            }
1588        }
1589
1590        let mut moz_value = n.value.clone();
1591
1592        if self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none() {
1593            if should_prefix("-moz-element()", &self.env, false) {
1594                replace_function_name(&mut moz_value, "element", "-moz-element");
1595            }
1596
1597            if should_prefix("-moz-calc()", &self.env, false) {
1598                replace_calc(&mut moz_value, Some("-moz-calc"));
1599            }
1600
1601            if should_prefix("-moz-linear-gradient()", &self.env, false) {
1602                replace_gradient_function_on_legacy_variant(
1603                    &mut moz_value,
1604                    "linear-gradient",
1605                    "-moz-linear-gradient",
1606                );
1607            }
1608
1609            if should_prefix("-moz-repeating-linear-gradient()", &self.env, false) {
1610                replace_gradient_function_on_legacy_variant(
1611                    &mut moz_value,
1612                    "repeating-linear-gradient",
1613                    "-moz-repeating-linear-gradient",
1614                );
1615            }
1616
1617            if should_prefix("-moz-radial-gradient()", &self.env, false) {
1618                replace_gradient_function_on_legacy_variant(
1619                    &mut moz_value,
1620                    "radial-gradient",
1621                    "-moz-radial-gradient",
1622                );
1623            }
1624
1625            if should_prefix("-moz-repeating-radial-gradient()", &self.env, false) {
1626                replace_gradient_function_on_legacy_variant(
1627                    &mut moz_value,
1628                    "repeating-radial-gradient",
1629                    "-moz-repeating-radial-gradient",
1630                );
1631            }
1632        }
1633
1634        let mut o_value = n.value.clone();
1635
1636        if self.rule_prefix == Some(Prefix::O) || self.rule_prefix.is_none() {
1637            if should_prefix("-o-repeating-linear-gradient()", &self.env, false) {
1638                replace_gradient_function_on_legacy_variant(
1639                    &mut o_value,
1640                    "linear-gradient",
1641                    "-o-linear-gradient",
1642                );
1643            }
1644
1645            if should_prefix("-o-repeating-linear-gradient()", &self.env, false) {
1646                replace_gradient_function_on_legacy_variant(
1647                    &mut o_value,
1648                    "repeating-linear-gradient",
1649                    "-o-repeating-linear-gradient",
1650                );
1651            }
1652
1653            if should_prefix("-o-radial-gradient()", &self.env, false) {
1654                replace_gradient_function_on_legacy_variant(
1655                    &mut o_value,
1656                    "radial-gradient",
1657                    "-o-radial-gradient",
1658                );
1659            }
1660
1661            if should_prefix("-o-repeating-radial-gradient()", &self.env, false) {
1662                replace_gradient_function_on_legacy_variant(
1663                    &mut o_value,
1664                    "repeating-radial-gradient",
1665                    "-o-repeating-radial-gradient",
1666                );
1667            }
1668        }
1669
1670        let mut ms_value = n.value.clone();
1671
1672        // TODO avoid insert moz/etc prefixes for `appearance: -webkit-button;`
1673        // TODO check logic for duplicate values
1674        macro_rules! add_declaration {
1675            ($prefix:expr, $property:expr, None) => {{
1676                self.add_declaration3(
1677                    &n,
1678                    $prefix,
1679                    $property,
1680                    Box::new(|| match $prefix {
1681                        Prefix::Webkit => webkit_value.clone(),
1682                        Prefix::Moz => moz_value.clone(),
1683                        Prefix::O => o_value.clone(),
1684                        Prefix::Ms => ms_value.clone(),
1685                    }),
1686                );
1687            }};
1688
1689            ($prefix:expr,$property:expr, $value:expr) => {{
1690                self.add_declaration3(&n, $prefix, $property, $value);
1691            }};
1692
1693            ($property:expr, $value:expr) => {{
1694                self.add_declaration2(&n, $property, $value);
1695            }};
1696        }
1697
1698        match &**name {
1699            "appearance" => {
1700                add_declaration!(Prefix::Webkit, "-webkit-appearance", None);
1701                add_declaration!(Prefix::Moz, "-moz-appearance", None);
1702                add_declaration!(Prefix::Ms, "-ms-appearance", None);
1703            }
1704
1705            "animation" => {
1706                let need_prefix = n.value.iter().all(|n| match n {
1707                    ComponentValue::Ident(ident) => !matches!(
1708                        &*ident.value.to_ascii_lowercase(),
1709                        "reverse" | "alternate-reverse"
1710                    ),
1711                    _ => true,
1712                });
1713
1714                if need_prefix {
1715                    add_declaration!(Prefix::Webkit, "-webkit-animation", None);
1716                    add_declaration!(Prefix::Moz, "-moz-animation", None);
1717                    add_declaration!(Prefix::O, "-o-animation", None);
1718                }
1719            }
1720
1721            "animation-name" => {
1722                add_declaration!(Prefix::Webkit, "-webkit-animation-name", None);
1723                add_declaration!(Prefix::Moz, "-moz-animation-name", None);
1724                add_declaration!(Prefix::O, "-o-animation-name", None);
1725            }
1726
1727            "animation-duration" => {
1728                add_declaration!(Prefix::Webkit, "-webkit-animation-duration", None);
1729                add_declaration!(Prefix::Moz, "-moz-animation-duration", None);
1730                add_declaration!(Prefix::O, "-o-animation-duration", None);
1731            }
1732
1733            "animation-delay" => {
1734                add_declaration!(Prefix::Webkit, "-webkit-animation-delay", None);
1735                add_declaration!(Prefix::Moz, "-moz-animation-delay", None);
1736                add_declaration!(Prefix::O, "-o-animation-delay", None);
1737            }
1738
1739            "animation-direction" => {
1740                if let ComponentValue::Ident(ident) = &n.value[0] {
1741                    match &*ident.value.to_ascii_lowercase() {
1742                        "alternate-reverse" | "reverse" => {}
1743                        _ => {
1744                            add_declaration!(Prefix::Webkit, "-webkit-animation-direction", None);
1745                            add_declaration!(Prefix::Moz, "-moz-animation-direction", None);
1746                            add_declaration!(Prefix::O, "-o-animation-direction", None);
1747                        }
1748                    }
1749                }
1750            }
1751
1752            "animation-fill-mode" => {
1753                add_declaration!(Prefix::Webkit, "-webkit-animation-fill-mode", None);
1754                add_declaration!(Prefix::Moz, "-moz-animation-fill-mode", None);
1755                add_declaration!(Prefix::O, "-o-animation-fill-mode", None);
1756            }
1757
1758            "animation-iteration-count" => {
1759                add_declaration!(Prefix::Webkit, "-webkit-animation-iteration-count", None);
1760                add_declaration!(Prefix::Moz, "-moz-animation-iteration-count", None);
1761                add_declaration!(Prefix::O, "-o-animation-iteration-count", None);
1762            }
1763
1764            "animation-play-state" => {
1765                add_declaration!(Prefix::Webkit, "-webkit-animation-play-state", None);
1766                add_declaration!(Prefix::Moz, "-moz-animation-play-state", None);
1767                add_declaration!(Prefix::O, "-o-animation-play-state", None);
1768            }
1769
1770            "animation-timing-function" => {
1771                add_declaration!(Prefix::Webkit, "-webkit-animation-timing-function", None);
1772                add_declaration!(Prefix::Moz, "-moz-animation-timing-function", None);
1773                add_declaration!(Prefix::O, "-o-animation-timing-function", None);
1774            }
1775
1776            "background-clip" => {
1777                if let ComponentValue::Ident(ident) = &n.value[0] {
1778                    if ident.value.eq_ignore_ascii_case("text") {
1779                        add_declaration!(Prefix::Webkit, "-webkit-background-clip", None);
1780                    }
1781                }
1782            }
1783
1784            "box-decoration-break" => {
1785                add_declaration!(Prefix::Webkit, "-webkit-box-decoration-break", None);
1786            }
1787
1788            "box-sizing" => {
1789                add_declaration!(Prefix::Webkit, "-webkit-box-sizing", None);
1790                add_declaration!(Prefix::Moz, "-moz-box-sizing", None);
1791            }
1792
1793            "color-adjust" => {
1794                add_declaration!(Prefix::Webkit, "-webkit-print-color-adjust", None);
1795            }
1796
1797            "print-color-adjust" => {
1798                add_declaration!(Prefix::Moz, "color-adjust", None);
1799                add_declaration!(Prefix::Webkit, "-webkit-print-color-adjust", None);
1800            }
1801
1802            "columns" => {
1803                add_declaration!(Prefix::Webkit, "-webkit-columns", None);
1804                add_declaration!(Prefix::Moz, "-moz-columns", None);
1805            }
1806
1807            "column-width" => {
1808                add_declaration!(Prefix::Webkit, "-webkit-column-width", None);
1809                add_declaration!(Prefix::Moz, "-moz-column-width", None);
1810            }
1811
1812            "column-gap" => {
1813                add_declaration!(Prefix::Webkit, "-webkit-column-gap", None);
1814                add_declaration!(Prefix::Moz, "-moz-column-gap", None);
1815            }
1816
1817            "column-rule" => {
1818                add_declaration!(Prefix::Webkit, "-webkit-column-rule", None);
1819                add_declaration!(Prefix::Moz, "-moz-column-rule", None);
1820            }
1821
1822            "column-rule-color" => {
1823                add_declaration!(Prefix::Webkit, "-webkit-column-rule-color", None);
1824                add_declaration!(Prefix::Moz, "-moz-column-rule-color", None);
1825            }
1826
1827            "column-rule-width" => {
1828                add_declaration!(Prefix::Webkit, "-webkit-column-rule-width", None);
1829                add_declaration!(Prefix::Moz, "-moz-column-rule-width", None);
1830            }
1831
1832            "column-count" => {
1833                add_declaration!(Prefix::Webkit, "-webkit-column-count", None);
1834                add_declaration!(Prefix::Moz, "-moz-column-count", None);
1835            }
1836
1837            "column-rule-style" => {
1838                add_declaration!(Prefix::Webkit, "-webkit-column-rule-style", None);
1839                add_declaration!(Prefix::Moz, "-moz-column-rule-style", None);
1840            }
1841
1842            "column-span" => {
1843                add_declaration!(Prefix::Webkit, "-webkit-column-span", None);
1844                add_declaration!(Prefix::Moz, "-moz-column-span", None);
1845            }
1846
1847            "column-fill" => {
1848                add_declaration!(Prefix::Webkit, "-webkit-column-fill", None);
1849                add_declaration!(Prefix::Moz, "-moz-column-fill", None);
1850            }
1851
1852            "cursor" => {
1853                if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
1854                    if should_prefix("-o-repeating-radial-gradient()", &self.env, false) {
1855                        replace_ident(&mut webkit_value, "zoom-in", "-webkit-zoom-in");
1856                    }
1857
1858                    if should_prefix("-o-repeating-radial-gradient()", &self.env, false) {
1859                        replace_ident(&mut webkit_value, "zoom-out", "-webkit-zoom-out");
1860                    }
1861
1862                    if should_prefix("-webkit-grab", &self.env, false) {
1863                        replace_ident(&mut webkit_value, "grab", "-webkit-grab");
1864                    }
1865
1866                    if should_prefix("-webkit-grabbing", &self.env, false) {
1867                        replace_ident(&mut webkit_value, "grabbing", "-webkit-grabbing");
1868                    }
1869                }
1870
1871                if self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none() {
1872                    if should_prefix("-moz-zoom-in", &self.env, false) {
1873                        replace_ident(&mut moz_value, "zoom-in", "-moz-zoom-in");
1874                    }
1875
1876                    if should_prefix("-moz-zoom-out", &self.env, false) {
1877                        replace_ident(&mut moz_value, "zoom-out", "-moz-zoom-out");
1878                    }
1879
1880                    if should_prefix("-moz-grab", &self.env, false) {
1881                        replace_ident(&mut moz_value, "grab", "-moz-grab");
1882                    }
1883
1884                    if should_prefix("-moz-grabbing", &self.env, false) {
1885                        replace_ident(&mut moz_value, "grabbing", "-moz-grabbing");
1886                    }
1887                }
1888            }
1889
1890            "display" => {
1891                if n.value.len() == 1 {
1892                    if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
1893                        let mut old_spec_webkit_value = webkit_value.clone();
1894
1895                        if should_prefix("-webkit-box", &self.env, false) {
1896                            replace_ident(&mut old_spec_webkit_value, "flex", "-webkit-box");
1897                        }
1898
1899                        if should_prefix("-webkit-inline-box", &self.env, false) {
1900                            replace_ident(
1901                                &mut old_spec_webkit_value,
1902                                "inline-flex",
1903                                "-webkit-inline-box",
1904                            );
1905                        }
1906
1907                        if !n.value.eq_ignore_span(&old_spec_webkit_value) {
1908                            self.added_declarations.push(Box::new(Declaration {
1909                                span: n.span,
1910                                name: n.name.clone(),
1911                                value: old_spec_webkit_value,
1912                                important: n.important.clone(),
1913                            }));
1914                        }
1915
1916                        if should_prefix("-webkit-flex:display", &self.env, false) {
1917                            replace_ident(&mut webkit_value, "flex", "-webkit-flex");
1918                        }
1919
1920                        if should_prefix("-webkit-inline-flex", &self.env, false) {
1921                            replace_ident(&mut webkit_value, "inline-flex", "-webkit-inline-flex");
1922                        }
1923                    }
1924
1925                    if self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none() {
1926                        if should_prefix("-moz-box", &self.env, false) {
1927                            replace_ident(&mut moz_value, "flex", "-moz-box");
1928                        }
1929
1930                        if should_prefix("-moz-inline-box", &self.env, false) {
1931                            replace_ident(&mut moz_value, "inline-flex", "-moz-inline-box");
1932                        }
1933                    }
1934
1935                    if self.rule_prefix == Some(Prefix::Ms) || self.rule_prefix.is_none() {
1936                        if should_prefix("-ms-flexbox", &self.env, false) {
1937                            replace_ident(&mut ms_value, "flex", "-ms-flexbox");
1938                        }
1939
1940                        if should_prefix("-ms-inline-flexbox", &self.env, false) {
1941                            replace_ident(&mut ms_value, "inline-flex", "-ms-inline-flexbox");
1942                        }
1943                    }
1944                } else if n.value.len() == 2
1945                    && should_prefix("display:multi-keyword-values", &self.env, false)
1946                {
1947                    if let (
1948                        Some(ComponentValue::Ident(first)),
1949                        Some(ComponentValue::Ident(second)),
1950                    ) = (n.value.first(), n.value.get(1))
1951                    {
1952                        match (&*first.value, &*second.value) {
1953                            ("block", "flow") | ("flow", "block") => {
1954                                self.added_declarations.push(Box::new(Declaration {
1955                                    span: n.span,
1956                                    name: n.name.clone(),
1957                                    value: vec![to_ident!("block")],
1958                                    important: n.important.clone(),
1959                                }));
1960                            }
1961                            ("block", "flow-root") | ("flow-root", "block") => {
1962                                self.added_declarations.push(Box::new(Declaration {
1963                                    span: n.span,
1964                                    name: n.name.clone(),
1965                                    value: vec![to_ident!("flow-root")],
1966                                    important: n.important.clone(),
1967                                }));
1968                            }
1969                            ("inline", "flow") | ("flow", "inline") => {
1970                                self.added_declarations.push(Box::new(Declaration {
1971                                    span: n.span,
1972                                    name: n.name.clone(),
1973                                    value: vec![to_ident!("inline")],
1974                                    important: n.important.clone(),
1975                                }));
1976                            }
1977                            ("inline", "flow-root") | ("flow-root", "inline") => {
1978                                self.added_declarations.push(Box::new(Declaration {
1979                                    span: n.span,
1980                                    name: n.name.clone(),
1981                                    value: vec![to_ident!("inline-block")],
1982                                    important: n.important.clone(),
1983                                }));
1984                            }
1985                            ("run-in", "flow") | ("flow", "run-in") => {
1986                                self.added_declarations.push(Box::new(Declaration {
1987                                    span: n.span,
1988                                    name: n.name.clone(),
1989                                    value: vec![to_ident!("run-in")],
1990                                    important: n.important.clone(),
1991                                }));
1992                            }
1993                            ("block", "flex") | ("flex", "block") => {
1994                                let mut declaration = Declaration {
1995                                    span: n.span,
1996                                    name: n.name.clone(),
1997                                    value: vec![to_ident!("flex")],
1998                                    important: n.important.clone(),
1999                                };
2000
2001                                declaration.visit_mut_with(self);
2002
2003                                self.added_declarations.push(Box::new(declaration));
2004                            }
2005                            ("inline", "flex") | ("flex", "inline") => {
2006                                let mut declaration = Declaration {
2007                                    span: n.span,
2008                                    name: n.name.clone(),
2009                                    value: vec![to_ident!("inline-flex")],
2010                                    important: n.important.clone(),
2011                                };
2012
2013                                declaration.visit_mut_with(self);
2014
2015                                self.added_declarations.push(Box::new(declaration));
2016                            }
2017                            ("block", "grid") | ("grid", "block") => {
2018                                self.added_declarations.push(Box::new(Declaration {
2019                                    span: n.span,
2020                                    name: n.name.clone(),
2021                                    value: vec![to_ident!("grid")],
2022                                    important: n.important.clone(),
2023                                }));
2024                            }
2025                            ("inline", "grid") | ("grid", "inline") => {
2026                                self.added_declarations.push(Box::new(Declaration {
2027                                    span: n.span,
2028                                    name: n.name.clone(),
2029                                    value: vec![to_ident!("inline-grid")],
2030                                    important: n.important.clone(),
2031                                }));
2032                            }
2033                            ("inline", "ruby") | ("ruby", "inline") => {
2034                                self.added_declarations.push(Box::new(Declaration {
2035                                    span: n.span,
2036                                    name: n.name.clone(),
2037                                    value: vec![to_ident!("ruby")],
2038                                    important: n.important.clone(),
2039                                }));
2040                            }
2041                            ("block", "table") | ("table", "block") => {
2042                                self.added_declarations.push(Box::new(Declaration {
2043                                    span: n.span,
2044                                    name: n.name.clone(),
2045                                    value: vec![to_ident!("table")],
2046                                    important: n.important.clone(),
2047                                }));
2048                            }
2049                            ("inline", "table") | ("table", "inline") => {
2050                                self.added_declarations.push(Box::new(Declaration {
2051                                    span: n.span,
2052                                    name: n.name.clone(),
2053                                    value: vec![to_ident!("inline-table")],
2054                                    important: n.important.clone(),
2055                                }));
2056                            }
2057                            ("table-cell", "flow") | ("flow", "table-cell") => {
2058                                self.added_declarations.push(Box::new(Declaration {
2059                                    span: n.span,
2060                                    name: n.name.clone(),
2061                                    value: vec![to_ident!("table-cell")],
2062                                    important: n.important.clone(),
2063                                }));
2064                            }
2065                            ("table-caption", "flow") | ("flow", "table-caption") => {
2066                                self.added_declarations.push(Box::new(Declaration {
2067                                    span: n.span,
2068                                    name: n.name.clone(),
2069                                    value: vec![to_ident!("table-caption")],
2070                                    important: n.important.clone(),
2071                                }));
2072                            }
2073                            ("ruby-base", "flow") | ("flow", "ruby-base") => {
2074                                self.added_declarations.push(Box::new(Declaration {
2075                                    span: n.span,
2076                                    name: n.name.clone(),
2077                                    value: vec![to_ident!("ruby-base")],
2078                                    important: n.important.clone(),
2079                                }));
2080                            }
2081                            ("ruby-text", "flow") | ("flow", "ruby-text") => {
2082                                self.added_declarations.push(Box::new(Declaration {
2083                                    span: n.span,
2084                                    name: n.name.clone(),
2085                                    value: vec![to_ident!("ruby-text")],
2086                                    important: n.important.clone(),
2087                                }));
2088                            }
2089                            _ => {}
2090                        }
2091                    }
2092                } else if n.value.len() == 3
2093                    && should_prefix("display:multi-keyword-values", &self.env, false)
2094                {
2095                    if let (
2096                        Some(ComponentValue::Ident(first)),
2097                        Some(ComponentValue::Ident(second)),
2098                        Some(ComponentValue::Ident(third)),
2099                    ) = (n.value.first(), n.value.get(1), n.value.get(2))
2100                    {
2101                        match (&*first.value, &*second.value, &*third.value) {
2102                            ("list-item", "block", "flow")
2103                            | ("list-item", "flow", "block")
2104                            | ("block", "list-item", "flow")
2105                            | ("block", "flow", "list-item")
2106                            | ("flow", "block", "list-item")
2107                            | ("flow", "list-item", "block") => {
2108                                self.added_declarations.push(Box::new(Declaration {
2109                                    span: n.span,
2110                                    name: n.name.clone(),
2111                                    value: vec![to_ident!("list-item")],
2112                                    important: n.important.clone(),
2113                                }));
2114                            }
2115                            ("inline", "flow", "list-item")
2116                            | ("inline", "list-item", "flow")
2117                            | ("flow", "inline", "list-item")
2118                            | ("flow", "list-item", "inline")
2119                            | ("list-item", "flow", "inline")
2120                            | ("list-item", "inline", "flow") => {
2121                                self.added_declarations.push(Box::new(Declaration {
2122                                    span: n.span,
2123                                    name: n.name.clone(),
2124                                    value: vec![to_ident!("inline"), to_ident!("list-item")],
2125                                    important: n.important.clone(),
2126                                }));
2127                            }
2128                            _ => {}
2129                        }
2130                    };
2131                }
2132            }
2133
2134            "flex" => {
2135                let spec_2009_value = match n.value.first() {
2136                    Some(ComponentValue::Ident(ident))
2137                        if ident.value.eq_ignore_ascii_case("none") =>
2138                    {
2139                        Some(ComponentValue::Integer(Box::new(Integer {
2140                            span: ident.span,
2141                            value: 0,
2142                            raw: None,
2143                        })))
2144                    }
2145                    Some(ComponentValue::Ident(ident))
2146                        if ident.value.eq_ignore_ascii_case("auto") =>
2147                    {
2148                        Some(ComponentValue::Integer(Box::new(Integer {
2149                            span: ident.span,
2150                            value: 1,
2151                            raw: None,
2152                        })))
2153                    }
2154                    Some(any) => Some(any.clone()),
2155                    None => None,
2156                };
2157
2158                if let Some(spec_2009_value) = &spec_2009_value {
2159                    add_declaration!(
2160                        Prefix::Webkit,
2161                        "-webkit-box-flex",
2162                        Box::new(|| { vec![spec_2009_value.clone()] })
2163                    );
2164                } else {
2165                    add_declaration!(Prefix::Webkit, "-webkit-box-flex", None);
2166                }
2167
2168                add_declaration!(Prefix::Webkit, "-webkit-flex", None);
2169
2170                if let Some(spec_2009_value) = &spec_2009_value {
2171                    add_declaration!(
2172                        Prefix::Moz,
2173                        "-moz-box-flex",
2174                        Box::new(|| { vec![spec_2009_value.clone()] })
2175                    );
2176                } else {
2177                    add_declaration!(Prefix::Webkit, "-moz-box-flex", None);
2178                }
2179
2180                if n.value.len() == 3 {
2181                    add_declaration!(
2182                        Prefix::Ms,
2183                        "-ms-flex",
2184                        Box::new(|| {
2185                            let mut value = ms_value.clone();
2186
2187                            if let Some(span) = value
2188                                .get(2)
2189                                .and_then(|component_value| component_value.as_integer())
2190                                .filter(|integer| integer.value == 0)
2191                                .map(|integer| integer.span)
2192                            {
2193                                value[2] = ComponentValue::Dimension(Box::new(Dimension::Length(
2194                                    Length {
2195                                        span,
2196                                        value: Number {
2197                                            span: DUMMY_SP,
2198                                            value: 0.0,
2199                                            raw: None,
2200                                        },
2201                                        unit: Ident {
2202                                            span: DUMMY_SP,
2203                                            value: atom!("px"),
2204                                            raw: None,
2205                                        },
2206                                    },
2207                                )));
2208                            }
2209
2210                            value
2211                        })
2212                    );
2213                } else {
2214                    add_declaration!(Prefix::Ms, "-ms-flex", None);
2215                }
2216            }
2217
2218            "flex-grow" => {
2219                add_declaration!(Prefix::Webkit, "-webkit-box-flex", None);
2220                add_declaration!(Prefix::Webkit, "-webkit-flex-grow", None);
2221                add_declaration!(Prefix::Moz, "-moz-box-flex", None);
2222                add_declaration!(Prefix::Ms, "-ms-flex-positive", None);
2223            }
2224
2225            "flex-shrink" => {
2226                add_declaration!(Prefix::Webkit, "-webkit-flex-shrink", None);
2227                add_declaration!(Prefix::Ms, "-ms-flex-negative", None);
2228            }
2229
2230            "flex-basis" => {
2231                add_declaration!(Prefix::Webkit, "-webkit-flex-basis", None);
2232                add_declaration!(Prefix::Ms, "-ms-flex-preferred-size", None);
2233            }
2234
2235            "flex-direction" => {
2236                let old_values = match n.value.first() {
2237                    Some(ComponentValue::Ident(ident))
2238                        if ident.value.eq_ignore_ascii_case("row") =>
2239                    {
2240                        Some(("horizontal", "normal"))
2241                    }
2242                    Some(ComponentValue::Ident(ident))
2243                        if ident.value.eq_ignore_ascii_case("column") =>
2244                    {
2245                        Some(("vertical", "normal"))
2246                    }
2247                    Some(ComponentValue::Ident(ident))
2248                        if ident.value.eq_ignore_ascii_case("row-reverse") =>
2249                    {
2250                        Some(("horizontal", "reverse"))
2251                    }
2252                    Some(ComponentValue::Ident(ident))
2253                        if ident.value.eq_ignore_ascii_case("column-reverse") =>
2254                    {
2255                        Some(("vertical", "reverse"))
2256                    }
2257                    Some(_) | None => None,
2258                };
2259
2260                if let Some((orient, direction)) = old_values {
2261                    add_declaration!(
2262                        Prefix::Webkit,
2263                        "-webkit-box-orient",
2264                        Box::new(|| { vec![to_ident!(orient)] })
2265                    );
2266                    add_declaration!(
2267                        Prefix::Webkit,
2268                        "-webkit-box-direction",
2269                        Box::new(|| { vec![to_ident!(direction)] })
2270                    );
2271                }
2272
2273                add_declaration!(Prefix::Webkit, "-webkit-flex-direction", None);
2274
2275                if let Some((orient, direction)) = old_values {
2276                    add_declaration!(
2277                        Prefix::Moz,
2278                        "-moz-box-orient",
2279                        Box::new(|| { vec![to_ident!(orient)] })
2280                    );
2281                    add_declaration!(
2282                        Prefix::Webkit,
2283                        "-moz-box-direction",
2284                        Box::new(|| { vec![to_ident!(direction)] })
2285                    );
2286                }
2287
2288                add_declaration!(Prefix::Ms, "-ms-flex-direction", None);
2289            }
2290
2291            "flex-wrap" => {
2292                add_declaration!(Prefix::Webkit, "-webkit-flex-wrap", None);
2293                add_declaration!(Prefix::Ms, "-ms-flex-wrap", None);
2294            }
2295
2296            "flex-flow" => {
2297                let is_single_flex_wrap = matches!(n.value.first(), Some(ComponentValue::Ident(ident)) if n.value.len() == 1
2298                && matches!(
2299                   &* ident.value.to_ascii_lowercase(),
2300                    "wrap" | "nowrap" | "wrap-reverse"
2301                ));
2302
2303                let old_values = match is_single_flex_wrap {
2304                    true => None,
2305                    _ => {
2306                        let get_old_values = |index: usize| match n.value.get(index) {
2307                            Some(ComponentValue::Ident(ident))
2308                                if ident.value.eq_ignore_ascii_case("row") =>
2309                            {
2310                                Some(("horizontal", "normal"))
2311                            }
2312                            Some(ComponentValue::Ident(ident))
2313                                if ident.value.eq_ignore_ascii_case("column") =>
2314                            {
2315                                Some(("vertical", "normal"))
2316                            }
2317                            Some(ComponentValue::Ident(ident))
2318                                if ident.value.eq_ignore_ascii_case("row-reverse") =>
2319                            {
2320                                Some(("horizontal", "reverse"))
2321                            }
2322                            Some(ComponentValue::Ident(ident))
2323                                if ident.value.eq_ignore_ascii_case("column-reverse") =>
2324                            {
2325                                Some(("vertical", "reverse"))
2326                            }
2327                            Some(_) | None => None,
2328                        };
2329
2330                        get_old_values(0).or_else(|| get_old_values(1))
2331                    }
2332                };
2333
2334                if let Some((orient, direction)) = old_values {
2335                    add_declaration!(
2336                        Prefix::Webkit,
2337                        "-webkit-box-orient",
2338                        Box::new(|| { vec![to_ident!(orient)] })
2339                    );
2340                    add_declaration!(
2341                        Prefix::Webkit,
2342                        "-webkit-box-direction",
2343                        Box::new(|| { vec![to_ident!(direction)] })
2344                    );
2345                }
2346
2347                add_declaration!(Prefix::Webkit, "-webkit-flex-flow", None);
2348
2349                if let Some((orient, direction)) = old_values {
2350                    add_declaration!(
2351                        Prefix::Moz,
2352                        "-moz-box-orient",
2353                        Box::new(|| { vec![to_ident!(orient)] })
2354                    );
2355                    add_declaration!(
2356                        Prefix::Moz,
2357                        "-moz-box-direction",
2358                        Box::new(|| { vec![to_ident!(direction)] })
2359                    );
2360                }
2361
2362                add_declaration!(Prefix::Ms, "-ms-flex-flow", None);
2363            }
2364
2365            "justify-content" => {
2366                let need_old_spec = !matches!(
2367                    n.value.first(),
2368                    Some(ComponentValue::Ident(ident)) if ident.value.eq_ignore_ascii_case("space-around")
2369                );
2370
2371                if need_old_spec {
2372                    add_declaration!(
2373                        Prefix::Webkit,
2374                        "-webkit-box-pack",
2375                        Box::new(|| {
2376                            let mut old_spec_webkit_new_value = webkit_value.clone();
2377
2378                            replace_ident(&mut old_spec_webkit_new_value, "flex-start", "start");
2379                            replace_ident(&mut old_spec_webkit_new_value, "flex-end", "end");
2380                            replace_ident(
2381                                &mut old_spec_webkit_new_value,
2382                                "space-between",
2383                                "justify",
2384                            );
2385
2386                            old_spec_webkit_new_value
2387                        })
2388                    );
2389                }
2390
2391                add_declaration!(Prefix::Webkit, "-webkit-justify-content", None);
2392
2393                if need_old_spec {
2394                    add_declaration!(
2395                        Prefix::Moz,
2396                        "-moz-box-pack",
2397                        Box::new(|| {
2398                            let mut old_spec_moz_value = moz_value.clone();
2399
2400                            replace_ident(&mut old_spec_moz_value, "flex-start", "start");
2401                            replace_ident(&mut old_spec_moz_value, "flex-end", "end");
2402                            replace_ident(&mut old_spec_moz_value, "space-between", "justify");
2403
2404                            old_spec_moz_value
2405                        })
2406                    );
2407                }
2408
2409                add_declaration!(
2410                    Prefix::Ms,
2411                    "-ms-flex-pack",
2412                    Box::new(|| {
2413                        let mut old_spec_ms_value = ms_value.clone();
2414
2415                        replace_ident(&mut old_spec_ms_value, "flex-start", "start");
2416                        replace_ident(&mut old_spec_ms_value, "flex-end", "end");
2417                        replace_ident(&mut old_spec_ms_value, "space-between", "justify");
2418                        replace_ident(&mut old_spec_ms_value, "space-around", "distribute");
2419
2420                        old_spec_ms_value
2421                    })
2422                );
2423            }
2424
2425            "opacity" if should_prefix("opacity", &self.env, true) => {
2426                let old_value = match n.value.first() {
2427                    Some(ComponentValue::Percentage(percentage)) => Some(percentage.value.value),
2428                    _ => None,
2429                };
2430
2431                if let Some(old_value) = old_value {
2432                    let rounded_alpha = (old_value * 1000.0).round() / 100000.0;
2433
2434                    self.added_declarations.push(Box::new(Declaration {
2435                        span: n.span,
2436                        name: n.name.clone(),
2437                        value: vec![to_number!(rounded_alpha)],
2438                        important: n.important.clone(),
2439                    }));
2440                }
2441            }
2442
2443            "order" => {
2444                let old_spec_num = match n.value.first() {
2445                    Some(ComponentValue::Integer(integer)) => Some(integer.value + 1),
2446                    _ => None,
2447                };
2448
2449                match old_spec_num {
2450                    Some(old_spec_num) if n.value.len() == 1 => {
2451                        add_declaration!(
2452                            Prefix::Webkit,
2453                            "-webkit-box-ordinal-group",
2454                            Box::new(|| { vec![to_integer!(old_spec_num)] })
2455                        );
2456                    }
2457                    _ => {
2458                        add_declaration!(Prefix::Webkit, "-webkit-box-ordinal-group", None);
2459                    }
2460                }
2461
2462                add_declaration!(Prefix::Webkit, "-webkit-order", None);
2463
2464                match old_spec_num {
2465                    Some(old_spec_num) if n.value.len() == 1 => {
2466                        add_declaration!(
2467                            Prefix::Moz,
2468                            "-moz-box-ordinal-group",
2469                            Box::new(|| { vec![to_integer!(old_spec_num)] })
2470                        );
2471                    }
2472                    _ => {
2473                        add_declaration!(Prefix::Webkit, "-moz-box-ordinal-group", None);
2474                    }
2475                }
2476
2477                add_declaration!(Prefix::Ms, "-ms-flex-order", None);
2478            }
2479
2480            "align-items" => {
2481                add_declaration!(
2482                    Prefix::Webkit,
2483                    "-webkit-box-align",
2484                    Box::new(|| {
2485                        let mut old_spec_webkit_new_value = webkit_value.clone();
2486
2487                        replace_ident(&mut old_spec_webkit_new_value, "flex-end", "end");
2488                        replace_ident(&mut old_spec_webkit_new_value, "flex-start", "start");
2489
2490                        old_spec_webkit_new_value
2491                    })
2492                );
2493                add_declaration!(Prefix::Webkit, "-webkit-align-items", None);
2494                add_declaration!(
2495                    Prefix::Moz,
2496                    "-moz-box-align",
2497                    Box::new(|| {
2498                        let mut old_spec_moz_value = moz_value.clone();
2499
2500                        replace_ident(&mut old_spec_moz_value, "flex-end", "end");
2501                        replace_ident(&mut old_spec_moz_value, "flex-start", "start");
2502
2503                        old_spec_moz_value
2504                    })
2505                );
2506                add_declaration!(
2507                    Prefix::Ms,
2508                    "-ms-flex-align",
2509                    Box::new(|| {
2510                        let mut old_spec_ms_value = ms_value.clone();
2511
2512                        replace_ident(&mut old_spec_ms_value, "flex-end", "end");
2513                        replace_ident(&mut old_spec_ms_value, "flex-start", "start");
2514
2515                        old_spec_ms_value
2516                    })
2517                );
2518            }
2519
2520            "align-self" => {
2521                add_declaration!(Prefix::Webkit, "-webkit-align-self", None);
2522                add_declaration!(
2523                    Prefix::Ms,
2524                    "-ms-flex-item-align",
2525                    Box::new(|| {
2526                        let mut spec_2012_ms_value = ms_value.clone();
2527
2528                        replace_ident(&mut spec_2012_ms_value, "flex-end", "end");
2529                        replace_ident(&mut spec_2012_ms_value, "flex-start", "start");
2530
2531                        spec_2012_ms_value
2532                    })
2533                );
2534            }
2535
2536            "align-content" => {
2537                add_declaration!(Prefix::Webkit, "-webkit-align-content", None);
2538                add_declaration!(
2539                    Prefix::Ms,
2540                    "-ms-flex-line-pack",
2541                    Box::new(|| {
2542                        let mut spec_2012_ms_value = ms_value.clone();
2543
2544                        replace_ident(&mut spec_2012_ms_value, "flex-end", "end");
2545                        replace_ident(&mut spec_2012_ms_value, "flex-start", "start");
2546                        replace_ident(&mut spec_2012_ms_value, "space-between", "justify");
2547                        replace_ident(&mut spec_2012_ms_value, "space-around", "distribute");
2548
2549                        spec_2012_ms_value
2550                    })
2551                );
2552            }
2553
2554            "image-rendering" => {
2555                if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
2556                    if should_prefix("-webkit-optimize-contrast:fallback", &self.env, false) {
2557                        // Fallback to nearest-neighbor algorithm
2558                        replace_ident(&mut webkit_value, "pixelated", "-webkit-optimize-contrast");
2559                    }
2560
2561                    if should_prefix("-webkit-optimize-contrast", &self.env, false) {
2562                        replace_ident(
2563                            &mut webkit_value,
2564                            "crisp-edges",
2565                            "-webkit-optimize-contrast",
2566                        );
2567                    }
2568                }
2569
2570                if should_prefix("-moz-crisp-edges", &self.env, false)
2571                    && (self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none())
2572                {
2573                    // Fallback to nearest-neighbor algorithm
2574                    replace_ident(&mut moz_value, "pixelated", "-moz-crisp-edges");
2575                    replace_ident(&mut moz_value, "crisp-edges", "-moz-crisp-edges");
2576                }
2577
2578                if should_prefix("-o-pixelated", &self.env, false)
2579                    && (self.rule_prefix == Some(Prefix::O) || self.rule_prefix.is_none())
2580                {
2581                    replace_ident(&mut o_value, "pixelated", "-o-pixelated");
2582                }
2583
2584                if should_prefix("nearest-neighbor", &self.env, false)
2585                    && (self.rule_prefix == Some(Prefix::Ms) || self.rule_prefix.is_none())
2586                {
2587                    let mut old_spec_ms_value = ms_value.clone();
2588
2589                    replace_ident(&mut old_spec_ms_value, "pixelated", "nearest-neighbor");
2590
2591                    if !ms_value.eq_ignore_span(&old_spec_ms_value) {
2592                        add_declaration!(
2593                            Prefix::Ms,
2594                            "-ms-interpolation-mode",
2595                            Box::new(|| { old_spec_ms_value.clone() })
2596                        );
2597                    }
2598                }
2599            }
2600
2601            "filter" => match &n.value.first() {
2602                Some(ComponentValue::PreservedToken(_)) => {}
2603                Some(ComponentValue::Function(function)) if function.name == "alpha" => {}
2604                None => {}
2605                _ => {
2606                    add_declaration!(Prefix::Webkit, "-webkit-filter", None);
2607                }
2608            },
2609
2610            "backdrop-filter" => {
2611                add_declaration!(Prefix::Webkit, "-webkit-backdrop-filter", None);
2612            }
2613
2614            "mask-clip" => {
2615                add_declaration!(Prefix::Webkit, "-webkit-mask-clip", None);
2616            }
2617
2618            // Fix me https://github.com/postcss/autoprefixer/blob/main/lib/hacks/mask-composite.js
2619            "mask-composite" => {
2620                add_declaration!(Prefix::Webkit, "-webkit-mask-composite", None);
2621            }
2622
2623            "mask-image" => {
2624                add_declaration!(Prefix::Webkit, "-webkit-mask-image", None);
2625            }
2626
2627            "mask-origin" => {
2628                add_declaration!(Prefix::Webkit, "-webkit-mask-origin", None);
2629            }
2630
2631            "mask-repeat" => {
2632                add_declaration!(Prefix::Webkit, "-webkit-mask-repeat", None);
2633            }
2634
2635            "mask-border-repeat" => {
2636                add_declaration!(Prefix::Webkit, "-webkit-mask-border-repeat", None);
2637            }
2638
2639            "mask-border-source" => {
2640                add_declaration!(Prefix::Webkit, "-webkit-mask-border-source", None);
2641            }
2642
2643            "mask" => {
2644                add_declaration!(Prefix::Webkit, "-webkit-mask", None);
2645            }
2646
2647            "mask-position" => {
2648                add_declaration!(Prefix::Webkit, "-webkit-mask-position", None);
2649            }
2650
2651            "mask-size" => {
2652                add_declaration!(Prefix::Webkit, "-webkit-mask-size", None);
2653            }
2654
2655            "mask-border" => {
2656                add_declaration!(Prefix::Webkit, "-webkit-mask-box-image", None);
2657            }
2658
2659            "mask-border-outset" => {
2660                add_declaration!(Prefix::Webkit, "-webkit-mask-box-image-outset", None);
2661            }
2662
2663            "mask-border-width" => {
2664                add_declaration!(Prefix::Webkit, "-webkit-mask-box-image-width", None);
2665            }
2666
2667            "mask-border-slice" => {
2668                add_declaration!(Prefix::Webkit, "-webkit-mask-box-image-slice", None);
2669            }
2670
2671            "border-inline-start" => {
2672                add_declaration!(Prefix::Webkit, "-webkit-border-start", None);
2673                add_declaration!(Prefix::Moz, "-moz-border-start", None);
2674            }
2675
2676            "border-inline-end" => {
2677                add_declaration!(Prefix::Webkit, "-webkit-border-end", None);
2678                add_declaration!(Prefix::Moz, "-moz-border-end", None);
2679            }
2680
2681            "margin-inline-start" => {
2682                add_declaration!(Prefix::Webkit, "-webkit-margin-start", None);
2683                add_declaration!(Prefix::Moz, "-moz-margin-start", None);
2684            }
2685
2686            "margin-inline-end" => {
2687                add_declaration!(Prefix::Webkit, "-webkit-margin-end", None);
2688                add_declaration!(Prefix::Moz, "-moz-margin-end", None);
2689            }
2690
2691            "padding-inline-start" => {
2692                add_declaration!(Prefix::Webkit, "-webkit-padding-start", None);
2693                add_declaration!(Prefix::Moz, "-moz-padding-start", None);
2694            }
2695
2696            "padding-inline-end" => {
2697                add_declaration!(Prefix::Webkit, "-webkit-padding-end", None);
2698                add_declaration!(Prefix::Moz, "-moz-padding-end", None);
2699            }
2700
2701            "border-block-start" => {
2702                add_declaration!(Prefix::Webkit, "-webkit-border-before", None);
2703            }
2704
2705            "border-block-end" => {
2706                add_declaration!(Prefix::Webkit, "-webkit-border-after", None);
2707            }
2708
2709            "margin-block-start" => {
2710                add_declaration!(Prefix::Webkit, "-webkit-margin-before", None);
2711            }
2712
2713            "margin-block-end" => {
2714                add_declaration!(Prefix::Webkit, "-webkit-margin-after", None);
2715            }
2716
2717            "padding-block-start" => {
2718                add_declaration!(Prefix::Webkit, "-webkit-padding-before", None);
2719            }
2720
2721            "padding-block-end" => {
2722                add_declaration!(Prefix::Webkit, "-webkit-padding-after", None);
2723            }
2724
2725            "backface-visibility" => {
2726                add_declaration!(Prefix::Webkit, "-webkit-backface-visibility", None);
2727                add_declaration!(Prefix::Moz, "-moz-backface-visibility", None);
2728            }
2729
2730            "clip-path" => {
2731                add_declaration!(Prefix::Webkit, "-webkit-clip-path", None);
2732            }
2733
2734            "position" if n.value.len() == 1 => {
2735                if should_prefix("-webkit-sticky", &self.env, false)
2736                    && (self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none())
2737                {
2738                    replace_ident(&mut webkit_value, "sticky", "-webkit-sticky");
2739                }
2740            }
2741
2742            "user-select" => {
2743                add_declaration!(Prefix::Webkit, "-webkit-user-select", None);
2744                add_declaration!(Prefix::Moz, "-moz-user-select", None);
2745
2746                if let ComponentValue::Ident(ident) = &n.value[0] {
2747                    match &*ident.value.to_ascii_lowercase() {
2748                        "contain" => {
2749                            add_declaration!(
2750                                Prefix::Ms,
2751                                "-ms-user-select",
2752                                Box::new(|| { vec![to_ident!("element")] })
2753                            );
2754                        }
2755                        "all" => {}
2756                        _ => {
2757                            add_declaration!(Prefix::Ms, "-ms-user-select", None);
2758                        }
2759                    }
2760                }
2761            }
2762
2763            "transform" => {
2764                add_declaration!(Prefix::Webkit, "-webkit-transform", None);
2765                add_declaration!(Prefix::Moz, "-moz-transform", None);
2766
2767                let has_3d_function = n.value.iter().any(|n| match n {
2768                    ComponentValue::Function(function)
2769                        if matches_eq!(
2770                            function.name,
2771                            "matrix3d",
2772                            "translate3d",
2773                            "translatez",
2774                            "scale3d",
2775                            "scalez",
2776                            "rotate3d",
2777                            "rotatex",
2778                            "rotatey",
2779                            "rotatez",
2780                            "perspective"
2781                        ) =>
2782                    {
2783                        true
2784                    }
2785                    _ => false,
2786                });
2787
2788                if !has_3d_function {
2789                    if !self.in_keyframe_block {
2790                        add_declaration!(Prefix::Ms, "-ms-transform", None);
2791                    }
2792
2793                    add_declaration!(Prefix::O, "-o-transform", None);
2794                }
2795            }
2796
2797            "transform-origin" => {
2798                add_declaration!(Prefix::Webkit, "-webkit-transform-origin", None);
2799                add_declaration!(Prefix::Moz, "-moz-transform-origin", None);
2800
2801                if !self.in_keyframe_block {
2802                    add_declaration!(Prefix::Ms, "-ms-transform-origin", None);
2803                }
2804
2805                add_declaration!(Prefix::O, "-o-transform-origin", None);
2806            }
2807
2808            "transform-style" => {
2809                add_declaration!(Prefix::Webkit, "-webkit-transform-style", None);
2810                add_declaration!(Prefix::Moz, "-moz-transform-style", None);
2811            }
2812
2813            "perspective" => {
2814                add_declaration!(Prefix::Webkit, "-webkit-perspective", None);
2815                add_declaration!(Prefix::Moz, "-moz-perspective", None);
2816            }
2817
2818            "perspective-origin" => {
2819                add_declaration!(Prefix::Webkit, "-webkit-perspective-origin", None);
2820                add_declaration!(Prefix::Moz, "-moz-perspective-origin", None);
2821            }
2822
2823            "text-decoration" => {
2824                if n.value.len() == 1 {
2825                    match &n.value[0] {
2826                        ComponentValue::Ident(ident)
2827                            if matches!(
2828                                &*ident.value.to_ascii_lowercase(),
2829                                "none"
2830                                    | "underline"
2831                                    | "overline"
2832                                    | "line-through"
2833                                    | "blink"
2834                                    | "inherit"
2835                                    | "initial"
2836                                    | "revert"
2837                                    | "unset"
2838                            ) => {}
2839                        _ => {
2840                            add_declaration!(Prefix::Webkit, "-webkit-text-decoration", None);
2841                            add_declaration!(Prefix::Moz, "-moz-text-decoration", None);
2842                        }
2843                    }
2844                } else {
2845                    add_declaration!(Prefix::Webkit, "-webkit-text-decoration", None);
2846                    add_declaration!(Prefix::Moz, "-moz-text-decoration", None);
2847                }
2848            }
2849
2850            "text-decoration-style" => {
2851                add_declaration!(Prefix::Webkit, "-webkit-text-decoration-style", None);
2852                add_declaration!(Prefix::Moz, "-moz-text-decoration-style", None);
2853            }
2854
2855            "text-decoration-color" => {
2856                add_declaration!(Prefix::Webkit, "-webkit-text-decoration-color", None);
2857                add_declaration!(Prefix::Moz, "-moz-text-decoration-color", None);
2858            }
2859
2860            "text-decoration-line" => {
2861                add_declaration!(Prefix::Webkit, "-webkit-text-decoration-line", None);
2862                add_declaration!(Prefix::Moz, "-moz-text-decoration-line", None);
2863            }
2864
2865            "text-decoration-skip" => {
2866                add_declaration!(Prefix::Webkit, "-webkit-text-decoration-skip", None);
2867            }
2868
2869            "text-decoration-skip-ink" => {
2870                if let ComponentValue::Ident(ident) = &n.value[0] {
2871                    match &*ident.value.to_ascii_lowercase() {
2872                        "auto" => {
2873                            add_declaration!(
2874                                Prefix::Webkit,
2875                                "-webkit-text-decoration-skip",
2876                                Box::new(|| { vec![to_ident!("ink")] })
2877                            );
2878                        }
2879                        _ => {
2880                            add_declaration!(
2881                                Prefix::Webkit,
2882                                "-webkit-text-decoration-skip-ink",
2883                                None
2884                            );
2885                        }
2886                    }
2887                }
2888            }
2889
2890            "text-size-adjust" if n.value.len() == 1 => {
2891                if let ComponentValue::Ident(ident) = &n.value[0] {
2892                    if ident.value.eq_ignore_ascii_case("none") {
2893                        add_declaration!(Prefix::Webkit, "-webkit-text-size-adjust", None);
2894                        add_declaration!(Prefix::Moz, "-moz-text-size-adjust", None);
2895                        add_declaration!(Prefix::Ms, "-ms-text-size-adjust", None);
2896                    }
2897                }
2898            }
2899
2900            // TODO improve me for `filter` values https://github.com/postcss/autoprefixer/blob/main/test/cases/transition.css#L6
2901            // TODO https://github.com/postcss/autoprefixer/blob/main/lib/transition.js
2902            "transition" => {
2903                if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
2904                    if should_prefix("-webkit-transform", &self.env, false) {
2905                        replace_ident(&mut webkit_value, "transform", "-webkit-transform");
2906                    }
2907
2908                    if should_prefix("-webkit-filter", &self.env, false) {
2909                        replace_ident(&mut webkit_value, "filter", "-webkit-filter");
2910                    }
2911                }
2912
2913                add_declaration!(Prefix::Webkit, "-webkit-transition", None);
2914
2915                if should_prefix("-moz-transform", &self.env, false)
2916                    && (self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none())
2917                {
2918                    replace_ident(&mut moz_value, "transform", "-moz-transform");
2919                }
2920
2921                add_declaration!(Prefix::Moz, "-moz-transition", None);
2922
2923                if should_prefix("-o-transform", &self.env, false)
2924                    && (self.rule_prefix == Some(Prefix::O) || self.rule_prefix.is_none())
2925                {
2926                    replace_ident(&mut o_value, "transform", "-o-transform");
2927                }
2928
2929                add_declaration!(Prefix::O, "-o-transition", None);
2930            }
2931
2932            "transition-property" => {
2933                if should_prefix("-webkit-transform", &self.env, false)
2934                    && (self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none())
2935                {
2936                    replace_ident(&mut webkit_value, "transform", "-webkit-transform");
2937                }
2938
2939                if should_prefix("-webkit-filter", &self.env, false)
2940                    && (self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none())
2941                {
2942                    replace_ident(&mut webkit_value, "filter", "-webkit-filter");
2943                }
2944
2945                if should_prefix("-moz-transform", &self.env, false)
2946                    && (self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none())
2947                {
2948                    replace_ident(&mut moz_value, "transform", "-moz-transform");
2949                }
2950
2951                if should_prefix("-o-transform", &self.env, false)
2952                    && (self.rule_prefix == Some(Prefix::O) || self.rule_prefix.is_none())
2953                {
2954                    replace_ident(&mut o_value, "transform", "-o-transform");
2955                }
2956
2957                add_declaration!(Prefix::Webkit, "-webkit-transition-property", None);
2958                add_declaration!(Prefix::Moz, "-moz-transition-timing-function", None);
2959                add_declaration!(Prefix::O, "-o-transition-timing-function", None);
2960            }
2961
2962            "transition-duration" => {
2963                add_declaration!(Prefix::Webkit, "-webkit-transition-duration", None);
2964                add_declaration!(Prefix::Moz, "-moz-transition-duration", None);
2965                add_declaration!(Prefix::O, "-o-transition-duration", None);
2966            }
2967
2968            "transition-delay" => {
2969                add_declaration!(Prefix::Webkit, "-webkit-transition-delay", None);
2970                add_declaration!(Prefix::Moz, "-moz-transition-delay", None);
2971                add_declaration!(Prefix::O, "-o-transition-delay", None);
2972            }
2973
2974            "transition-timing-function" => {
2975                add_declaration!(Prefix::Webkit, "-webkit-transition-timing-function", None);
2976                add_declaration!(Prefix::Moz, "-moz-transition-timing-function", None);
2977                add_declaration!(Prefix::O, "-o-transition-timing-function", None);
2978            }
2979
2980            "writing-mode" if n.value.len() == 1 => {
2981                if let Some(simple_block) = &self.simple_block {
2982                    let direction = match simple_block.value.iter().rev().find(|declaration| {
2983                        declaration
2984                            .as_declaration()
2985                            .and_then(|declaration| declaration.name.as_ident())
2986                            .filter(|ident| ident.value.eq_ignore_ascii_case("direction"))
2987                            .is_some()
2988                    }) {
2989                        Some(ComponentValue::Declaration(declaration)) => {
2990                            match declaration.value.first() {
2991                                Some(ComponentValue::Ident(ident))
2992                                    if ident.value.eq_ignore_ascii_case("rtl") =>
2993                                {
2994                                    Some("rtl")
2995                                }
2996                                _ => Some("ltr"),
2997                            }
2998                        }
2999                        _ => Some("ltr"),
3000                    };
3001
3002                    if let ComponentValue::Ident(ident) = &n.value[0] {
3003                        match &*ident.value.to_ascii_lowercase() {
3004                            "vertical-lr" => {
3005                                add_declaration!(Prefix::Webkit, "-webkit-writing-mode", None);
3006
3007                                match direction {
3008                                    Some("ltr") => {
3009                                        add_declaration!(
3010                                            Prefix::Ms,
3011                                            "-ms-writing-mode",
3012                                            Box::new(|| { vec![to_ident!("tb-lr")] })
3013                                        );
3014                                    }
3015                                    Some("rtl") => {
3016                                        add_declaration!(
3017                                            Prefix::Ms,
3018                                            "-ms-writing-mode",
3019                                            Box::new(|| { vec![to_ident!("bt-lr")] })
3020                                        );
3021                                    }
3022                                    _ => {}
3023                                }
3024                            }
3025
3026                            "vertical-rl" => {
3027                                add_declaration!(Prefix::Webkit, "-webkit-writing-mode", None);
3028
3029                                match direction {
3030                                    Some("ltr") => {
3031                                        add_declaration!(
3032                                            Prefix::Ms,
3033                                            "-ms-writing-mode",
3034                                            Box::new(|| { vec![to_ident!("tb-rl")] })
3035                                        );
3036                                    }
3037                                    Some("rtl") => {
3038                                        add_declaration!(
3039                                            Prefix::Ms,
3040                                            "-ms-writing-mode",
3041                                            Box::new(|| { vec![to_ident!("bt-rl")] })
3042                                        );
3043                                    }
3044                                    _ => {}
3045                                }
3046                            }
3047
3048                            "horizontal-tb" => {
3049                                add_declaration!(Prefix::Webkit, "-webkit-writing-mode", None);
3050
3051                                match direction {
3052                                    Some("ltr") => {
3053                                        add_declaration!(
3054                                            Prefix::Ms,
3055                                            "-ms-writing-mode",
3056                                            Box::new(|| { vec![to_ident!("lr-tb")] })
3057                                        );
3058                                    }
3059                                    Some("rtl") => {
3060                                        add_declaration!(
3061                                            Prefix::Ms,
3062                                            "-ms-writing-mode",
3063                                            Box::new(|| { vec![to_ident!("rl-tb")] })
3064                                        );
3065                                    }
3066                                    _ => {}
3067                                }
3068                            }
3069
3070                            "sideways-rl" | "sideways-lr" => {
3071                                add_declaration!(Prefix::Webkit, "-webkit-writing-mode", None);
3072                            }
3073
3074                            _ => {
3075                                add_declaration!(Prefix::Webkit, "-webkit-writing-mode", None);
3076                                add_declaration!(Prefix::Ms, "-ms-writing-mode", None);
3077                            }
3078                        }
3079                    }
3080                }
3081            }
3082
3083            "width"
3084            | "min-width"
3085            | "max-width"
3086            | "height"
3087            | "min-height"
3088            | "max-height"
3089            | "inline-size"
3090            | "min-inline-size"
3091            | "max-inline-size"
3092            | "block-size"
3093            | "min-block-size"
3094            | "max-block-size"
3095            | "grid"
3096            | "grid-template"
3097            | "grid-template-rows"
3098            | "grid-template-columns"
3099            | "grid-auto-columns"
3100            | "grid-auto-rows" => {
3101                let is_grid_property = matches!(
3102                    &**name,
3103                    "grid"
3104                        | "grid-template"
3105                        | "grid-template-rows"
3106                        | "grid-template-columns"
3107                        | "grid-auto-columns"
3108                        | "grid-auto-rows"
3109                );
3110
3111                if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
3112                    if should_prefix("-webkit-fit-content", &self.env, false) {
3113                        replace_ident(&mut webkit_value, "fit-content", "-webkit-fit-content");
3114                    }
3115
3116                    if should_prefix("-webkit-max-content", &self.env, false) {
3117                        replace_ident(&mut webkit_value, "max-content", "-webkit-max-content");
3118                    }
3119
3120                    if should_prefix("-webkit-min-content", &self.env, false) {
3121                        replace_ident(&mut webkit_value, "min-content", "-webkit-min-content");
3122                    }
3123
3124                    if should_prefix("-webkit-fill-available", &self.env, false) {
3125                        replace_ident(
3126                            &mut webkit_value,
3127                            "fill-available",
3128                            "-webkit-fill-available",
3129                        );
3130                        replace_ident(&mut webkit_value, "fill", "-webkit-fill-available");
3131                        replace_ident(&mut webkit_value, "stretch", "-webkit-fill-available");
3132                    }
3133                }
3134
3135                if !is_grid_property
3136                    && (self.rule_prefix == Some(Prefix::Moz) || self.rule_prefix.is_none())
3137                {
3138                    if should_prefix("-moz-fit-content", &self.env, false) {
3139                        replace_ident(&mut moz_value, "fit-content", "-moz-fit-content");
3140                    }
3141
3142                    if should_prefix("-moz-max-content", &self.env, false) {
3143                        replace_ident(&mut moz_value, "max-content", "-moz-max-content");
3144                    }
3145
3146                    if should_prefix("-moz-min-content", &self.env, false) {
3147                        replace_ident(&mut moz_value, "min-content", "-moz-min-content");
3148                    }
3149
3150                    if should_prefix("-moz-available", &self.env, false) {
3151                        replace_ident(&mut moz_value, "fill-available", "-moz-available");
3152                        replace_ident(&mut moz_value, "fill", "-moz-available");
3153                        replace_ident(&mut moz_value, "stretch", "-moz-available");
3154                    }
3155                }
3156            }
3157
3158            "touch-action" => {
3159                let env = self.env.clone();
3160                add_declaration!(
3161                    Prefix::Ms,
3162                    "-ms-touch-action",
3163                    Box::new(|| {
3164                        let mut new_ms_value = ms_value.clone();
3165
3166                        if should_prefix("-ms-pan-x", &env, false) {
3167                            replace_ident(&mut new_ms_value, "pan-x", "-ms-pan-x");
3168                        }
3169
3170                        if should_prefix("-ms-pan-y", &env, false) {
3171                            replace_ident(&mut new_ms_value, "pan-y", "-ms-pan-y");
3172                        }
3173
3174                        if should_prefix("-ms-double-tap-zoom", &env, false) {
3175                            replace_ident(
3176                                &mut new_ms_value,
3177                                "double-tap-zoom",
3178                                "-ms-double-tap-zoom",
3179                            );
3180                        }
3181
3182                        if should_prefix("-ms-manipulation", &env, false) {
3183                            replace_ident(&mut new_ms_value, "manipulation", "-ms-manipulation");
3184                        }
3185
3186                        if should_prefix("-ms-none", &env, false) {
3187                            replace_ident(&mut new_ms_value, "none", "-ms-none");
3188                        }
3189
3190                        if should_prefix("-ms-pinch-zoom", &env, false) {
3191                            replace_ident(&mut new_ms_value, "pinch-zoom", "-ms-pinch-zoom");
3192                        }
3193
3194                        new_ms_value
3195                    })
3196                );
3197
3198                add_declaration!(Prefix::Ms, "-ms-touch-action", None);
3199            }
3200
3201            "text-orientation" => {
3202                add_declaration!(Prefix::Webkit, "-webkit-text-orientation", None);
3203            }
3204
3205            "unicode-bidi" => {
3206                if self.rule_prefix == Some(Prefix::Webkit) || self.rule_prefix.is_none() {
3207                    if should_prefix("-moz-isolate", &self.env, false) {
3208                        replace_ident(&mut moz_value, "isolate", "-moz-isolate");
3209                    }
3210
3211                    if should_prefix("-moz-isolate-override", &self.env, false) {
3212                        replace_ident(&mut moz_value, "isolate-override", "-moz-isolate-override");
3213                    }
3214
3215                    if should_prefix("-moz-plaintext", &self.env, false) {
3216                        replace_ident(&mut moz_value, "plaintext", "-moz-plaintext");
3217                    }
3218
3219                    if should_prefix("-webkit-isolate", &self.env, false) {
3220                        replace_ident(&mut webkit_value, "isolate", "-webkit-isolate");
3221                    }
3222
3223                    if should_prefix("-webpack-isolate-override", &self.env, false) {
3224                        replace_ident(
3225                            &mut webkit_value,
3226                            "isolate-override",
3227                            "-webpack-isolate-override",
3228                        );
3229                    }
3230
3231                    if should_prefix("-webpack-plaintext", &self.env, false) {
3232                        replace_ident(&mut webkit_value, "plaintext", "-webpack-plaintext");
3233                    }
3234                }
3235            }
3236
3237            "text-spacing" => {
3238                add_declaration!(Prefix::Ms, "-ms-text-spacing", None);
3239            }
3240
3241            "text-emphasis" => {
3242                add_declaration!(Prefix::Webkit, "-webkit-text-emphasis", None);
3243            }
3244
3245            "text-emphasis-position" => {
3246                add_declaration!(Prefix::Webkit, "-webkit-text-emphasis-position", None);
3247            }
3248
3249            "text-emphasis-style" => {
3250                add_declaration!(Prefix::Webkit, "-webkit-text-emphasis-style", None);
3251            }
3252
3253            "text-emphasis-color" => {
3254                add_declaration!(Prefix::Webkit, "-webkit-text-emphasis-color", None);
3255            }
3256
3257            "flow-into" => {
3258                add_declaration!(Prefix::Webkit, "-webkit-flow-into", None);
3259                add_declaration!(Prefix::Ms, "-ms-flow-into", None);
3260            }
3261
3262            "flow-from" => {
3263                add_declaration!(Prefix::Webkit, "-webkit-flow-from", None);
3264                add_declaration!(Prefix::Ms, "-ms-flow-from", None);
3265            }
3266
3267            "region-fragment" => {
3268                add_declaration!(Prefix::Webkit, "-webkit-region-fragment", None);
3269                add_declaration!(Prefix::Ms, "-ms-region-fragment", None);
3270            }
3271
3272            "scroll-snap-type" => {
3273                add_declaration!(Prefix::Webkit, "-webkit-scroll-snap-type", None);
3274                add_declaration!(Prefix::Ms, "-ms-scroll-snap-type", None);
3275            }
3276
3277            "scroll-snap-coordinate" => {
3278                add_declaration!(Prefix::Webkit, "-webkit-scroll-snap-coordinate", None);
3279                add_declaration!(Prefix::Ms, "-ms-scroll-snap-coordinate", None);
3280            }
3281
3282            "scroll-snap-destination" => {
3283                add_declaration!(Prefix::Webkit, "-webkit-scroll-snap-destination", None);
3284                add_declaration!(Prefix::Ms, "-ms-scroll-snap-destination", None);
3285            }
3286
3287            "scroll-snap-points-x" => {
3288                add_declaration!(Prefix::Webkit, "-webkit-scroll-snap-points-x", None);
3289                add_declaration!(Prefix::Ms, "-ms-scroll-snap-points-x", None);
3290            }
3291
3292            "scroll-snap-points-y" => {
3293                add_declaration!(Prefix::Webkit, "-webkit-scroll-snap-points-y", None);
3294                add_declaration!(Prefix::Ms, "-ms-scroll-snap-points-y", None);
3295            }
3296
3297            "text-align-last" => {
3298                add_declaration!(Prefix::Moz, "-moz-text-align-last", None);
3299            }
3300
3301            "text-overflow" => {
3302                add_declaration!(Prefix::O, "-o-text-overflow", None);
3303            }
3304
3305            "shape-margin" => {
3306                add_declaration!(Prefix::Webkit, "-webkit-shape-margin", None);
3307            }
3308
3309            "shape-outside" => {
3310                add_declaration!(Prefix::Webkit, "-webkit-shape-outside", None);
3311            }
3312
3313            "shape-image-threshold" => {
3314                add_declaration!(Prefix::Webkit, "-webkit-shape-image-threshold", None);
3315            }
3316
3317            "object-fit" => {
3318                add_declaration!(Prefix::O, "-o-object-fit", None);
3319            }
3320
3321            "object-position" => {
3322                add_declaration!(Prefix::O, "-o-object-position", None);
3323            }
3324
3325            "overflow-wrap" => {
3326                add_declaration!("word-wrap", None);
3327            }
3328
3329            "overflow" if should_prefix("overflow", &self.env, false) && n.value.len() == 2 => {
3330                if let (
3331                    Some(left @ ComponentValue::Ident(first)),
3332                    Some(right @ ComponentValue::Ident(second)),
3333                ) = (n.value.first(), n.value.get(1))
3334                {
3335                    if first.value.eq_ignore_ascii_case(&second.value) {
3336                        self.added_declarations.push(Box::new(Declaration {
3337                            span: n.span,
3338                            name: n.name.clone(),
3339                            value: vec![left.clone()],
3340                            important: n.important.clone(),
3341                        }));
3342                    } else {
3343                        self.added_declarations.push(Box::new(Declaration {
3344                            span: n.span,
3345                            name: DeclarationName::Ident(Ident {
3346                                span: DUMMY_SP,
3347                                value: atom!("overflow-x"),
3348                                raw: None,
3349                            }),
3350                            value: vec![left.clone()],
3351                            important: n.important.clone(),
3352                        }));
3353                        self.added_declarations.push(Box::new(Declaration {
3354                            span: n.span,
3355                            name: DeclarationName::Ident(Ident {
3356                                span: DUMMY_SP,
3357                                value: atom!("overflow-y"),
3358                                raw: None,
3359                            }),
3360                            value: vec![right.clone()],
3361                            important: n.important.clone(),
3362                        }));
3363                    }
3364                }
3365            }
3366
3367            "tab-size" => {
3368                add_declaration!(Prefix::Moz, "-moz-tab-size", None);
3369                add_declaration!(Prefix::O, "-o-tab-size", None);
3370            }
3371
3372            "hyphens" => {
3373                add_declaration!(Prefix::Webkit, "-webkit-hyphens", None);
3374                add_declaration!(Prefix::Moz, "-moz-hyphens", None);
3375                add_declaration!(Prefix::Ms, "-ms-hyphens", None);
3376            }
3377
3378            "border-image" => {
3379                add_declaration!(Prefix::Webkit, "-webkit-border-image", None);
3380                add_declaration!(Prefix::Moz, "-moz-border-image", None);
3381                add_declaration!(Prefix::O, "-o-border-image", None);
3382            }
3383
3384            "font-kerning" => {
3385                add_declaration!(Prefix::Webkit, "-webkit-font-kerning", None);
3386            }
3387
3388            "font-feature-settings" => {
3389                add_declaration!(Prefix::Webkit, "-webkit-font-feature-settings", None);
3390                add_declaration!(Prefix::Moz, "-moz-font-feature-settings", None);
3391            }
3392
3393            "font-variant-ligatures" => {
3394                add_declaration!(Prefix::Webkit, "-webkit-font-variant-ligatures", None);
3395                add_declaration!(Prefix::Moz, "-moz-font-variant-ligatures", None);
3396            }
3397
3398            "font-language-override" => {
3399                add_declaration!(Prefix::Webkit, "-webkit-font-language-override", None);
3400                add_declaration!(Prefix::Moz, "-moz-font-language-override", None);
3401            }
3402
3403            "background-origin" => {
3404                add_declaration!(Prefix::Webkit, "-webkit-background-origin", None);
3405                add_declaration!(Prefix::Moz, "-moz-background-origin", None);
3406                add_declaration!(Prefix::O, "-o-background-origin", None);
3407            }
3408
3409            "background-size" => {
3410                add_declaration!(Prefix::Webkit, "-webkit-background-size", None);
3411                add_declaration!(Prefix::Moz, "-moz-background-size", None);
3412                add_declaration!(Prefix::O, "-o-background-size", None);
3413            }
3414
3415            "overscroll-behavior" => {
3416                if let ComponentValue::Ident(ident) = &n.value[0] {
3417                    match &*ident.value.to_ascii_lowercase() {
3418                        "auto" => {
3419                            add_declaration!(
3420                                Prefix::Ms,
3421                                "-ms-scroll-chaining",
3422                                Box::new(|| { vec![to_ident!("chained")] })
3423                            );
3424                        }
3425                        "none" | "contain" => {
3426                            add_declaration!(
3427                                Prefix::Ms,
3428                                "-ms-scroll-chaining",
3429                                Box::new(|| { vec![to_ident!("none")] })
3430                            );
3431                        }
3432                        _ => {
3433                            add_declaration!(Prefix::Ms, "-ms-scroll-chaining", None);
3434                        }
3435                    }
3436                } else {
3437                    add_declaration!(Prefix::Ms, "-ms-scroll-chaining", None);
3438                }
3439            }
3440
3441            "box-shadow" => {
3442                add_declaration!(Prefix::Webkit, "-webkit-box-shadow", None);
3443                add_declaration!(Prefix::Moz, "-moz-box-shadow", None);
3444            }
3445
3446            "forced-color-adjust" => {
3447                add_declaration!(Prefix::Ms, "-ms-high-contrast-adjust", None);
3448            }
3449
3450            "break-inside" => {
3451                if let ComponentValue::Ident(ident) = &n.value[0] {
3452                    match &*ident.value.to_ascii_lowercase() {
3453                        "auto" | "avoid" | "initial" | "inherit" | "revert" | "revert-layer" => {
3454                            add_declaration!(Prefix::Webkit, "-webkit-column-break-inside", None);
3455                            add_declaration!("page-break-inside", None);
3456                        }
3457                        _ => {}
3458                    }
3459                }
3460            }
3461
3462            "break-before" => {
3463                if let ComponentValue::Ident(ident) = &n.value[0] {
3464                    match &*ident.value.to_ascii_lowercase() {
3465                        "auto" | "avoid" | "initial" | "inherit" | "revert" | "revert-layer" => {
3466                            add_declaration!(Prefix::Webkit, "-webkit-column-break-before", None);
3467                            add_declaration!("page-break-before", None);
3468                        }
3469                        "left" | "right" => {
3470                            add_declaration!("page-break-before", None);
3471                        }
3472                        "page" => {
3473                            add_declaration!(
3474                                "page-break-before",
3475                                Some(Box::new(|| { vec![to_ident!("always")] }))
3476                            );
3477                        }
3478                        "column" => {
3479                            add_declaration!(
3480                                Prefix::Webkit,
3481                                "-webkit-column-break-before",
3482                                Box::new(|| { vec![to_ident!("always")] })
3483                            );
3484                        }
3485                        _ => {}
3486                    }
3487                }
3488            }
3489
3490            "break-after" => {
3491                if let ComponentValue::Ident(ident) = &n.value[0] {
3492                    match &*ident.value.to_ascii_lowercase() {
3493                        "auto" | "avoid" | "initial" | "inherit" | "revert" | "revert-layer" => {
3494                            add_declaration!(Prefix::Webkit, "-webkit-column-break-after", None);
3495                            add_declaration!("page-break-after", None);
3496                        }
3497                        "left" | "right" => {
3498                            add_declaration!("page-break-after", None);
3499                        }
3500                        "page" => {
3501                            add_declaration!(
3502                                "page-break-after",
3503                                Some(Box::new(|| { vec![to_ident!("always")] }))
3504                            );
3505                        }
3506                        "column" => {
3507                            add_declaration!(
3508                                Prefix::Webkit,
3509                                "-webkit-column-break-after",
3510                                Box::new(|| { vec![to_ident!("always")] })
3511                            );
3512                        }
3513                        _ => {}
3514                    }
3515                }
3516            }
3517
3518            "border-radius" => {
3519                add_declaration!(Prefix::Webkit, "-webkit-border-radius", None);
3520                add_declaration!(Prefix::Moz, "-moz-border-radius", None);
3521            }
3522
3523            "border-top-left-radius" => {
3524                add_declaration!(Prefix::Webkit, "-webkit-border-top-left-radius", None);
3525                add_declaration!(Prefix::Moz, "-moz-border-radius-topleft", None);
3526            }
3527
3528            "border-top-right-radius" => {
3529                add_declaration!(Prefix::Webkit, "-webkit-border-top-right-radius", None);
3530                add_declaration!(Prefix::Moz, "-moz-border-radius-topright", None);
3531            }
3532
3533            "border-bottom-right-radius" => {
3534                add_declaration!(Prefix::Webkit, "-webkit-border-bottom-right-radius", None);
3535                add_declaration!(Prefix::Moz, "-moz-border-radius-bottomright", None);
3536            }
3537
3538            "border-bottom-left-radius" => {
3539                add_declaration!(Prefix::Webkit, "-webkit-border-bottom-left-radius", None);
3540                add_declaration!(Prefix::Moz, "-moz-border-radius-bottomleft", None);
3541            }
3542
3543            "src" if should_prefix("font-face-format-ident", &self.env, false) => {
3544                let mut new_declaration = n.clone();
3545
3546                font_face_format_old_syntax(&mut new_declaration);
3547
3548                if n.value != new_declaration.value {
3549                    self.added_declarations.push(Box::new(new_declaration));
3550                }
3551            }
3552
3553            "place-content" if should_prefix("place-content", &self.env, false) => {
3554                match (n.value.first(), n.value.get(1)) {
3555                    (Some(left), Some(right)) => {
3556                        add_declaration!(
3557                            "align-content",
3558                            Some(Box::new(|| { vec![left.clone()] }))
3559                        );
3560                        add_declaration!(
3561                            "justify-content",
3562                            Some(Box::new(|| { vec![right.clone()] }))
3563                        );
3564                    }
3565                    (Some(left), None) => {
3566                        add_declaration!(
3567                            "align-content",
3568                            Some(Box::new(|| { vec![left.clone()] }))
3569                        );
3570                        add_declaration!(
3571                            "justify-content",
3572                            Some(Box::new(|| { vec![left.clone()] }))
3573                        );
3574                    }
3575                    _ => {}
3576                }
3577            }
3578
3579            "place-items" if should_prefix("place-items", &self.env, false) => {
3580                match (n.value.first(), n.value.get(1)) {
3581                    (Some(left), Some(right)) => {
3582                        add_declaration!("align-items", Some(Box::new(|| { vec![left.clone()] })));
3583                        add_declaration!(
3584                            "justify-items",
3585                            Some(Box::new(|| { vec![right.clone()] }))
3586                        );
3587                    }
3588                    (Some(left), None) => {
3589                        add_declaration!("align-items", Some(Box::new(|| { vec![left.clone()] })));
3590                        add_declaration!(
3591                            "justify-items",
3592                            Some(Box::new(|| { vec![left.clone()] }))
3593                        );
3594                    }
3595                    _ => {}
3596                }
3597            }
3598
3599            "place-self" if should_prefix("place-self", &self.env, false) => {
3600                match (n.value.first(), n.value.get(1)) {
3601                    (Some(left), Some(right)) => {
3602                        add_declaration!("align-self", Some(Box::new(|| { vec![left.clone()] })));
3603                        add_declaration!(
3604                            "justify-self",
3605                            Some(Box::new(|| { vec![right.clone()] }))
3606                        );
3607                    }
3608                    (Some(left), None) => {
3609                        add_declaration!("align-self", Some(Box::new(|| { vec![left.clone()] })));
3610                        add_declaration!("justify-self", Some(Box::new(|| { vec![left.clone()] })));
3611                    }
3612                    _ => {}
3613                }
3614            }
3615
3616            // TODO add `grid` support https://github.com/postcss/autoprefixer/tree/main/lib/hacks (starting with grid)
3617            // TODO fix me https://github.com/postcss/autoprefixer/blob/main/test/cases/custom-prefix.out.css
3618            _ => {}
3619        }
3620
3621        if !n.value.eq_ignore_span(&webkit_value) {
3622            self.added_declarations.push(Box::new(Declaration {
3623                span: n.span,
3624                name: n.name.clone(),
3625                value: webkit_value,
3626                important: n.important.clone(),
3627            }));
3628        }
3629
3630        if !n.value.eq_ignore_span(&moz_value) {
3631            self.added_declarations.push(Box::new(Declaration {
3632                span: n.span,
3633                name: n.name.clone(),
3634                value: moz_value,
3635                important: n.important.clone(),
3636            }));
3637        }
3638
3639        if !n.value.eq_ignore_span(&o_value) {
3640            self.added_declarations.push(Box::new(Declaration {
3641                span: n.span,
3642                name: n.name.clone(),
3643                value: o_value,
3644                important: n.important.clone(),
3645            }));
3646        }
3647
3648        if !n.value.eq_ignore_span(&ms_value) {
3649            self.added_declarations.push(Box::new(Declaration {
3650                span: n.span,
3651                name: n.name.clone(),
3652                value: ms_value.clone(),
3653                important: n.important.clone(),
3654            }));
3655        }
3656
3657        if should_prefix("calc-nested", &self.env, false) {
3658            let mut value = n.value.clone();
3659
3660            replace_calc(&mut value, None);
3661
3662            if !n.value.eq_ignore_span(&value) {
3663                self.added_declarations.push(Box::new(Declaration {
3664                    span: n.span,
3665                    name: n.name.clone(),
3666                    value,
3667                    important: n.important.clone(),
3668                }));
3669            }
3670        }
3671    }
3672}