Skip to content

Commit dd73b66

Browse files
committed
Handle adding and removing vendor prefixed selectors
1 parent f1a074f commit dd73b66

File tree

6 files changed

+576
-110
lines changed

6 files changed

+576
-110
lines changed

src/lib.rs

Lines changed: 173 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,32 +2446,191 @@ mod tests {
24462446
..Browsers::default()
24472447
});
24482448

2449+
test(r#"
2450+
.foo:-moz-read-only {
2451+
color: red;
2452+
}
2453+
"#, indoc! {r#"
2454+
.foo:-moz-read-only {
2455+
color: red;
2456+
}
2457+
"#});
2458+
2459+
test(r#"
2460+
.foo:-moz-read-only {
2461+
color: red;
2462+
}
2463+
2464+
.foo:read-only {
2465+
color: red;
2466+
}
2467+
"#, indoc! {r#"
2468+
.foo:-moz-read-only {
2469+
color: red;
2470+
}
2471+
2472+
.foo:read-only {
2473+
color: red;
2474+
}
2475+
"#});
2476+
2477+
prefix_test(r#"
2478+
.foo:-moz-read-only {
2479+
color: red;
2480+
}
2481+
2482+
.foo:read-only {
2483+
color: red;
2484+
}
2485+
"#, indoc! {r#"
2486+
.foo:read-only {
2487+
color: red;
2488+
}
2489+
"#}, Browsers {
2490+
firefox: Some(85 << 16),
2491+
..Browsers::default()
2492+
});
2493+
24492494
prefix_test(r#"
2450-
.form-input:-moz-placeholder-shown {
2451-
color: black;
2495+
.foo:-moz-read-only {
2496+
color: red;
24522497
}
2453-
.form-input:-ms-input-placeholder {
2454-
color: black;
2498+
2499+
.bar {
2500+
color: yellow;
24552501
}
2456-
.form-input:placeholder-shown {
2457-
color: black;
2502+
2503+
.foo:read-only {
2504+
color: red;
24582505
}
24592506
"#, indoc! {r#"
2460-
.form-input:-moz-placeholder-shown {
2461-
color: #000;
2507+
.foo:-moz-read-only {
2508+
color: red;
24622509
}
24632510
2464-
.form-input:-ms-input-placeholder {
2465-
color: #000;
2511+
.bar {
2512+
color: #ff0;
24662513
}
24672514
2468-
.form-input:placeholder-shown {
2469-
color: #000;
2515+
.foo:read-only {
2516+
color: red;
24702517
}
24712518
"#}, Browsers {
24722519
firefox: Some(85 << 16),
2473-
chrome: Some(90 << 16),
2474-
edge: Some(85 << 16),
2520+
..Browsers::default()
2521+
});
2522+
2523+
prefix_test(r#"
2524+
.foo:-moz-read-only {
2525+
color: red;
2526+
}
2527+
2528+
.foo:read-only {
2529+
color: red;
2530+
}
2531+
"#, indoc! {r#"
2532+
.foo:-moz-read-only {
2533+
color: red;
2534+
}
2535+
2536+
.foo:read-only {
2537+
color: red;
2538+
}
2539+
"#}, Browsers {
2540+
firefox: Some(36 << 16),
2541+
..Browsers::default()
2542+
});
2543+
2544+
prefix_test(r#"
2545+
.foo:read-only {
2546+
color: red;
2547+
}
2548+
"#, indoc! {r#"
2549+
.foo:-moz-read-only {
2550+
color: red;
2551+
}
2552+
2553+
.foo:read-only {
2554+
color: red;
2555+
}
2556+
"#}, Browsers {
2557+
firefox: Some(36 << 16),
2558+
..Browsers::default()
2559+
});
2560+
2561+
prefix_test(r#"
2562+
.foo:-webkit-full-screen {
2563+
color: red;
2564+
}
2565+
.foo:-moz-full-screen {
2566+
color: red;
2567+
}
2568+
.foo:-ms-fullscreen {
2569+
color: red;
2570+
}
2571+
.foo:fullscreen {
2572+
color: red;
2573+
}
2574+
"#, indoc! {r#"
2575+
.foo:fullscreen {
2576+
color: red;
2577+
}
2578+
"#}, Browsers {
2579+
chrome: Some(96 << 16),
2580+
..Browsers::default()
2581+
});
2582+
2583+
prefix_test(r#"
2584+
.foo:fullscreen {
2585+
color: red;
2586+
}
2587+
"#, indoc! {r#"
2588+
.foo:-webkit-full-screen {
2589+
color: red;
2590+
}
2591+
2592+
.foo:-moz-full-screen {
2593+
color: red;
2594+
}
2595+
2596+
.foo:-ms-fullscreen {
2597+
color: red;
2598+
}
2599+
2600+
.foo:fullscreen {
2601+
color: red;
2602+
}
2603+
"#}, Browsers {
2604+
chrome: Some(45 << 16),
2605+
firefox: Some(45 << 16),
2606+
ie: Some(11 << 16),
2607+
..Browsers::default()
2608+
});
2609+
2610+
prefix_test(r#"
2611+
.foo::placeholder {
2612+
color: red;
2613+
}
2614+
"#, indoc! {r#"
2615+
.foo::-webkit-input-placeholder {
2616+
color: red;
2617+
}
2618+
2619+
.foo::-moz-placeholder {
2620+
color: red;
2621+
}
2622+
2623+
.foo::-ms-input-placeholder {
2624+
color: red;
2625+
}
2626+
2627+
.foo::placeholder {
2628+
color: red;
2629+
}
2630+
"#}, Browsers {
2631+
chrome: Some(45 << 16),
2632+
firefox: Some(45 << 16),
2633+
ie: Some(11 << 16),
24752634
..Browsers::default()
24762635
});
24772636
}

src/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser {
480480

481481
Ok(CssRule::Style(StyleRule {
482482
selectors,
483+
vendor_prefix: VendorPrefix::empty(),
483484
declarations: DeclarationBlock {
484485
declarations
485486
},

src/printer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
use std::fmt::*;
22
use cssparser::SourceLocation;
33
use parcel_sourcemap::{SourceMap, OriginalLocation};
4+
use crate::properties::VendorPrefix;
45

56
pub struct Printer<'a, W> {
67
dest: &'a mut W,
78
source_map: Option<&'a mut SourceMap>,
89
indent: u8,
910
line: u32,
1011
col: u32,
11-
pub minify: bool
12+
pub minify: bool,
13+
/// Vendor prefix override. When non-empty, it overrides
14+
/// the vendor prefix of whatever is being printed.
15+
pub vendor_prefix: VendorPrefix
1216
}
1317

1418
impl<'a, W: Write + Sized> Printer<'a, W> {
1519
pub fn new(dest: &'a mut W, source_map: Option<&'a mut SourceMap>, minify: bool) -> Printer<'a, W> {
16-
Printer { dest, source_map, indent: 0, line: 0, col: 0, minify }
20+
Printer { dest, source_map, indent: 0, line: 0, col: 0, minify, vendor_prefix: VendorPrefix::empty() }
1721
}
1822

1923
pub fn write_str(&mut self, s: &str) -> Result {

src/rules/mod.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::declaration::DeclarationHandler;
2323
use crate::properties::VendorPrefix;
2424
use crate::properties::prefixes::{Feature, Browsers};
2525
use std::collections::HashMap;
26+
use crate::selector::{is_equivalent, get_prefix, get_necessary_prefixes};
2627

2728
#[derive(Debug, PartialEq)]
2829
pub enum CssRule {
@@ -95,14 +96,41 @@ impl CssRuleList {
9596
CssRule::Style(style) => {
9697
style.minify(handler, important_handler);
9798

99+
if let Some(targets) = targets {
100+
style.vendor_prefix = get_prefix(&style.selectors);
101+
if style.vendor_prefix.contains(VendorPrefix::None) {
102+
style.vendor_prefix = get_necessary_prefixes(&style.selectors, targets);
103+
}
104+
}
105+
98106
if let Some(CssRule::Style(last_style_rule)) = rules.last_mut() {
107+
// Merge declarations if the selectors are equivalent, and both are compatible with all targets.
99108
if style.selectors == last_style_rule.selectors && style.is_compatible(targets) && last_style_rule.is_compatible(targets) {
100109
last_style_rule.declarations.declarations.extend(style.declarations.declarations.drain(..));
101110
last_style_rule.declarations.minify(handler, important_handler);
102111
continue
103-
} else if style.declarations == last_style_rule.declarations && style.is_compatible(targets) && last_style_rule.is_compatible(targets) {
104-
last_style_rule.selectors.0.extend(style.selectors.0.drain(..));
105-
continue
112+
} else if style.declarations == last_style_rule.declarations {
113+
// Append the selectors to the last rule if the declarations are the same, and all selectors are compatible.
114+
if style.is_compatible(targets) && last_style_rule.is_compatible(targets) {
115+
last_style_rule.selectors.0.extend(style.selectors.0.drain(..));
116+
continue
117+
}
118+
119+
// If both selectors are potentially vendor prefixable, and they are
120+
// equivalent minus prefixes, add the prefix to the last rule.
121+
if !style.vendor_prefix.is_empty() &&
122+
!last_style_rule.vendor_prefix.is_empty() &&
123+
is_equivalent(&style.selectors, &last_style_rule.selectors)
124+
{
125+
// If the new rule is unprefixed, replace the prefixes of the last rule.
126+
// Otherwise, add the new prefix.
127+
if style.vendor_prefix.contains(VendorPrefix::None) {
128+
last_style_rule.vendor_prefix = style.vendor_prefix;
129+
} else {
130+
last_style_rule.vendor_prefix |= style.vendor_prefix;
131+
}
132+
continue
133+
}
106134
}
107135
}
108136
},

src/rules/style.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ use crate::selector::{Selectors, is_compatible};
44
use crate::traits::ToCss;
55
use crate::printer::Printer;
66
use crate::declaration::{DeclarationBlock, DeclarationHandler};
7+
use crate::properties::VendorPrefix;
78
use crate::properties::prefixes::Browsers;
89

910
#[derive(Debug, PartialEq)]
1011
pub struct StyleRule {
1112
pub selectors: SelectorList<Selectors>,
13+
pub vendor_prefix: VendorPrefix,
1214
pub declarations: DeclarationBlock,
1315
pub loc: SourceLocation
1416
}
@@ -25,6 +27,42 @@ impl StyleRule {
2527

2628
impl ToCss for StyleRule {
2729
fn to_css<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
30+
if self.vendor_prefix.is_empty() {
31+
self.to_css_base(dest)
32+
} else {
33+
let mut first_rule = true;
34+
macro_rules! prefix {
35+
($prefix: ident) => {
36+
if self.vendor_prefix.contains(VendorPrefix::$prefix) {
37+
#[allow(unused_assignments)]
38+
if first_rule {
39+
first_rule = false;
40+
} else {
41+
if !dest.minify {
42+
dest.write_char('\n')?; // no indent
43+
}
44+
dest.newline()?;
45+
}
46+
dest.vendor_prefix = VendorPrefix::$prefix;
47+
self.to_css_base(dest)?;
48+
}
49+
};
50+
}
51+
52+
prefix!(WebKit);
53+
prefix!(Moz);
54+
prefix!(Ms);
55+
prefix!(O);
56+
prefix!(None);
57+
58+
dest.vendor_prefix = VendorPrefix::empty();
59+
Ok(())
60+
}
61+
}
62+
}
63+
64+
impl StyleRule {
65+
fn to_css_base<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
2866
dest.add_mapping(self.loc);
2967
self.selectors.to_css(dest)?;
3068
self.declarations.to_css(dest)

0 commit comments

Comments
 (0)