1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::ops::Range;
6
7use crate::context::{DeclarationContext, PropertyHandlerContext};
8use crate::error::{ParserError, PrinterError};
9use crate::parser::ParserOptions;
10use crate::printer::Printer;
11use crate::properties::box_shadow::BoxShadowHandler;
12use crate::properties::custom::{CustomProperty, CustomPropertyName};
13use crate::properties::masking::MaskHandler;
14use crate::properties::text::{Direction, UnicodeBidi};
15use crate::properties::{
16 align::AlignHandler,
17 animation::AnimationHandler,
18 background::BackgroundHandler,
19 border::BorderHandler,
20 contain::ContainerHandler,
21 display::DisplayHandler,
22 flex::FlexHandler,
23 font::FontHandler,
24 grid::GridHandler,
25 list::ListStyleHandler,
26 margin_padding::*,
27 outline::OutlineHandler,
28 overflow::OverflowHandler,
29 position::PositionHandler,
30 prefix_handler::{FallbackHandler, PrefixHandler},
31 size::SizeHandler,
32 text::TextDecorationHandler,
33 transform::TransformHandler,
34 transition::TransitionHandler,
35 ui::ColorSchemeHandler,
36};
37use crate::properties::{Property, PropertyId};
38use crate::traits::{PropertyHandler, ToCss};
39use crate::values::ident::DashedIdent;
40use crate::values::string::CowArcStr;
41#[cfg(feature = "visitor")]
42use crate::visitor::Visit;
43use cssparser::*;
44
45#[derive(Debug, PartialEq, Clone, Default)]
51#[cfg_attr(feature = "visitor", derive(Visit), visit(visit_declaration_block, PROPERTIES))]
52#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
53#[cfg_attr(
54 feature = "serde",
55 derive(serde::Serialize, serde::Deserialize),
56 serde(rename_all = "camelCase")
57)]
58#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
59pub struct DeclarationBlock<'i> {
60 #[cfg_attr(feature = "serde", serde(borrow, default))]
62 pub important_declarations: Vec<Property<'i>>,
63 #[cfg_attr(feature = "serde", serde(default))]
65 pub declarations: Vec<Property<'i>>,
66}
67
68impl<'i> DeclarationBlock<'i> {
69 pub fn parse<'a, 'o, 't>(
71 input: &mut Parser<'i, 't>,
72 options: &'a ParserOptions<'o, 'i>,
73 ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
74 let mut important_declarations = DeclarationList::new();
75 let mut declarations = DeclarationList::new();
76 let mut decl_parser = PropertyDeclarationParser {
77 important_declarations: &mut important_declarations,
78 declarations: &mut declarations,
79 options,
80 };
81 let mut parser = RuleBodyParser::new(input, &mut decl_parser);
82 while let Some(res) = parser.next() {
83 if let Err((err, _)) = res {
84 if options.error_recovery {
85 options.warn(err);
86 continue;
87 }
88 return Err(err);
89 }
90 }
91
92 Ok(DeclarationBlock {
93 important_declarations,
94 declarations,
95 })
96 }
97
98 pub fn parse_string<'o>(
100 input: &'i str,
101 options: ParserOptions<'o, 'i>,
102 ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
103 let mut input = ParserInput::new(input);
104 let mut parser = Parser::new(&mut input);
105 let result = Self::parse(&mut parser, &options)?;
106 parser.expect_exhausted()?;
107 Ok(result)
108 }
109
110 pub fn new() -> Self {
112 Self {
113 declarations: vec![],
114 important_declarations: vec![],
115 }
116 }
117
118 pub fn len(&self) -> usize {
120 self.declarations.len() + self.important_declarations.len()
121 }
122}
123
124impl<'i> ToCss for DeclarationBlock<'i> {
125 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
126 where
127 W: std::fmt::Write,
128 {
129 let len = self.declarations.len() + self.important_declarations.len();
130 let mut i = 0;
131
132 macro_rules! write {
133 ($decls: expr, $important: literal) => {
134 for decl in &$decls {
135 decl.to_css(dest, $important)?;
136 if i != len - 1 {
137 dest.write_char(';')?;
138 dest.whitespace()?;
139 }
140 i += 1;
141 }
142 };
143 }
144
145 write!(self.declarations, false);
146 write!(self.important_declarations, true);
147 Ok(())
148 }
149}
150
151impl<'i> DeclarationBlock<'i> {
152 pub fn to_css_block<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
154 where
155 W: std::fmt::Write,
156 {
157 dest.whitespace()?;
158 dest.write_char('{')?;
159 dest.indent();
160
161 let mut i = 0;
162 let len = self.len();
163
164 macro_rules! write {
165 ($decls: expr, $important: literal) => {
166 for decl in &$decls {
167 dest.newline()?;
168 decl.to_css(dest, $important)?;
169 if i != len - 1 || !dest.minify {
170 dest.write_char(';')?;
171 }
172 i += 1;
173 }
174 };
175 }
176
177 write!(self.declarations, false);
178 write!(self.important_declarations, true);
179
180 dest.dedent();
181 dest.newline()?;
182 dest.write_char('}')
183 }
184}
185
186impl<'i> DeclarationBlock<'i> {
187 pub(crate) fn minify(
188 &mut self,
189 handler: &mut DeclarationHandler<'i>,
190 important_handler: &mut DeclarationHandler<'i>,
191 context: &mut PropertyHandlerContext<'i, '_>,
192 ) {
193 macro_rules! handle {
194 ($decls: expr, $handler: expr, $important: literal) => {
195 for decl in $decls.iter() {
196 context.is_important = $important;
197 let handled = $handler.handle_property(decl, context);
198
199 if !handled {
200 $handler.decls.push(decl.clone());
201 }
202 }
203 };
204 }
205
206 handle!(self.important_declarations, important_handler, true);
207 handle!(self.declarations, handler, false);
208
209 handler.finalize(context);
210 important_handler.finalize(context);
211 self.important_declarations = std::mem::take(&mut important_handler.decls);
212 self.declarations = std::mem::take(&mut handler.decls);
213 }
214
215 pub fn is_empty(&self) -> bool {
217 return self.declarations.is_empty() && self.important_declarations.is_empty();
218 }
219
220 pub(crate) fn property_location<'t>(
221 &self,
222 input: &mut Parser<'i, 't>,
223 index: usize,
224 ) -> Result<(Range<SourceLocation>, Range<SourceLocation>), ParseError<'i, ParserError<'i>>> {
225 for _ in 0..index {
227 input.expect_ident()?;
228 input.expect_colon()?;
229 input.parse_until_after(Delimiter::Semicolon, |parser| {
230 while parser.next().is_ok() {}
231 Ok(())
232 })?;
233 }
234
235 input.skip_whitespace();
237 let key_start = input.current_source_location();
238 input.expect_ident()?;
239 let key_end = input.current_source_location();
240 let key_range = key_start..key_end;
241
242 input.expect_colon()?;
243 input.skip_whitespace();
244
245 let val_start = input.current_source_location();
247 input.parse_until_before(Delimiter::Semicolon, |parser| {
248 while parser.next().is_ok() {}
249 Ok(())
250 })?;
251 let val_end = input.current_source_location();
252 let val_range = val_start..val_end;
253
254 Ok((key_range, val_range))
255 }
256}
257
258impl<'i> DeclarationBlock<'i> {
259 pub fn iter(&self) -> impl std::iter::DoubleEndedIterator<Item = (&Property<'i>, bool)> {
261 self
262 .declarations
263 .iter()
264 .map(|property| (property, false))
265 .chain(self.important_declarations.iter().map(|property| (property, true)))
266 }
267
268 pub fn iter_mut(&mut self) -> impl std::iter::DoubleEndedIterator<Item = &mut Property<'i>> {
270 self.declarations.iter_mut().chain(self.important_declarations.iter_mut())
271 }
272
273 pub fn get<'a>(&'a self, property_id: &PropertyId) -> Option<(Cow<'a, Property<'i>>, bool)> {
279 if property_id.is_shorthand() {
280 if let Some((shorthand, important)) = property_id.shorthand_value(&self) {
281 return Some((Cow::Owned(shorthand), important));
282 }
283 } else {
284 for (property, important) in self.iter().rev() {
285 if property.property_id() == *property_id {
286 return Some((Cow::Borrowed(property), important));
287 }
288
289 if let Some(val) = property.longhand(&property_id) {
290 return Some((Cow::Owned(val), important));
291 }
292 }
293 }
294
295 None
296 }
297
298 pub fn set(&mut self, property: Property<'i>, important: bool) {
304 let property_id = property.property_id();
305 let declarations = if important {
306 self.declarations.retain(|decl| decl.property_id() != property_id);
308 &mut self.important_declarations
309 } else {
310 self.important_declarations.retain(|decl| decl.property_id() != property_id);
312 &mut self.declarations
313 };
314
315 let longhands = property_id.longhands().unwrap_or_else(|| vec![property.property_id()]);
316
317 for decl in declarations.iter_mut().rev() {
318 {
319 let id = decl.property_id();
323 let id_longhands = id.longhands().unwrap_or_else(|| vec![id]);
324 if longhands.iter().any(|longhand| {
325 let logical_group = longhand.logical_group();
326 let category = longhand.category();
327
328 logical_group.is_some()
329 && id_longhands.iter().any(|id_longhand| {
330 logical_group == id_longhand.logical_group() && category != id_longhand.category()
331 })
332 }) {
333 break;
334 }
335 }
336
337 if decl.property_id() == property_id {
338 *decl = property;
339 return;
340 }
341
342 if decl.set_longhand(&property).is_ok() {
344 return;
345 }
346 }
347
348 declarations.push(property)
349 }
350
351 pub fn remove(&mut self, property_id: &PropertyId) {
357 fn remove<'i, 'a>(declarations: &mut Vec<Property<'i>>, property_id: &PropertyId<'a>) {
358 let longhands = property_id.longhands().unwrap_or(vec![]);
359 let mut i = 0;
360 while i < declarations.len() {
361 let replacement = {
362 let property = &declarations[i];
363 let id = property.property_id();
364 if id == *property_id || longhands.contains(&id) {
365 None
368 } else if longhands.is_empty() && id.longhands().unwrap_or(vec![]).contains(&property_id) {
369 Some(
372 id.longhands()
373 .unwrap()
374 .iter()
375 .filter_map(|longhand| {
376 if *longhand == *property_id {
377 None
378 } else {
379 property.longhand(longhand)
380 }
381 })
382 .collect::<Vec<Property>>(),
383 )
384 } else {
385 i += 1;
386 continue;
387 }
388 };
389
390 match replacement {
391 Some(properties) => {
392 let count = properties.len();
393 declarations.splice(i..i + 1, properties);
394 i += count;
395 }
396 None => {
397 declarations.remove(i);
398 }
399 }
400 }
401 }
402
403 remove(&mut self.declarations, property_id);
404 remove(&mut self.important_declarations, property_id);
405 }
406}
407
408struct PropertyDeclarationParser<'a, 'o, 'i> {
409 important_declarations: &'a mut Vec<Property<'i>>,
410 declarations: &'a mut Vec<Property<'i>>,
411 options: &'a ParserOptions<'o, 'i>,
412}
413
414impl<'a, 'o, 'i> cssparser::DeclarationParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> {
416 type Declaration = ();
417 type Error = ParserError<'i>;
418
419 fn parse_value<'t>(
420 &mut self,
421 name: CowRcStr<'i>,
422 input: &mut cssparser::Parser<'i, 't>,
423 ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
424 parse_declaration(
425 name,
426 input,
427 &mut self.declarations,
428 &mut self.important_declarations,
429 &self.options,
430 )
431 }
432}
433
434impl<'a, 'o, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> {
436 type Prelude = ();
437 type AtRule = ();
438 type Error = ParserError<'i>;
439}
440
441impl<'a, 'o, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> {
442 type Prelude = ();
443 type QualifiedRule = ();
444 type Error = ParserError<'i>;
445}
446
447impl<'a, 'o, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for PropertyDeclarationParser<'a, 'o, 'i> {
448 fn parse_qualified(&self) -> bool {
449 false
450 }
451
452 fn parse_declarations(&self) -> bool {
453 true
454 }
455}
456
457pub(crate) fn parse_declaration<'i, 't>(
458 name: CowRcStr<'i>,
459 input: &mut cssparser::Parser<'i, 't>,
460 declarations: &mut DeclarationList<'i>,
461 important_declarations: &mut DeclarationList<'i>,
462 options: &ParserOptions<'_, 'i>,
463) -> Result<(), cssparser::ParseError<'i, ParserError<'i>>> {
464 let property_id = PropertyId::from(CowArcStr::from(name));
468 let mut delimiters = Delimiter::Bang;
469 if !matches!(property_id, PropertyId::Custom(CustomPropertyName::Custom(..))) {
470 delimiters = delimiters | Delimiter::CurlyBracketBlock;
471 }
472 let property = input.parse_until_before(delimiters, |input| Property::parse(property_id, input, options))?;
473 let important = input
474 .try_parse(|input| {
475 input.expect_delim('!')?;
476 input.expect_ident_matching("important")
477 })
478 .is_ok();
479 input.expect_exhausted()?;
480 if important {
481 important_declarations.push(property);
482 } else {
483 declarations.push(property);
484 }
485 Ok(())
486}
487
488pub(crate) type DeclarationList<'i> = Vec<Property<'i>>;
489
490#[derive(Default)]
491pub(crate) struct DeclarationHandler<'i> {
492 background: BackgroundHandler<'i>,
493 border: BorderHandler<'i>,
494 outline: OutlineHandler,
495 flex: FlexHandler,
496 grid: GridHandler<'i>,
497 align: AlignHandler,
498 size: SizeHandler<'i>,
499 margin: MarginHandler<'i>,
500 padding: PaddingHandler<'i>,
501 scroll_margin: ScrollMarginHandler<'i>,
502 scroll_padding: ScrollPaddingHandler<'i>,
503 font: FontHandler<'i>,
504 text: TextDecorationHandler<'i>,
505 list: ListStyleHandler<'i>,
506 transition: TransitionHandler<'i>,
507 animation: AnimationHandler<'i>,
508 display: DisplayHandler<'i>,
509 position: PositionHandler,
510 inset: InsetHandler<'i>,
511 overflow: OverflowHandler,
512 transform: TransformHandler,
513 box_shadow: BoxShadowHandler,
514 mask: MaskHandler<'i>,
515 container: ContainerHandler<'i>,
516 color_scheme: ColorSchemeHandler,
517 fallback: FallbackHandler,
518 prefix: PrefixHandler,
519 direction: Option<Direction>,
520 unicode_bidi: Option<UnicodeBidi>,
521 custom_properties: HashMap<DashedIdent<'i>, usize>,
522 decls: DeclarationList<'i>,
523}
524
525impl<'i> DeclarationHandler<'i> {
526 pub fn handle_property(
527 &mut self,
528 property: &Property<'i>,
529 context: &mut PropertyHandlerContext<'i, '_>,
530 ) -> bool {
531 self.background.handle_property(property, &mut self.decls, context)
532 || self.border.handle_property(property, &mut self.decls, context)
533 || self.outline.handle_property(property, &mut self.decls, context)
534 || self.flex.handle_property(property, &mut self.decls, context)
535 || self.grid.handle_property(property, &mut self.decls, context)
536 || self.align.handle_property(property, &mut self.decls, context)
537 || self.size.handle_property(property, &mut self.decls, context)
538 || self.margin.handle_property(property, &mut self.decls, context)
539 || self.padding.handle_property(property, &mut self.decls, context)
540 || self.scroll_margin.handle_property(property, &mut self.decls, context)
541 || self.scroll_padding.handle_property(property, &mut self.decls, context)
542 || self.font.handle_property(property, &mut self.decls, context)
543 || self.text.handle_property(property, &mut self.decls, context)
544 || self.list.handle_property(property, &mut self.decls, context)
545 || self.transition.handle_property(property, &mut self.decls, context)
546 || self.animation.handle_property(property, &mut self.decls, context)
547 || self.display.handle_property(property, &mut self.decls, context)
548 || self.position.handle_property(property, &mut self.decls, context)
549 || self.inset.handle_property(property, &mut self.decls, context)
550 || self.overflow.handle_property(property, &mut self.decls, context)
551 || self.transform.handle_property(property, &mut self.decls, context)
552 || self.box_shadow.handle_property(property, &mut self.decls, context)
553 || self.mask.handle_property(property, &mut self.decls, context)
554 || self.container.handle_property(property, &mut self.decls, context)
555 || self.color_scheme.handle_property(property, &mut self.decls, context)
556 || self.fallback.handle_property(property, &mut self.decls, context)
557 || self.prefix.handle_property(property, &mut self.decls, context)
558 || self.handle_all(property)
559 || self.handle_custom_property(property, context)
560 }
561
562 fn handle_custom_property(
563 &mut self,
564 property: &Property<'i>,
565 context: &mut PropertyHandlerContext<'i, '_>,
566 ) -> bool {
567 if let Property::Custom(custom) = property {
568 if context.unused_symbols.contains(custom.name.as_ref()) {
569 return true;
570 }
571
572 if let CustomPropertyName::Custom(name) = &custom.name {
573 if let Some(index) = self.custom_properties.get(name) {
574 if self.decls[*index] == *property {
575 return true;
576 }
577 let mut custom = custom.clone();
578 self.add_conditional_fallbacks(&mut custom, context);
579 self.decls[*index] = Property::Custom(custom);
580 } else {
581 self.custom_properties.insert(name.clone(), self.decls.len());
582 let mut custom = custom.clone();
583 self.add_conditional_fallbacks(&mut custom, context);
584 self.decls.push(Property::Custom(custom));
585 }
586
587 return true;
588 }
589 }
590
591 false
592 }
593
594 fn handle_all(&mut self, property: &Property<'i>) -> bool {
595 match property {
598 Property::UnicodeBidi(bidi) => {
599 self.unicode_bidi = Some(*bidi);
600 true
601 }
602 Property::Direction(direction) => {
603 self.direction = Some(*direction);
604 true
605 }
606 Property::All(keyword) => {
607 let mut handler = DeclarationHandler {
608 unicode_bidi: self.unicode_bidi.clone(),
609 direction: self.direction.clone(),
610 ..Default::default()
611 };
612 for (key, index) in self.custom_properties.drain() {
613 handler.custom_properties.insert(key, handler.decls.len());
614 handler.decls.push(self.decls[index].clone());
615 }
616 handler.decls.push(Property::All(keyword.clone()));
617 *self = handler;
618 true
619 }
620 _ => false,
621 }
622 }
623
624 fn add_conditional_fallbacks(
625 &self,
626 custom: &mut CustomProperty<'i>,
627 context: &mut PropertyHandlerContext<'i, '_>,
628 ) {
629 if context.context != DeclarationContext::Keyframes {
630 let fallbacks = custom.value.get_fallbacks(context.targets);
631 for (condition, fallback) in fallbacks {
632 context.add_conditional_property(
633 condition,
634 Property::Custom(CustomProperty {
635 name: custom.name.clone(),
636 value: fallback,
637 }),
638 );
639 }
640 }
641 }
642
643 pub fn finalize(&mut self, context: &mut PropertyHandlerContext<'i, '_>) {
644 if let Some(direction) = std::mem::take(&mut self.direction) {
645 self.decls.push(Property::Direction(direction));
646 }
647 if let Some(unicode_bidi) = std::mem::take(&mut self.unicode_bidi) {
648 self.decls.push(Property::UnicodeBidi(unicode_bidi));
649 }
650
651 self.background.finalize(&mut self.decls, context);
652 self.border.finalize(&mut self.decls, context);
653 self.outline.finalize(&mut self.decls, context);
654 self.flex.finalize(&mut self.decls, context);
655 self.grid.finalize(&mut self.decls, context);
656 self.align.finalize(&mut self.decls, context);
657 self.size.finalize(&mut self.decls, context);
658 self.margin.finalize(&mut self.decls, context);
659 self.padding.finalize(&mut self.decls, context);
660 self.scroll_margin.finalize(&mut self.decls, context);
661 self.scroll_padding.finalize(&mut self.decls, context);
662 self.font.finalize(&mut self.decls, context);
663 self.text.finalize(&mut self.decls, context);
664 self.list.finalize(&mut self.decls, context);
665 self.transition.finalize(&mut self.decls, context);
666 self.animation.finalize(&mut self.decls, context);
667 self.display.finalize(&mut self.decls, context);
668 self.position.finalize(&mut self.decls, context);
669 self.inset.finalize(&mut self.decls, context);
670 self.overflow.finalize(&mut self.decls, context);
671 self.transform.finalize(&mut self.decls, context);
672 self.box_shadow.finalize(&mut self.decls, context);
673 self.mask.finalize(&mut self.decls, context);
674 self.container.finalize(&mut self.decls, context);
675 self.color_scheme.finalize(&mut self.decls, context);
676 self.fallback.finalize(&mut self.decls, context);
677 self.prefix.finalize(&mut self.decls, context);
678 self.custom_properties.clear();
679 }
680}