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 "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 "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
314impl 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 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 if self.rule_prefix == Some(prefix) || self.rule_prefix.is_none() {
861 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 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 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 }
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 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 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 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 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 "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 "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 _ => {}
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}