Skip to content

Commit df1bae2

Browse files
committed
Fix merging rules with vendor prefixes
Fixes parcel-bundler#478
1 parent 09f6b7a commit df1bae2

File tree

4 files changed

+131
-59
lines changed

4 files changed

+131
-59
lines changed

src/lib.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9250,6 +9250,91 @@ mod tests {
92509250
..Browsers::default()
92519251
},
92529252
);
9253+
9254+
prefix_test(
9255+
r#"
9256+
.foo:placeholder-shown .bar { color: red; }
9257+
.foo:autofill .baz { color: red; }
9258+
"#,
9259+
indoc! {r#"
9260+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9261+
color: red;
9262+
}
9263+
9264+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9265+
color: red;
9266+
}
9267+
"#},
9268+
Browsers {
9269+
chrome: Some(103 << 16),
9270+
..Browsers::default()
9271+
},
9272+
);
9273+
9274+
prefix_test(
9275+
r#"
9276+
.foo:placeholder-shown .bar,.foo:autofill .baz{color:red}
9277+
"#,
9278+
indoc! {r#"
9279+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9280+
color: red;
9281+
}
9282+
9283+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9284+
color: red;
9285+
}
9286+
"#},
9287+
Browsers {
9288+
chrome: Some(103 << 16),
9289+
..Browsers::default()
9290+
},
9291+
);
9292+
9293+
prefix_test(
9294+
r#"
9295+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9296+
color: red;
9297+
}
9298+
9299+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9300+
color: red;
9301+
}
9302+
"#,
9303+
indoc! {r#"
9304+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9305+
color: red;
9306+
}
9307+
9308+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9309+
color: red;
9310+
}
9311+
"#},
9312+
Browsers {
9313+
chrome: Some(103 << 16),
9314+
..Browsers::default()
9315+
},
9316+
);
9317+
9318+
test(
9319+
r#"
9320+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9321+
color: red;
9322+
}
9323+
9324+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9325+
color: red;
9326+
}
9327+
"#,
9328+
indoc! {r#"
9329+
.foo:placeholder-shown .bar, .foo:-webkit-autofill .baz {
9330+
color: red;
9331+
}
9332+
9333+
.foo:placeholder-shown .bar, .foo:autofill .baz {
9334+
color: red;
9335+
}
9336+
"#},
9337+
);
92539338
}
92549339

92559340
#[test]

src/rules/mod.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,9 @@ impl<'i, T> CssRuleList<'i, T> {
587587
continue;
588588
}
589589

590-
if context.targets.should_compile_selectors() {
591-
style.vendor_prefix = get_prefix(&style.selectors);
592-
if style.vendor_prefix.contains(VendorPrefix::None) {
593-
style.vendor_prefix = downlevel_selectors(style.selectors.0.as_mut_slice(), *context.targets);
594-
}
590+
style.vendor_prefix = get_prefix(&style.selectors);
591+
if style.vendor_prefix.contains(VendorPrefix::None) & context.targets.should_compile_selectors() {
592+
style.vendor_prefix = downlevel_selectors(style.selectors.0.as_mut_slice(), *context.targets);
595593
}
596594

597595
// Attempt to merge the new rule with the last rule we added.
@@ -724,28 +722,28 @@ fn merge_style_rules<'i, T>(
724722
&& style.rules.0.is_empty()
725723
&& last_style_rule.rules.0.is_empty()
726724
{
727-
// Append the selectors to the last rule if the declarations are the same, and all selectors are compatible.
728-
if style.is_compatible(context.targets.browsers) && last_style_rule.is_compatible(context.targets.browsers) {
729-
last_style_rule.selectors.0.extend(style.selectors.0.drain(..));
730-
return true;
731-
}
732-
733725
// If both selectors are potentially vendor prefixable, and they are
734726
// equivalent minus prefixes, add the prefix to the last rule.
735727
if !style.vendor_prefix.is_empty()
736728
&& !last_style_rule.vendor_prefix.is_empty()
737-
&& !last_style_rule.vendor_prefix.contains(style.vendor_prefix)
738729
&& is_equivalent(&style.selectors, &last_style_rule.selectors)
739730
{
740731
// If the new rule is unprefixed, replace the prefixes of the last rule.
741732
// Otherwise, add the new prefix.
742-
if style.vendor_prefix.contains(VendorPrefix::None) {
733+
if style.vendor_prefix.contains(VendorPrefix::None) && context.targets.should_compile_selectors() {
743734
last_style_rule.vendor_prefix = style.vendor_prefix;
744735
} else {
745736
last_style_rule.vendor_prefix |= style.vendor_prefix;
746737
}
747738
return true;
748739
}
740+
741+
// Append the selectors to the last rule if the declarations are the same, and all selectors are compatible.
742+
if style.is_compatible(context.targets.browsers) && last_style_rule.is_compatible(context.targets.browsers) {
743+
last_style_rule.selectors.0.extend(style.selectors.0.drain(..));
744+
last_style_rule.vendor_prefix |= style.vendor_prefix;
745+
return true;
746+
}
749747
}
750748
false
751749
}

src/rules/style.rs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -193,30 +193,19 @@ impl<'a, 'i, T: ToCss> ToCss for StyleRule<'i, T> {
193193
self.to_css_base(dest)
194194
} else {
195195
let mut first_rule = true;
196-
macro_rules! prefix {
197-
($prefix: ident) => {
198-
if self.vendor_prefix.contains(VendorPrefix::$prefix) {
199-
#[allow(unused_assignments)]
200-
if first_rule {
201-
first_rule = false;
202-
} else {
203-
if !dest.minify {
204-
dest.write_char('\n')?; // no indent
205-
}
206-
dest.newline()?;
207-
}
208-
dest.vendor_prefix = VendorPrefix::$prefix;
209-
self.to_css_base(dest)?;
196+
for prefix in self.vendor_prefix {
197+
if first_rule {
198+
first_rule = false;
199+
} else {
200+
if !dest.minify {
201+
dest.write_char('\n')?; // no indent
210202
}
211-
};
203+
dest.newline()?;
204+
}
205+
dest.vendor_prefix = prefix;
206+
self.to_css_base(dest)?;
212207
}
213208

214-
prefix!(WebKit);
215-
prefix!(Moz);
216-
prefix!(Ms);
217-
prefix!(O);
218-
prefix!(None);
219-
220209
dest.vendor_prefix = VendorPrefix::empty();
221210
Ok(())
222211
}

src/selector.rs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ where
606606
dest.write_char(':')?;
607607
// If the printer has a vendor prefix override, use that.
608608
let vp = if !dest.vendor_prefix.is_empty() {
609-
dest.vendor_prefix
609+
(dest.vendor_prefix & *$prefix).or_none()
610610
} else {
611611
*$prefix
612612
};
@@ -658,7 +658,7 @@ where
658658
Fullscreen(prefix) => {
659659
dest.write_char(':')?;
660660
let vp = if !dest.vendor_prefix.is_empty() {
661-
dest.vendor_prefix
661+
(dest.vendor_prefix & *prefix).or_none()
662662
} else {
663663
*prefix
664664
};
@@ -766,20 +766,21 @@ impl<'i> PseudoClass<'i> {
766766
}
767767
}
768768

769-
pub(crate) fn get_necessary_prefixes(&self, targets: Targets) -> VendorPrefix {
769+
pub(crate) fn get_necessary_prefixes(&mut self, targets: Targets) -> VendorPrefix {
770770
use crate::prefixes::Feature;
771771
use PseudoClass::*;
772-
let feature = match self {
773-
Fullscreen(p) if *p == VendorPrefix::None => Feature::PseudoClassFullscreen,
774-
AnyLink(p) if *p == VendorPrefix::None => Feature::PseudoClassAnyLink,
775-
ReadOnly(p) if *p == VendorPrefix::None => Feature::PseudoClassReadOnly,
776-
ReadWrite(p) if *p == VendorPrefix::None => Feature::PseudoClassReadWrite,
777-
PlaceholderShown(p) if *p == VendorPrefix::None => Feature::PseudoClassPlaceholderShown,
778-
Autofill(p) if *p == VendorPrefix::None => Feature::PseudoClassAutofill,
772+
let (p, feature) = match self {
773+
Fullscreen(p) => (p, Feature::PseudoClassFullscreen),
774+
AnyLink(p) => (p, Feature::PseudoClassAnyLink),
775+
ReadOnly(p) => (p, Feature::PseudoClassReadOnly),
776+
ReadWrite(p) => (p, Feature::PseudoClassReadWrite),
777+
PlaceholderShown(p) => (p, Feature::PseudoClassPlaceholderShown),
778+
Autofill(p) => (p, Feature::PseudoClassAutofill),
779779
_ => return VendorPrefix::empty(),
780780
};
781781

782-
targets.prefixes(VendorPrefix::None, feature)
782+
*p = targets.prefixes(*p, feature);
783+
*p
783784
}
784785
}
785786

@@ -1001,7 +1002,7 @@ where
10011002
dest.write_str("::")?;
10021003
// If the printer has a vendor prefix override, use that.
10031004
let vp = if !dest.vendor_prefix.is_empty() {
1004-
dest.vendor_prefix
1005+
(dest.vendor_prefix & *$prefix).or_none()
10051006
} else {
10061007
*$prefix
10071008
};
@@ -1169,18 +1170,19 @@ impl<'i> PseudoElement<'i> {
11691170
}
11701171
}
11711172

1172-
pub(crate) fn get_necessary_prefixes(&self, targets: Targets) -> VendorPrefix {
1173+
pub(crate) fn get_necessary_prefixes(&mut self, targets: Targets) -> VendorPrefix {
11731174
use crate::prefixes::Feature;
11741175
use PseudoElement::*;
1175-
let feature = match self {
1176-
Selection(p) if *p == VendorPrefix::None => Feature::PseudoElementSelection,
1177-
Placeholder(p) if *p == VendorPrefix::None => Feature::PseudoElementPlaceholder,
1178-
Backdrop(p) if *p == VendorPrefix::None => Feature::PseudoElementBackdrop,
1179-
FileSelectorButton(p) if *p == VendorPrefix::None => Feature::PseudoElementFileSelectorButton,
1176+
let (p, feature) = match self {
1177+
Selection(p) => (p, Feature::PseudoElementSelection),
1178+
Placeholder(p) => (p, Feature::PseudoElementPlaceholder),
1179+
Backdrop(p) => (p, Feature::PseudoElementBackdrop),
1180+
FileSelectorButton(p) => (p, Feature::PseudoElementFileSelectorButton),
11801181
_ => return VendorPrefix::empty(),
11811182
};
11821183

1183-
targets.prefixes(VendorPrefix::None, feature)
1184+
*p = targets.prefixes(*p, feature);
1185+
*p
11841186
}
11851187
}
11861188

@@ -1835,8 +1837,10 @@ pub(crate) fn get_prefix(selectors: &SelectorList) -> VendorPrefix {
18351837
};
18361838

18371839
if !p.is_empty() {
1838-
if prefix.is_empty() || prefix == p {
1839-
prefix = p;
1840+
// Allow none to be mixed with a prefix.
1841+
let prefix_without_none = prefix - VendorPrefix::None;
1842+
if prefix_without_none.is_empty() || prefix_without_none == p {
1843+
prefix |= p;
18401844
} else {
18411845
return VendorPrefix::empty();
18421846
}
@@ -1856,10 +1860,6 @@ const RTL_LANGS: &[&str] = &[
18561860
/// Returns the necessary vendor prefixes.
18571861
pub(crate) fn downlevel_selectors(selectors: &mut [Selector], targets: Targets) -> VendorPrefix {
18581862
let mut necessary_prefixes = VendorPrefix::empty();
1859-
if !targets.should_compile_selectors() {
1860-
return necessary_prefixes;
1861-
}
1862-
18631863
for selector in selectors {
18641864
for component in selector.iter_mut_raw_match_order() {
18651865
necessary_prefixes |= downlevel_component(component, targets);

0 commit comments

Comments
 (0)