1use std::collections::HashSet;
2
3use crate::compat::Feature;
4use crate::declaration::DeclarationBlock;
5use crate::media_query::{
6 MediaCondition, MediaFeatureId, MediaFeatureName, MediaFeatureValue, MediaList, MediaQuery, MediaType,
7 QueryFeature,
8};
9use crate::properties::custom::UnparsedProperty;
10use crate::properties::Property;
11use crate::rules::media::MediaRule;
12use crate::rules::supports::{SupportsCondition, SupportsRule};
13use crate::rules::{style::StyleRule, CssRule, CssRuleList};
14use crate::selector::{Direction, PseudoClass};
15use crate::targets::Targets;
16use crate::values::ident::Ident;
17use crate::vendor_prefix::VendorPrefix;
18use parcel_selectors::parser::Component;
19
20#[derive(Debug)]
21pub(crate) struct SupportsEntry<'i> {
22 pub condition: SupportsCondition<'i>,
23 pub declarations: Vec<Property<'i>>,
24 pub important_declarations: Vec<Property<'i>>,
25}
26
27#[derive(Debug, PartialEq)]
28pub(crate) enum DeclarationContext {
29 None,
30 StyleRule,
31 Keyframes,
32 StyleAttribute,
33}
34
35#[derive(Debug)]
36pub(crate) struct PropertyHandlerContext<'i, 'o> {
37 pub targets: Targets,
38 pub is_important: bool,
39 supports: Vec<SupportsEntry<'i>>,
40 ltr: Vec<Property<'i>>,
41 rtl: Vec<Property<'i>>,
42 dark: Vec<Property<'i>>,
43 pub context: DeclarationContext,
44 pub unused_symbols: &'o HashSet<String>,
45}
46
47impl<'i, 'o> PropertyHandlerContext<'i, 'o> {
48 pub fn new(targets: Targets, unused_symbols: &'o HashSet<String>) -> Self {
49 PropertyHandlerContext {
50 targets,
51 is_important: false,
52 supports: Vec::new(),
53 ltr: Vec::new(),
54 rtl: Vec::new(),
55 dark: Vec::new(),
56 context: DeclarationContext::None,
57 unused_symbols,
58 }
59 }
60
61 pub fn child(&self, context: DeclarationContext) -> Self {
62 PropertyHandlerContext {
63 targets: self.targets,
64 is_important: false,
65 supports: Vec::new(),
66 ltr: Vec::new(),
67 rtl: Vec::new(),
68 dark: Vec::new(),
69 context,
70 unused_symbols: self.unused_symbols,
71 }
72 }
73
74 pub fn should_compile_logical(&self, feature: Feature) -> bool {
75 if self.context == DeclarationContext::StyleAttribute {
78 return false;
79 }
80
81 self.targets.should_compile_logical(feature)
82 }
83
84 pub fn add_logical_rule(&mut self, ltr: Property<'i>, rtl: Property<'i>) {
85 self.ltr.push(ltr);
86 self.rtl.push(rtl);
87 }
88
89 pub fn add_dark_rule(&mut self, property: Property<'i>) {
90 self.dark.push(property);
91 }
92
93 pub fn get_additional_rules<T>(&self, style_rule: &StyleRule<'i, T>) -> Vec<CssRule<'i, T>> {
94 let mut dest = Vec::new();
96
97 macro_rules! rule {
98 ($dir: ident, $decls: ident) => {
99 let mut selectors = style_rule.selectors.clone();
100 for selector in &mut selectors.0 {
101 selector.append(Component::NonTSPseudoClass(PseudoClass::Dir {
102 direction: Direction::$dir,
103 }));
104 }
105
106 let rule = StyleRule {
107 selectors,
108 vendor_prefix: VendorPrefix::None,
109 declarations: DeclarationBlock {
110 declarations: self.$decls.clone(),
111 important_declarations: vec![],
112 },
113 rules: CssRuleList(vec![]),
114 loc: style_rule.loc.clone(),
115 };
116
117 dest.push(CssRule::Style(rule));
118 };
119 }
120
121 if !self.ltr.is_empty() {
122 rule!(Ltr, ltr);
123 }
124
125 if !self.rtl.is_empty() {
126 rule!(Rtl, rtl);
127 }
128
129 if !self.dark.is_empty() {
130 dest.push(CssRule::Media(MediaRule {
131 query: MediaList {
132 media_queries: vec![MediaQuery {
133 qualifier: None,
134 media_type: MediaType::All,
135 condition: Some(MediaCondition::Feature(QueryFeature::Plain {
136 name: MediaFeatureName::Standard(MediaFeatureId::PrefersColorScheme),
137 value: MediaFeatureValue::Ident(Ident("dark".into())),
138 })),
139 }],
140 },
141 rules: CssRuleList(vec![CssRule::Style(StyleRule {
142 selectors: style_rule.selectors.clone(),
143 vendor_prefix: VendorPrefix::None,
144 declarations: DeclarationBlock {
145 declarations: self.dark.clone(),
146 important_declarations: vec![],
147 },
148 rules: CssRuleList(vec![]),
149 loc: style_rule.loc.clone(),
150 })]),
151 loc: style_rule.loc.clone(),
152 }))
153 }
154
155 dest
156 }
157
158 pub fn add_conditional_property(&mut self, condition: SupportsCondition<'i>, property: Property<'i>) {
159 if self.context != DeclarationContext::StyleRule {
160 return;
161 }
162
163 if let Some(entry) = self.supports.iter_mut().find(|supports| condition == supports.condition) {
164 if self.is_important {
165 entry.important_declarations.push(property);
166 } else {
167 entry.declarations.push(property);
168 }
169 } else {
170 let mut important_declarations = Vec::new();
171 let mut declarations = Vec::new();
172 if self.is_important {
173 important_declarations.push(property);
174 } else {
175 declarations.push(property);
176 }
177 self.supports.push(SupportsEntry {
178 condition,
179 important_declarations,
180 declarations,
181 });
182 }
183 }
184
185 pub fn add_unparsed_fallbacks(&mut self, unparsed: &mut UnparsedProperty<'i>) {
186 if self.context != DeclarationContext::StyleRule && self.context != DeclarationContext::StyleAttribute {
187 return;
188 }
189
190 let fallbacks = unparsed.value.get_fallbacks(self.targets);
191 for (condition, fallback) in fallbacks {
192 self.add_conditional_property(
193 condition,
194 Property::Unparsed(UnparsedProperty {
195 property_id: unparsed.property_id.clone(),
196 value: fallback,
197 }),
198 );
199 }
200 }
201
202 pub fn get_supports_rules<T>(&self, style_rule: &StyleRule<'i, T>) -> Vec<CssRule<'i, T>> {
203 if self.supports.is_empty() {
204 return Vec::new();
205 }
206
207 let mut dest = Vec::new();
208 for entry in &self.supports {
209 dest.push(CssRule::Supports(SupportsRule {
210 condition: entry.condition.clone(),
211 rules: CssRuleList(vec![CssRule::Style(StyleRule {
212 selectors: style_rule.selectors.clone(),
213 vendor_prefix: VendorPrefix::None,
214 declarations: DeclarationBlock {
215 declarations: entry.declarations.clone(),
216 important_declarations: entry.important_declarations.clone(),
217 },
218 rules: CssRuleList(vec![]),
219 loc: style_rule.loc.clone(),
220 })]),
221 loc: style_rule.loc.clone(),
222 }));
223 }
224
225 dest
226 }
227
228 pub fn reset(&mut self) {
229 self.supports.clear();
230 self.ltr.clear();
231 self.rtl.clear();
232 self.dark.clear();
233 }
234}