Skip to main content

style/properties/
declaration_block.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A property declaration block.
6
7#![deny(missing_docs)]
8
9use super::{
10    property_counts, AllShorthand, ComputedValues, LogicalGroupSet, LonghandIdSet,
11    LonghandIdSetIterator, NonCustomPropertyId, NonCustomPropertyIdSet, PropertyDeclaration,
12    PropertyDeclarationId, PropertyId, ShorthandId, SourcePropertyDeclaration,
13    SourcePropertyDeclarationDrain, SubpropertiesVec,
14};
15
16use crate::context::{QuirksMode, TreeCountingCaches};
17use crate::custom_properties;
18use crate::derives::*;
19use crate::dom::{AttributeTracker, DummyElementContext};
20use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
21use crate::parser::ParserContext;
22use crate::properties::{
23    animated_properties::{AnimationValue, AnimationValueMap},
24    StyleBuilder,
25};
26use crate::rule_cache::RuleCacheConditions;
27use crate::rule_tree::RuleCascadeFlags;
28use crate::selector_map::PrecomputedHashSet;
29use crate::selector_parser::SelectorImpl;
30use crate::shared_lock::Locked;
31use crate::stylesheets::container_rule::ContainerSizeQuery;
32use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
33use crate::stylist::Stylist;
34use crate::typed_om::TypedValueList;
35use crate::values::computed::Context;
36use cssparser::{
37    parse_important, AtRuleParser, CowRcStr, DeclarationParser, Delimiter, ParseErrorKind, Parser,
38    ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
39};
40use itertools::Itertools;
41use selectors::SelectorList;
42use servo_arc::Arc;
43use smallbitvec::SmallBitVec;
44use smallvec::SmallVec;
45use std::borrow::Cow;
46use std::fmt::{self, Write};
47use std::iter::Zip;
48use std::slice::Iter;
49use std::sync::atomic::AtomicBool;
50use style_traits::{
51    CssString, CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,
52};
53use thin_vec::ThinVec;
54
55/// A set of property declarations including animations and transitions.
56#[derive(Default)]
57pub struct AnimationDeclarations {
58    /// Declarations for animations.
59    pub animations: Option<Arc<Locked<PropertyDeclarationBlock>>>,
60    /// Declarations for transitions.
61    pub transitions: Option<Arc<Locked<PropertyDeclarationBlock>>>,
62}
63
64impl AnimationDeclarations {
65    /// Whether or not this `AnimationDeclarations` is empty.
66    pub fn is_empty(&self) -> bool {
67        self.animations.is_none() && self.transitions.is_none()
68    }
69}
70
71/// An enum describes how a declaration should update
72/// the declaration block.
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74enum DeclarationUpdate {
75    /// The given declaration doesn't update anything.
76    None,
77    /// The given declaration is new, and should be append directly.
78    Append,
79    /// The given declaration can be updated in-place at the given position.
80    UpdateInPlace { pos: usize },
81    /// The given declaration cannot be updated in-place, and an existing
82    /// one needs to be removed at the given position.
83    AppendAndRemove { pos: usize },
84}
85
86/// A struct describes how a declaration block should be updated by
87/// a `SourcePropertyDeclaration`.
88#[derive(Default)]
89pub struct SourcePropertyDeclarationUpdate {
90    updates: SubpropertiesVec<DeclarationUpdate>,
91    new_count: usize,
92    any_removal: bool,
93}
94
95/// A declaration [importance][importance].
96///
97/// [importance]: https://drafts.csswg.org/css-cascade/#importance
98#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
99pub enum Importance {
100    /// Indicates a declaration without `!important`.
101    Normal,
102
103    /// Indicates a declaration with `!important`.
104    Important,
105}
106
107impl Default for Importance {
108    fn default() -> Self {
109        Self::Normal
110    }
111}
112
113impl Importance {
114    /// Return whether this is an important declaration.
115    pub fn important(self) -> bool {
116        match self {
117            Self::Normal => false,
118            Self::Important => true,
119        }
120    }
121}
122
123/// A set of properties.
124#[derive(Clone, Debug, ToShmem, Default, MallocSizeOf)]
125pub struct PropertyDeclarationIdSet {
126    longhands: LonghandIdSet,
127    custom: PrecomputedHashSet<custom_properties::Name>,
128}
129
130impl PropertyDeclarationIdSet {
131    /// Add the given property to the set.
132    pub fn insert(&mut self, id: PropertyDeclarationId) -> bool {
133        match id {
134            PropertyDeclarationId::Longhand(id) => {
135                if self.longhands.contains(id) {
136                    return false;
137                }
138                self.longhands.insert(id);
139                true
140            },
141            PropertyDeclarationId::Custom(name) => self.custom.insert((*name).clone()),
142        }
143    }
144
145    /// Return whether the given property is in the set.
146    pub fn contains(&self, id: PropertyDeclarationId) -> bool {
147        match id {
148            PropertyDeclarationId::Longhand(id) => self.longhands.contains(id),
149            PropertyDeclarationId::Custom(name) => self.custom.contains(name),
150        }
151    }
152
153    /// Remove the given property from the set.
154    pub fn remove(&mut self, id: PropertyDeclarationId) {
155        match id {
156            PropertyDeclarationId::Longhand(id) => self.longhands.remove(id),
157            PropertyDeclarationId::Custom(name) => {
158                self.custom.remove(name);
159            },
160        }
161    }
162
163    /// Remove all properties from the set.
164    pub fn clear(&mut self) {
165        self.longhands.clear();
166        self.custom.clear();
167    }
168
169    /// Returns whether the set is empty.
170    #[inline]
171    pub fn is_empty(&self) -> bool {
172        self.longhands.is_empty() && self.custom.is_empty()
173    }
174    /// Returns whether this set contains any reset longhand.
175    #[inline]
176    pub fn contains_any_reset(&self) -> bool {
177        self.longhands.contains_any_reset()
178    }
179
180    /// Returns whether this set contains all longhands in the specified set.
181    #[inline]
182    pub fn contains_all_longhands(&self, longhands: &LonghandIdSet) -> bool {
183        self.longhands.contains_all(longhands)
184    }
185
186    /// Returns whether this set contains all properties in the specified set.
187    #[inline]
188    pub fn contains_all(&self, properties: &PropertyDeclarationIdSet) -> bool {
189        if !self.longhands.contains_all(&properties.longhands) {
190            return false;
191        }
192        if properties.custom.len() > self.custom.len() {
193            return false;
194        }
195        properties
196            .custom
197            .iter()
198            .all(|item| self.custom.contains(item))
199    }
200
201    /// Iterate over the current property declaration id set.
202    pub fn iter(&self) -> PropertyDeclarationIdSetIterator<'_> {
203        PropertyDeclarationIdSetIterator {
204            longhands: self.longhands.iter(),
205            custom: self.custom.iter(),
206        }
207    }
208}
209
210/// An iterator over a set of longhand ids.
211pub struct PropertyDeclarationIdSetIterator<'a> {
212    longhands: LonghandIdSetIterator<'a>,
213    custom: std::collections::hash_set::Iter<'a, custom_properties::Name>,
214}
215
216impl<'a> Iterator for PropertyDeclarationIdSetIterator<'a> {
217    type Item = PropertyDeclarationId<'a>;
218
219    fn next(&mut self) -> Option<Self::Item> {
220        // LonghandIdSetIterator's implementation always returns None
221        // after it did it once, so the code below will then continue
222        // to iterate over the custom properties.
223        match self.longhands.next() {
224            Some(id) => Some(PropertyDeclarationId::Longhand(id)),
225            None => self.custom.next().map(PropertyDeclarationId::Custom),
226        }
227    }
228}
229
230/// Overridden declarations are skipped.
231#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
232#[derive(Default)]
233pub struct PropertyDeclarationBlock {
234    /// The group of declarations, along with their importance.
235    ///
236    /// Only deduplicated declarations appear here.
237    declarations: ThinVec<PropertyDeclaration>,
238
239    /// The "important" flag for each declaration in `declarations`.
240    declarations_importance: SmallBitVec,
241
242    /// The set of properties that are present in the block.
243    property_ids: PropertyDeclarationIdSet,
244
245    /// Whether this declaration block may be mutated by CSSOM without copying.
246    /// Set when a declaration is shared across elements and needs to be copied, even when not in
247    /// the rule tree, e.g. via the style attribute or the XUL prototype caches.
248    pub immutable: AtomicBool,
249}
250
251impl to_shmem::ToShmem for PropertyDeclarationBlock {
252    fn to_shmem(&self, builder: &mut to_shmem::SharedMemoryBuilder) -> to_shmem::Result<Self> {
253        use std::mem::ManuallyDrop;
254        let declarations = self.declarations.to_shmem(builder)?;
255        let declarations_importance = self.declarations_importance.to_shmem(builder)?;
256        let property_ids = self.property_ids.to_shmem(builder)?;
257        let immutable = AtomicBool::new(true);
258
259        Ok(ManuallyDrop::new(Self {
260            declarations: ManuallyDrop::into_inner(declarations),
261            declarations_importance: ManuallyDrop::into_inner(declarations_importance),
262            property_ids: ManuallyDrop::into_inner(property_ids),
263            immutable,
264        }))
265    }
266}
267
268impl Clone for PropertyDeclarationBlock {
269    fn clone(&self) -> Self {
270        Self {
271            declarations: self.declarations.clone(),
272            declarations_importance: self.declarations_importance.clone(),
273            property_ids: self.property_ids.clone(),
274            immutable: AtomicBool::new(false),
275        }
276    }
277}
278
279impl PartialEq for PropertyDeclarationBlock {
280    fn eq(&self, other: &Self) -> bool {
281        // property_ids must be equal if declarations are equal, so we don't
282        // need to compare them explicitly.
283        // immutable doesn't matter for equality either.
284        self.declarations == other.declarations
285            && self.declarations_importance == other.declarations_importance
286    }
287}
288
289/// Iterator over `(PropertyDeclaration, Importance)` pairs.
290pub struct DeclarationImportanceIterator<'a> {
291    iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,
292}
293
294impl<'a> Default for DeclarationImportanceIterator<'a> {
295    fn default() -> Self {
296        Self {
297            iter: [].iter().zip(smallbitvec::Iter::default()),
298        }
299    }
300}
301
302impl<'a> DeclarationImportanceIterator<'a> {
303    /// Constructor.
304    fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
305        DeclarationImportanceIterator {
306            iter: declarations.iter().zip(important.iter()),
307        }
308    }
309}
310
311impl<'a> Iterator for DeclarationImportanceIterator<'a> {
312    type Item = (&'a PropertyDeclaration, Importance);
313
314    #[inline]
315    fn next(&mut self) -> Option<Self::Item> {
316        self.iter.next().map(|(decl, important)| {
317            (
318                decl,
319                if important {
320                    Importance::Important
321                } else {
322                    Importance::Normal
323                },
324            )
325        })
326    }
327
328    #[inline]
329    fn size_hint(&self) -> (usize, Option<usize>) {
330        self.iter.size_hint()
331    }
332}
333
334impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> {
335    #[inline(always)]
336    fn next_back(&mut self) -> Option<Self::Item> {
337        self.iter.next_back().map(|(decl, important)| {
338            (
339                decl,
340                if important {
341                    Importance::Important
342                } else {
343                    Importance::Normal
344                },
345            )
346        })
347    }
348}
349
350/// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
351pub struct AnimationValueIterator<'a, 'cx, 'cx_a: 'cx> {
352    iter: DeclarationImportanceIterator<'a>,
353    context: &'cx mut Context<'cx_a>,
354    style: &'a ComputedValues,
355    default_values: &'a ComputedValues,
356}
357
358impl<'a, 'cx, 'cx_a: 'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
359    fn new(
360        declarations: &'a PropertyDeclarationBlock,
361        context: &'cx mut Context<'cx_a>,
362        style: &'a ComputedValues,
363        default_values: &'a ComputedValues,
364    ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
365        AnimationValueIterator {
366            iter: declarations.declaration_importance_iter(),
367            context,
368            style,
369            default_values,
370        }
371    }
372}
373
374impl<'a, 'cx, 'cx_a: 'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
375    type Item = AnimationValue;
376    #[inline]
377    fn next(&mut self) -> Option<Self::Item> {
378        loop {
379            let (decl, importance) = self.iter.next()?;
380
381            if importance.important() {
382                continue;
383            }
384
385            let animation = AnimationValue::from_declaration(
386                decl,
387                self.context,
388                self.style,
389                self.default_values,
390                // TODO (descalante): should be able to get an attr from an animated element
391                &mut AttributeTracker::new_dummy(),
392            );
393
394            if let Some(anim) = animation {
395                return Some(anim);
396            }
397        }
398    }
399}
400
401impl fmt::Debug for PropertyDeclarationBlock {
402    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403        self.declarations.fmt(f)
404    }
405}
406
407impl PropertyDeclarationBlock {
408    /// Returns the number of declarations in the block.
409    #[inline]
410    pub fn len(&self) -> usize {
411        self.declarations.len()
412    }
413
414    /// Returns whether the block is empty.
415    #[inline]
416    pub fn is_empty(&self) -> bool {
417        self.declarations.is_empty()
418    }
419
420    /// Create an empty block
421    #[inline]
422    pub fn new() -> Self {
423        PropertyDeclarationBlock {
424            declarations: ThinVec::new(),
425            declarations_importance: SmallBitVec::new(),
426            property_ids: PropertyDeclarationIdSet::default(),
427            immutable: AtomicBool::new(false),
428        }
429    }
430
431    /// Create a block with a single declaration
432    pub fn with_one(declaration: PropertyDeclaration, importance: Importance) -> Self {
433        let mut property_ids = PropertyDeclarationIdSet::default();
434        property_ids.insert(declaration.id());
435        let mut declarations = ThinVec::with_capacity(1);
436        declarations.push(declaration);
437        PropertyDeclarationBlock {
438            declarations,
439            declarations_importance: SmallBitVec::from_elem(1, importance.important()),
440            property_ids,
441            immutable: AtomicBool::new(false),
442        }
443    }
444
445    /// The declarations in this block
446    #[inline]
447    pub fn declarations(&self) -> &[PropertyDeclaration] {
448        &self.declarations
449    }
450
451    /// The `important` flags for declarations in this block
452    #[inline]
453    pub fn declarations_importance(&self) -> &SmallBitVec {
454        &self.declarations_importance
455    }
456
457    /// Iterate over `(PropertyDeclaration, Importance)` pairs
458    #[inline]
459    pub fn declaration_importance_iter(&self) -> DeclarationImportanceIterator<'_> {
460        DeclarationImportanceIterator::new(&self.declarations, &self.declarations_importance)
461    }
462
463    /// Iterate over `PropertyDeclaration` for Importance::Normal
464    #[inline]
465    pub fn normal_declaration_iter(&self) -> impl DoubleEndedIterator<Item = &PropertyDeclaration> {
466        self.declaration_importance_iter()
467            .filter(|(_, importance)| !importance.important())
468            .map(|(declaration, _)| declaration)
469    }
470
471    /// Return an iterator of (AnimatableLonghand, AnimationValue).
472    #[inline]
473    pub fn to_animation_value_iter<'a, 'cx, 'cx_a: 'cx>(
474        &'a self,
475        context: &'cx mut Context<'cx_a>,
476        style: &'a ComputedValues,
477        default_values: &'a ComputedValues,
478    ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
479        AnimationValueIterator::new(self, context, style, default_values)
480    }
481
482    /// Returns whether this block contains any declaration with `!important`.
483    ///
484    /// This is based on the `declarations_importance` bit-vector,
485    /// which should be maintained whenever `declarations` is changed.
486    #[inline]
487    pub fn any_important(&self) -> bool {
488        !self.declarations_importance.all_false()
489    }
490
491    /// Returns whether this block contains any declaration without `!important`.
492    ///
493    /// This is based on the `declarations_importance` bit-vector,
494    /// which should be maintained whenever `declarations` is changed.
495    #[inline]
496    pub fn any_normal(&self) -> bool {
497        !self.declarations_importance.all_true()
498    }
499
500    /// Returns a `PropertyDeclarationIdSet` representing the properties that are changed in
501    /// this block.
502    #[inline]
503    pub fn property_ids(&self) -> &PropertyDeclarationIdSet {
504        &self.property_ids
505    }
506
507    /// Returns whether this block contains a declaration of a given property id.
508    #[inline]
509    pub fn contains(&self, id: PropertyDeclarationId) -> bool {
510        self.property_ids.contains(id)
511    }
512
513    /// Returns whether this block contains any reset longhand.
514    #[inline]
515    pub fn contains_any_reset(&self) -> bool {
516        self.property_ids.contains_any_reset()
517    }
518
519    /// Get a declaration for a given property.
520    ///
521    /// NOTE: This is linear time in the case of custom properties or in the
522    /// case the longhand is actually in the declaration block.
523    #[inline]
524    pub fn get(
525        &self,
526        property: PropertyDeclarationId,
527    ) -> Option<(&PropertyDeclaration, Importance)> {
528        if !self.contains(property) {
529            return None;
530        }
531        self.declaration_importance_iter()
532            .find(|(declaration, _)| declaration.id() == property)
533    }
534
535    /// Tries to serialize a given shorthand from the declarations in this
536    /// block.
537    pub fn shorthand_to_css(
538        &self,
539        shorthand: ShorthandId,
540        dest: &mut CssStringWriter,
541    ) -> fmt::Result {
542        // Step 1.2.1 of
543        // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
544        let mut list = SmallVec::<[&_; 10]>::new();
545        let mut important_count = 0;
546
547        // Step 1.2.2
548        for longhand in shorthand.longhands() {
549            // Step 1.2.2.1
550            let declaration = self.get(PropertyDeclarationId::Longhand(longhand));
551
552            // Step 1.2.2.2 & 1.2.2.3
553            match declaration {
554                Some((declaration, importance)) => {
555                    list.push(declaration);
556                    if importance.important() {
557                        important_count += 1;
558                    }
559                },
560                None => return Ok(()),
561            }
562        }
563
564        // If there is one or more longhand with important, and one or more
565        // without important, we don't serialize it as a shorthand.
566        if important_count > 0 && important_count != list.len() {
567            return Ok(());
568        }
569
570        // Step 1.2.3
571        // We don't print !important when serializing individual properties,
572        // so we treat this as a normal-importance property
573        match shorthand.get_shorthand_appendable_value(&list) {
574            Some(appendable_value) => append_declaration_value(dest, appendable_value),
575            None => Ok(()),
576        }
577    }
578
579    /// Find the value of the given property in this block and serialize it
580    ///
581    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue>
582    pub fn property_value_to_css(
583        &self,
584        property: &PropertyId,
585        dest: &mut CssStringWriter,
586    ) -> fmt::Result {
587        // Step 1.1: done when parsing a string to PropertyId
588
589        // Step 1.2
590        let longhand_or_custom = match property.as_shorthand() {
591            Ok(shorthand) => return self.shorthand_to_css(shorthand, dest),
592            Err(longhand_or_custom) => longhand_or_custom,
593        };
594
595        if let Some((value, _importance)) = self.get(longhand_or_custom) {
596            // Step 2
597            value.to_css(dest)
598        } else {
599            // Step 3
600            Ok(())
601        }
602    }
603
604    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority>
605    pub fn property_priority(&self, property: &PropertyId) -> Importance {
606        // Step 1: done when parsing a string to PropertyId
607
608        // Step 2
609        match property.as_shorthand() {
610            Ok(shorthand) => {
611                // Step 2.1 & 2.2 & 2.3
612                if shorthand.longhands().all(|l| {
613                    self.get(PropertyDeclarationId::Longhand(l))
614                        .is_some_and(|(_, importance)| importance.important())
615                }) {
616                    Importance::Important
617                } else {
618                    Importance::Normal
619                }
620            },
621            Err(longhand_or_custom) => {
622                // Step 3
623                self.get(longhand_or_custom)
624                    .map_or(Importance::Normal, |(_, importance)| importance)
625            },
626        }
627    }
628
629    /// Find the value of the given property in this block and reify it.
630    /// Returns `Err(())` if the property is not present in this declaration
631    /// block.
632    pub fn property_value_to_typed_value_list(
633        &self,
634        property: &PropertyId,
635    ) -> Result<Option<TypedValueList>, ()> {
636        match property.as_shorthand() {
637            Ok(shorthand) => {
638                if shorthand
639                    .longhands()
640                    .all(|longhand| self.contains(PropertyDeclarationId::Longhand(longhand)))
641                {
642                    Ok(None)
643                } else {
644                    Err(())
645                }
646            },
647            Err(longhand_or_custom) => match self.get(longhand_or_custom) {
648                Some((value, _importance)) => Ok(value.to_typed_value_list()),
649                None => Err(()),
650            },
651        }
652    }
653
654    /// Adds or overrides the declaration for a given property in this block.
655    ///
656    /// See the documentation of `push` to see what impact `source` has when the
657    /// property is already there.
658    pub fn extend(
659        &mut self,
660        mut drain: SourcePropertyDeclarationDrain,
661        importance: Importance,
662    ) -> bool {
663        let all_shorthand_len = match drain.all_shorthand {
664            AllShorthand::NotSet => 0,
665            AllShorthand::CSSWideKeyword(_) | AllShorthand::WithVariables(_) => {
666                property_counts::ALL_SHORTHAND_EXPANDED
667            },
668        };
669        let push_calls_count = drain.declarations.len() + all_shorthand_len;
670
671        // With deduplication the actual length increase may be less than this.
672        self.declarations.reserve(push_calls_count);
673
674        let mut changed = false;
675        for decl in &mut drain.declarations {
676            changed |= self.push(decl, importance);
677        }
678        drain
679            .all_shorthand
680            .declarations()
681            .fold(changed, |changed, decl| {
682                changed | self.push(decl, importance)
683            })
684    }
685
686    /// Adds or overrides the declaration for a given property in this block.
687    ///
688    /// Returns whether the declaration has changed.
689    ///
690    /// This is only used for parsing and internal use.
691    pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) -> bool {
692        let id = declaration.id();
693        if !self.property_ids.insert(id) {
694            let mut index_to_remove = None;
695            for (i, slot) in self.declarations.iter_mut().enumerate() {
696                if slot.id() != id {
697                    continue;
698                }
699
700                let important = self.declarations_importance[i];
701
702                // For declarations from parsing, non-important declarations
703                // shouldn't override existing important one.
704                if important && !importance.important() {
705                    return false;
706                }
707
708                index_to_remove = Some(i);
709                break;
710            }
711
712            if let Some(index) = index_to_remove {
713                self.declarations.remove(index);
714                self.declarations_importance.remove(index);
715                self.declarations.push(declaration);
716                self.declarations_importance.push(importance.important());
717                return true;
718            }
719        }
720
721        self.declarations.push(declaration);
722        self.declarations_importance.push(importance.important());
723        true
724    }
725
726    /// Prepares updating this declaration block with the given
727    /// `SourcePropertyDeclaration` and importance, and returns whether
728    /// there is something to update.
729    pub fn prepare_for_update(
730        &self,
731        source_declarations: &SourcePropertyDeclaration,
732        importance: Importance,
733        updates: &mut SourcePropertyDeclarationUpdate,
734    ) -> bool {
735        debug_assert!(updates.updates.is_empty());
736        // Check whether we are updating for an all shorthand change.
737        if !matches!(source_declarations.all_shorthand, AllShorthand::NotSet) {
738            debug_assert!(source_declarations.declarations.is_empty());
739            return source_declarations
740                .all_shorthand
741                .declarations()
742                .any(|decl| {
743                    !self.contains(decl.id())
744                        || self
745                            .declarations
746                            .iter()
747                            .enumerate()
748                            .find(|&(_, d)| d.id() == decl.id())
749                            .is_none_or(|(i, d)| {
750                                let important = self.declarations_importance[i];
751                                *d != decl || important != importance.important()
752                            })
753                });
754        }
755        // Fill `updates` with update information.
756        let mut any_update = false;
757        let new_count = &mut updates.new_count;
758        let any_removal = &mut updates.any_removal;
759        let updates = &mut updates.updates;
760        updates.extend(
761            source_declarations
762                .declarations
763                .iter()
764                .map(|declaration| {
765                    if !self.contains(declaration.id()) {
766                        return DeclarationUpdate::Append;
767                    }
768                    let longhand_id = declaration.id().as_longhand();
769                    if let Some(longhand_id) = longhand_id {
770                        if let Some(logical_group) = longhand_id.logical_group() {
771                            let mut needs_append = false;
772                            for (pos, decl) in self.declarations.iter().enumerate().rev() {
773                                let id = match decl.id().as_longhand() {
774                                    Some(id) => id,
775                                    None => continue,
776                                };
777                                if id == longhand_id {
778                                    if needs_append {
779                                        return DeclarationUpdate::AppendAndRemove { pos };
780                                    }
781                                    let important = self.declarations_importance[pos];
782                                    if decl == declaration && important == importance.important() {
783                                        return DeclarationUpdate::None;
784                                    }
785                                    return DeclarationUpdate::UpdateInPlace { pos };
786                                }
787                                if !needs_append
788                                    && id.logical_group() == Some(logical_group)
789                                    && id.is_logical() != longhand_id.is_logical()
790                                {
791                                    needs_append = true;
792                                }
793                            }
794                            unreachable!("Longhand should be found in loop above");
795                        }
796                    }
797                    self.declarations
798                        .iter()
799                        .enumerate()
800                        .find(|&(_, decl)| decl.id() == declaration.id())
801                        .map_or(DeclarationUpdate::Append, |(pos, decl)| {
802                            let important = self.declarations_importance[pos];
803                            if decl == declaration && important == importance.important() {
804                                DeclarationUpdate::None
805                            } else {
806                                DeclarationUpdate::UpdateInPlace { pos }
807                            }
808                        })
809                })
810                .inspect(|update| {
811                    if matches!(update, DeclarationUpdate::None) {
812                        return;
813                    }
814                    any_update = true;
815                    match update {
816                        DeclarationUpdate::Append => {
817                            *new_count += 1;
818                        },
819                        DeclarationUpdate::AppendAndRemove { .. } => {
820                            *any_removal = true;
821                        },
822                        _ => {},
823                    }
824                }),
825        );
826        any_update
827    }
828
829    /// Update this declaration block with the given data.
830    pub fn update(
831        &mut self,
832        drain: SourcePropertyDeclarationDrain,
833        importance: Importance,
834        updates: &mut SourcePropertyDeclarationUpdate,
835    ) {
836        let important = importance.important();
837        if !matches!(drain.all_shorthand, AllShorthand::NotSet) {
838            debug_assert!(updates.updates.is_empty());
839            for decl in drain.all_shorthand.declarations() {
840                let id = decl.id();
841                if self.property_ids.insert(id) {
842                    self.declarations.push(decl);
843                    self.declarations_importance.push(important);
844                } else {
845                    let (idx, slot) = self
846                        .declarations
847                        .iter_mut()
848                        .enumerate()
849                        .find(|(_, d)| d.id() == decl.id())
850                        .unwrap();
851                    *slot = decl;
852                    self.declarations_importance.set(idx, important);
853                }
854            }
855            return;
856        }
857
858        self.declarations.reserve(updates.new_count);
859        if updates.any_removal {
860            // Prepare for removal and fixing update positions.
861            struct UpdateOrRemoval<'a> {
862                item: &'a mut DeclarationUpdate,
863                pos: usize,
864                remove: bool,
865            }
866            let mut updates_and_removals: SubpropertiesVec<UpdateOrRemoval> = updates
867                .updates
868                .iter_mut()
869                .filter_map(|item| {
870                    let (pos, remove) = match *item {
871                        DeclarationUpdate::UpdateInPlace { pos } => (pos, false),
872                        DeclarationUpdate::AppendAndRemove { pos } => (pos, true),
873                        _ => return None,
874                    };
875                    Some(UpdateOrRemoval { item, pos, remove })
876                })
877                .collect();
878            // Execute removals. It's important to do it in reverse index order,
879            // so that removing doesn't invalidate following positions.
880            updates_and_removals.sort_unstable_by_key(|update| update.pos);
881            updates_and_removals
882                .iter()
883                .rev()
884                .filter(|update| update.remove)
885                .for_each(|update| {
886                    self.declarations.remove(update.pos);
887                    self.declarations_importance.remove(update.pos);
888                });
889            // Fixup pos field for updates.
890            let mut removed_count = 0;
891            for update in updates_and_removals.iter_mut() {
892                if update.remove {
893                    removed_count += 1;
894                    continue;
895                }
896                debug_assert_eq!(
897                    *update.item,
898                    DeclarationUpdate::UpdateInPlace { pos: update.pos }
899                );
900                *update.item = DeclarationUpdate::UpdateInPlace {
901                    pos: update.pos - removed_count,
902                };
903            }
904        }
905        // Execute updates and appends.
906        for (decl, update) in drain.declarations.zip_eq(updates.updates.iter()) {
907            match *update {
908                DeclarationUpdate::None => {},
909                DeclarationUpdate::Append | DeclarationUpdate::AppendAndRemove { .. } => {
910                    self.property_ids.insert(decl.id());
911                    self.declarations.push(decl);
912                    self.declarations_importance.push(important);
913                },
914                DeclarationUpdate::UpdateInPlace { pos } => {
915                    self.declarations[pos] = decl;
916                    self.declarations_importance.set(pos, important);
917                },
918            }
919        }
920        updates.updates.clear();
921    }
922
923    /// Returns the first declaration that would be removed by removing
924    /// `property`.
925    #[inline]
926    pub fn first_declaration_to_remove(&self, property: &PropertyId) -> Option<usize> {
927        if let Err(longhand_or_custom) = property.as_shorthand() {
928            if !self.contains(longhand_or_custom) {
929                return None;
930            }
931        }
932
933        self.declarations
934            .iter()
935            .position(|declaration| declaration.id().is_or_is_longhand_of(property))
936    }
937
938    /// Removes a given declaration at a given index.
939    #[inline]
940    fn remove_declaration_at(&mut self, i: usize) {
941        self.property_ids.remove(self.declarations[i].id());
942        self.declarations_importance.remove(i);
943        self.declarations.remove(i);
944    }
945
946    /// Clears all the declarations from this block.
947    #[inline]
948    pub fn clear(&mut self) {
949        self.declarations_importance.clear();
950        self.declarations.clear();
951        self.property_ids.clear();
952    }
953
954    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty>
955    ///
956    /// `first_declaration` needs to be the result of
957    /// `first_declaration_to_remove`.
958    #[inline]
959    pub fn remove_property(&mut self, property: &PropertyId, first_declaration: usize) {
960        debug_assert_eq!(
961            Some(first_declaration),
962            self.first_declaration_to_remove(property)
963        );
964        debug_assert!(self.declarations[first_declaration]
965            .id()
966            .is_or_is_longhand_of(property));
967
968        self.remove_declaration_at(first_declaration);
969
970        let shorthand = match property.as_shorthand() {
971            Ok(s) => s,
972            Err(_longhand_or_custom) => return,
973        };
974
975        let mut i = first_declaration;
976        let mut len = self.len();
977        while i < len {
978            if !self.declarations[i].id().is_longhand_of(shorthand) {
979                i += 1;
980                continue;
981            }
982
983            self.remove_declaration_at(i);
984            len -= 1;
985        }
986    }
987
988    /// Take a declaration block known to contain a single property and serialize it.
989    pub fn single_value_to_css(
990        &self,
991        property: &PropertyId,
992        dest: &mut CssStringWriter,
993        computed_values: Option<&ComputedValues>,
994        stylist: &Stylist,
995    ) -> fmt::Result {
996        if let Ok(shorthand) = property.as_shorthand() {
997            return self.shorthand_to_css(shorthand, dest);
998        }
999
1000        // FIXME(emilio): Should this assert, or assert that the declaration is
1001        // the property we expect?
1002        let declaration = match self.declarations.first() {
1003            Some(d) => d,
1004            None => return Err(fmt::Error),
1005        };
1006
1007        let mut rule_cache_conditions = RuleCacheConditions::default();
1008        let mut tree_counting_caches = TreeCountingCaches::default();
1009        let mut context = Context::new(
1010            StyleBuilder::new(
1011                stylist.device(),
1012                Some(stylist),
1013                computed_values,
1014                None,
1015                None,
1016                false,
1017            ),
1018            stylist.quirks_mode(),
1019            &mut rule_cache_conditions,
1020            ContainerSizeQuery::none(),
1021            RuleCascadeFlags::empty(),
1022            &DummyElementContext {},
1023            &mut tree_counting_caches,
1024        );
1025
1026        if let Some(cv) = computed_values {
1027            context.builder.substitution_functions.custom_properties =
1028                cv.custom_properties().clone();
1029        };
1030
1031        match (declaration, computed_values) {
1032            // If we have a longhand declaration with variables, those variables
1033            // will be stored as unparsed values.
1034            //
1035            // As a temporary measure to produce sensible results in Gecko's
1036            // getKeyframes() implementation for CSS animations, if
1037            // |computed_values| is supplied, we use it to expand such variable
1038            // declarations. This will be fixed properly in Gecko bug 1391537.
1039            (PropertyDeclaration::WithVariables(declaration), Some(_)) => declaration
1040                .value
1041                .substitute_variables(
1042                    declaration.id,
1043                    &context.builder.substitution_functions,
1044                    stylist,
1045                    &context,
1046                    &mut Default::default(),
1047                    &mut AttributeTracker::new_dummy(),
1048                )
1049                .to_css(dest),
1050            (d, _) => d.to_css(dest),
1051        }
1052    }
1053
1054    /// Convert AnimationValueMap to PropertyDeclarationBlock.
1055    pub fn from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self {
1056        let len = animation_value_map.len();
1057        let mut declarations = ThinVec::with_capacity(len);
1058        let mut property_ids = PropertyDeclarationIdSet::default();
1059
1060        for (property, animation_value) in animation_value_map.iter() {
1061            property_ids.insert(property.as_borrowed());
1062            declarations.push(animation_value.uncompute());
1063        }
1064
1065        PropertyDeclarationBlock {
1066            declarations,
1067            property_ids,
1068            declarations_importance: SmallBitVec::from_elem(len, false),
1069            immutable: AtomicBool::new(false),
1070        }
1071    }
1072
1073    /// Returns true if the declaration block has a CSSWideKeyword for the given
1074    /// property.
1075    pub fn has_css_wide_keyword(&self, property: &PropertyId) -> bool {
1076        if let Err(longhand_or_custom) = property.as_shorthand() {
1077            if !self.property_ids.contains(longhand_or_custom) {
1078                return false;
1079            }
1080        }
1081        self.declarations.iter().any(|decl| {
1082            decl.id().is_or_is_longhand_of(property) && decl.get_css_wide_keyword().is_some()
1083        })
1084    }
1085
1086    /// Like the method on ToCss, but without the type parameter to avoid
1087    /// accidentally monomorphizing this large function multiple times for
1088    /// different writers.
1089    ///
1090    /// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
1091    pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
1092        let mut is_first_serialization = true; // trailing serializations should have a prepended space
1093
1094        // Step 1 -> dest = result list
1095
1096        // Step 2
1097        //
1098        // NOTE(emilio): We reuse this set for both longhands and shorthands
1099        // with subtly different meaning. For longhands, only longhands that
1100        // have actually been serialized (either by themselves, or as part of a
1101        // shorthand) appear here. For shorthands, all the shorthands that we've
1102        // attempted to serialize appear here.
1103        let mut already_serialized = NonCustomPropertyIdSet::new();
1104
1105        // Step 3
1106        'declaration_loop: for (declaration, importance) in self.declaration_importance_iter() {
1107            // Step 3.1
1108            let property = declaration.id();
1109            let longhand_id = match property {
1110                PropertyDeclarationId::Longhand(id) => id,
1111                PropertyDeclarationId::Custom(..) => {
1112                    // Given the invariants that there are no duplicate
1113                    // properties in a declaration block, and that custom
1114                    // properties can't be part of a shorthand, we can just care
1115                    // about them here.
1116                    append_serialization(
1117                        dest,
1118                        &property,
1119                        AppendableValue::Declaration(declaration),
1120                        importance,
1121                        &mut is_first_serialization,
1122                    )?;
1123                    continue;
1124                },
1125            };
1126
1127            // Step 3.2
1128            if already_serialized.contains(longhand_id.into()) {
1129                continue;
1130            }
1131
1132            // Steps 3.3 & 3.4
1133            for shorthand in longhand_id.shorthands() {
1134                // We already attempted to serialize this shorthand before.
1135                if already_serialized.contains(shorthand.into()) {
1136                    continue;
1137                }
1138                already_serialized.insert(shorthand.into());
1139
1140                if shorthand.is_legacy_shorthand()
1141                    && !(shorthand.allows_disabled_subproperties()
1142                        && !NonCustomPropertyId::from(longhand_id).enabled_for_all_content())
1143                {
1144                    // TODO(Bug 1540681): Remove when shipping line-clamp.
1145                    continue;
1146                }
1147
1148                // Step 3.3.1:
1149                //     Let longhands be an array consisting of all CSS
1150                //     declarations in declaration block’s declarations that
1151                //     that are not in already serialized and have a property
1152                //     name that maps to one of the shorthand properties in
1153                //     shorthands.
1154                let longhands = {
1155                    // TODO(emilio): This could just index in an array if we
1156                    // remove pref-controlled longhands.
1157                    let mut ids = LonghandIdSet::new();
1158                    for longhand in shorthand.longhands() {
1159                        ids.insert(longhand);
1160                    }
1161                    ids
1162                };
1163
1164                // Step 3.4.2
1165                //     If all properties that map to shorthand are not present
1166                //     in longhands, continue with the steps labeled shorthand
1167                //     loop.
1168                if !self.property_ids.contains_all_longhands(&longhands) {
1169                    continue;
1170                }
1171
1172                // Step 3.4.3:
1173                //     Let current longhands be an empty array.
1174                let mut current_longhands = SmallVec::<[&_; 10]>::new();
1175                let mut logical_groups = LogicalGroupSet::new();
1176                let mut saw_one = false;
1177                let mut logical_mismatch = false;
1178                let mut seen = LonghandIdSet::new();
1179                let mut important_count = 0;
1180
1181                // Step 3.4.4:
1182                //    Append all CSS declarations in longhands that have a
1183                //    property name that maps to shorthand to current longhands.
1184                for (declaration, importance) in self.declaration_importance_iter() {
1185                    let longhand = match declaration.id() {
1186                        PropertyDeclarationId::Longhand(id) => id,
1187                        PropertyDeclarationId::Custom(..) => continue,
1188                    };
1189
1190                    if longhands.contains(longhand) {
1191                        saw_one = true;
1192                        if importance.important() {
1193                            important_count += 1;
1194                        }
1195                        current_longhands.push(declaration);
1196                        if shorthand != ShorthandId::All {
1197                            // All is special because it contains both physical
1198                            // and logical longhands.
1199                            if let Some(g) = longhand.logical_group() {
1200                                logical_groups.insert(g);
1201                            }
1202                            seen.insert(longhand);
1203                            if seen == longhands {
1204                                break;
1205                            }
1206                        }
1207                    } else if saw_one {
1208                        if let Some(g) = longhand.logical_group() {
1209                            if logical_groups.contains(g) {
1210                                logical_mismatch = true;
1211                                break;
1212                            }
1213                        }
1214                    }
1215                }
1216
1217                // 3.4.5:
1218                //     If there is one or more CSS declarations in current
1219                //     longhands have their important flag set and one or more
1220                //     with it unset, continue with the steps labeled shorthand
1221                //     loop.
1222                let is_important = important_count > 0;
1223                if is_important && important_count != current_longhands.len() {
1224                    continue;
1225                }
1226
1227                // 3.4.6:
1228                //    If there’s any declaration in declaration block in between
1229                //    the first and the last longhand in current longhands which
1230                //    belongs to the same logical property group, but has a
1231                //    different mapping logic as any of the longhands in current
1232                //    longhands, and is not in current longhands, continue with
1233                //    the steps labeled shorthand loop.
1234                if logical_mismatch {
1235                    continue;
1236                }
1237
1238                let importance = if is_important {
1239                    Importance::Important
1240                } else {
1241                    Importance::Normal
1242                };
1243
1244                // 3.4.7:
1245                //    Let value be the result of invoking serialize a CSS value
1246                //    of current longhands.
1247                let appendable_value =
1248                    match shorthand.get_shorthand_appendable_value(&current_longhands) {
1249                        None => continue,
1250                        Some(appendable_value) => appendable_value,
1251                    };
1252
1253                // We avoid re-serializing if we're already an
1254                // AppendableValue::Css.
1255                let mut v = CssString::new();
1256                let value = match appendable_value {
1257                    AppendableValue::Css(css) => {
1258                        debug_assert!(!css.is_empty());
1259                        appendable_value
1260                    },
1261                    other => {
1262                        append_declaration_value(&mut v, other)?;
1263
1264                        // 3.4.8:
1265                        //     If value is the empty string, continue with the
1266                        //     steps labeled shorthand loop.
1267                        if v.is_empty() {
1268                            continue;
1269                        }
1270
1271                        AppendableValue::Css({
1272                            // Safety: serialization only generates valid utf-8.
1273                            #[cfg(feature = "gecko")]
1274                            unsafe {
1275                                v.as_str_unchecked()
1276                            }
1277                            #[cfg(feature = "servo")]
1278                            &v
1279                        })
1280                    },
1281                };
1282
1283                // 3.4.9:
1284                //     Let serialized declaration be the result of invoking
1285                //     serialize a CSS declaration with property name shorthand,
1286                //     value value, and the important flag set if the CSS
1287                //     declarations in current longhands have their important
1288                //     flag set.
1289                //
1290                // 3.4.10:
1291                //     Append serialized declaration to list.
1292                append_serialization(
1293                    dest,
1294                    &shorthand,
1295                    value,
1296                    importance,
1297                    &mut is_first_serialization,
1298                )?;
1299
1300                // 3.4.11:
1301                //     Append the property names of all items of current
1302                //     longhands to already serialized.
1303                for current_longhand in &current_longhands {
1304                    let longhand_id = match current_longhand.id() {
1305                        PropertyDeclarationId::Longhand(id) => id,
1306                        PropertyDeclarationId::Custom(..) => unreachable!(),
1307                    };
1308
1309                    // Substep 9
1310                    already_serialized.insert(longhand_id.into());
1311                }
1312
1313                // 3.4.12:
1314                //     Continue with the steps labeled declaration loop.
1315                continue 'declaration_loop;
1316            }
1317
1318            // Steps 3.5, 3.6 & 3.7:
1319            //     Let value be the result of invoking serialize a CSS value of
1320            //     declaration.
1321            //
1322            //     Let serialized declaration be the result of invoking
1323            //     serialize a CSS declaration with property name property,
1324            //     value value, and the important flag set if declaration has
1325            //     its important flag set.
1326            //
1327            //     Append serialized declaration to list.
1328            append_serialization(
1329                dest,
1330                &property,
1331                AppendableValue::Declaration(declaration),
1332                importance,
1333                &mut is_first_serialization,
1334            )?;
1335
1336            // Step 3.8:
1337            //     Append property to already serialized.
1338            already_serialized.insert(longhand_id.into());
1339        }
1340
1341        // Step 4
1342        Ok(())
1343    }
1344}
1345
1346/// A convenient enum to represent different kinds of stuff that can represent a
1347/// _value_ in the serialization of a property declaration.
1348pub enum AppendableValue<'a, 'b: 'a> {
1349    /// A given declaration, of which we'll serialize just the value.
1350    Declaration(&'a PropertyDeclaration),
1351    /// A set of declarations for a given shorthand.
1352    ///
1353    /// FIXME: This needs more docs, where are the shorthands expanded? We print
1354    /// the property name before-hand, don't we?
1355    DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]),
1356    /// A raw CSS string, coming for example from a property with CSS variables,
1357    /// or when storing a serialized shorthand value before appending directly.
1358    Css(&'a str),
1359}
1360
1361/// Potentially appends whitespace after the first (property: value;) pair.
1362fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result
1363where
1364    W: Write,
1365{
1366    if !*is_first_serialization {
1367        dest.write_char(' ')
1368    } else {
1369        *is_first_serialization = false;
1370        Ok(())
1371    }
1372}
1373
1374/// Append a given kind of appendable value to a serialization.
1375pub fn append_declaration_value<'a, 'b: 'a>(
1376    dest: &mut CssStringWriter,
1377    appendable_value: AppendableValue<'a, 'b>,
1378) -> fmt::Result {
1379    match appendable_value {
1380        AppendableValue::Css(css) => dest.write_str(css),
1381        AppendableValue::Declaration(decl) => decl.to_css(dest),
1382        AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
1383            shorthand.longhands_to_css(decls, dest)
1384        },
1385    }
1386}
1387
1388/// Append a given property and value pair to a serialization.
1389pub fn append_serialization<'a, 'b: 'a, N>(
1390    dest: &mut CssStringWriter,
1391    property_name: &N,
1392    appendable_value: AppendableValue<'a, 'b>,
1393    importance: Importance,
1394    is_first_serialization: &mut bool,
1395) -> fmt::Result
1396where
1397    N: ToCss,
1398{
1399    handle_first_serialization(dest, is_first_serialization)?;
1400
1401    property_name.to_css(&mut CssWriter::new(dest))?;
1402    dest.write_str(": ")?;
1403
1404    append_declaration_value(dest, appendable_value)?;
1405
1406    if importance.important() {
1407        dest.write_str(" !important")?;
1408    }
1409
1410    dest.write_char(';')
1411}
1412
1413/// A helper to parse the style attribute of an element, in order for this to be
1414/// shared between Servo and Gecko.
1415///
1416/// Inline because we call this cross-crate.
1417#[inline]
1418pub fn parse_style_attribute(
1419    input: &str,
1420    url_data: &UrlExtraData,
1421    error_reporter: Option<&dyn ParseErrorReporter>,
1422    quirks_mode: QuirksMode,
1423    rule_type: CssRuleType,
1424) -> PropertyDeclarationBlock {
1425    let context = ParserContext::new(
1426        Origin::Author,
1427        url_data,
1428        Some(rule_type),
1429        ParsingMode::DEFAULT,
1430        quirks_mode,
1431        /* namespaces = */ Default::default(),
1432        error_reporter,
1433        None,
1434        /* attr_taint */ Default::default(),
1435    );
1436
1437    parse_property_declaration_list(&context, &mut Parser::new(input), &[])
1438}
1439
1440/// Parse a given property declaration. Can result in multiple
1441/// `PropertyDeclaration`s when expanding a shorthand, for example.
1442///
1443/// This does not attempt to parse !important at all.
1444#[inline]
1445pub fn parse_one_declaration_into(
1446    declarations: &mut SourcePropertyDeclaration,
1447    id: PropertyId,
1448    input: &str,
1449    origin: Origin,
1450    url_data: &UrlExtraData,
1451    error_reporter: Option<&dyn ParseErrorReporter>,
1452    parsing_mode: ParsingMode,
1453    quirks_mode: QuirksMode,
1454    rule_type: CssRuleType,
1455) -> Result<(), ()> {
1456    let context = ParserContext::new(
1457        origin,
1458        url_data,
1459        Some(rule_type),
1460        parsing_mode,
1461        quirks_mode,
1462        /* namespaces = */ Default::default(),
1463        error_reporter,
1464        None,
1465        /* attr_taint */ Default::default(),
1466    );
1467
1468    let property_id_for_error_reporting = if context.error_reporting_enabled() {
1469        Some(id.clone())
1470    } else {
1471        None
1472    };
1473
1474    let mut parser = Parser::new(input);
1475    let start_position = parser.position();
1476    let start_location = parser.current_source_location();
1477    parser
1478        .parse_entirely(|parser| {
1479            PropertyDeclaration::parse_into(declarations, id, &context, parser)
1480        })
1481        .map_err(|err| {
1482            if context.error_reporting_enabled() {
1483                report_one_css_error(
1484                    &context,
1485                    None,
1486                    &[],
1487                    err,
1488                    parser.slice_from(start_position),
1489                    start_location,
1490                    property_id_for_error_reporting,
1491                )
1492            }
1493        })
1494}
1495
1496/// A struct to parse property declarations.
1497struct PropertyDeclarationParser<'a, 'b: 'a, 'i> {
1498    context: &'a ParserContext<'b>,
1499    state: &'a mut DeclarationParserState<'i>,
1500}
1501
1502/// The state needed to parse a declaration block.
1503///
1504/// It stores declarations in output_block.
1505#[derive(Default)]
1506pub struct DeclarationParserState<'i> {
1507    /// The output block where results are stored.
1508    output_block: PropertyDeclarationBlock,
1509    /// Declarations from the last declaration parsed. (note that a shorthand might expand to
1510    /// multiple declarations).
1511    declarations: SourcePropertyDeclaration,
1512    /// The importance from the last declaration parsed.
1513    importance: Importance,
1514    /// A list of errors that have happened so far. Not all of them might be reported.
1515    errors: SmallParseErrorVec<'i>,
1516    /// The start of the first declaration
1517    first_declaration_start: SourceLocation,
1518    /// The last parsed property id, if any.
1519    last_parsed_property_id: Option<PropertyId>,
1520}
1521
1522impl<'i> DeclarationParserState<'i> {
1523    /// Getter for first_declaration_start.
1524    pub fn first_declaration_start(&self) -> SourceLocation {
1525        self.first_declaration_start
1526    }
1527
1528    /// Returns whether any parsed declarations have been parsed so far.
1529    pub fn has_parsed_declarations(&self) -> bool {
1530        !self.output_block.is_empty()
1531    }
1532
1533    /// Takes the parsed declarations.
1534    pub fn take_declarations(&mut self) -> PropertyDeclarationBlock {
1535        std::mem::take(&mut self.output_block)
1536    }
1537
1538    /// Parse a single declaration value.
1539    pub fn parse_value(
1540        &mut self,
1541        context: &ParserContext,
1542        name: CowRcStr<'i>,
1543        input: &mut Parser<'i>,
1544        declaration_start: &ParserState,
1545    ) -> Result<(), ParseError> {
1546        let id = match PropertyId::parse(&name, context) {
1547            Ok(id) => id,
1548            Err(..) => {
1549                return Err(ParseError::custom(StyleParseErrorKind::UnknownProperty));
1550            },
1551        };
1552        if context.error_reporting_enabled() {
1553            self.last_parsed_property_id = Some(id.clone());
1554        }
1555        input.parse_until_before(Delimiter::Bang, |input| {
1556            PropertyDeclaration::parse_into(&mut self.declarations, id, context, input)
1557        })?;
1558        self.importance = match input.try_parse(parse_important) {
1559            Ok(()) => {
1560                if !context.allows_important_declarations() {
1561                    return Err(ParseError::custom(
1562                        StyleParseErrorKind::UnexpectedImportantDeclaration,
1563                    ));
1564                }
1565                Importance::Important
1566            },
1567            Err(_) => Importance::Normal,
1568        };
1569        // In case there is still unparsed text in the declaration, we should roll back.
1570        input.expect_exhausted()?;
1571        let has_parsed_declarations = self.has_parsed_declarations();
1572        self.output_block
1573            .extend(self.declarations.drain(), self.importance);
1574        // We've successfully parsed a declaration, so forget about
1575        // `last_parsed_property_id`. It'd be wrong to associate any
1576        // following error with this property.
1577        self.last_parsed_property_id = None;
1578
1579        if !has_parsed_declarations {
1580            self.first_declaration_start = declaration_start.source_location();
1581        }
1582
1583        Ok(())
1584    }
1585
1586    /// Reports any CSS errors that have ocurred if needed.
1587    #[inline]
1588    pub fn report_errors_if_needed(
1589        &mut self,
1590        context: &ParserContext,
1591        selectors: &[SelectorList<SelectorImpl>],
1592    ) {
1593        if self.errors.is_empty() {
1594            return;
1595        }
1596        self.do_report_css_errors(context, selectors);
1597    }
1598
1599    #[cold]
1600    fn do_report_css_errors(
1601        &mut self,
1602        context: &ParserContext,
1603        selectors: &[SelectorList<SelectorImpl>],
1604    ) {
1605        for (error, slice, location, property) in self.errors.drain(..) {
1606            report_one_css_error(
1607                context,
1608                Some(&self.output_block),
1609                selectors,
1610                error,
1611                slice,
1612                location,
1613                property,
1614            )
1615        }
1616    }
1617
1618    /// Resets the declaration parser state, and reports the error if needed.
1619    #[inline]
1620    pub fn did_error(
1621        &mut self,
1622        context: &ParserContext,
1623        error: ParseError,
1624        slice: &'i str,
1625        location: SourceLocation,
1626    ) {
1627        self.declarations.clear();
1628        if !context.error_reporting_enabled() {
1629            return;
1630        }
1631        let property = self.last_parsed_property_id.take();
1632        self.errors.push((error, slice, location, property));
1633    }
1634}
1635
1636/// Default methods reject all at rules.
1637impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
1638    type Prelude = ();
1639    type AtRule = ();
1640    type Error = StyleParseErrorKind;
1641}
1642
1643/// Default methods reject all rules.
1644impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
1645    type Prelude = ();
1646    type QualifiedRule = ();
1647    type Error = StyleParseErrorKind;
1648}
1649
1650/// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.
1651fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
1652    (name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_")
1653}
1654
1655impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
1656    type Declaration = ();
1657    type Error = StyleParseErrorKind;
1658
1659    fn parse_value(
1660        &mut self,
1661        name: CowRcStr<'i>,
1662        input: &mut Parser<'i>,
1663        declaration_start: &ParserState,
1664    ) -> Result<(), ParseError> {
1665        self.state
1666            .parse_value(self.context, name, input, declaration_start)
1667    }
1668}
1669
1670impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind>
1671    for PropertyDeclarationParser<'a, 'b, 'i>
1672{
1673    fn parse_declarations(&self) -> bool {
1674        true
1675    }
1676    // TODO(emilio): Nesting.
1677    fn parse_qualified(&self) -> bool {
1678        false
1679    }
1680}
1681
1682type SmallParseErrorVec<'i> =
1683    SmallVec<[(ParseError, &'i str, SourceLocation, Option<PropertyId>); 2]>;
1684
1685fn alias_of_known_property(name: &str) -> Option<PropertyId> {
1686    let mut prefixed = String::with_capacity(name.len() + 5);
1687    prefixed.push_str("-moz-");
1688    prefixed.push_str(name);
1689    PropertyId::parse_enabled_for_all_content(&prefixed).ok()
1690}
1691
1692#[cold]
1693fn report_one_css_error(
1694    context: &ParserContext,
1695    block: Option<&PropertyDeclarationBlock>,
1696    selectors: &[SelectorList<SelectorImpl>],
1697    mut error: ParseError,
1698    slice: &str,
1699    location: SourceLocation,
1700    property: Option<PropertyId>,
1701) {
1702    debug_assert!(context.error_reporting_enabled());
1703
1704    fn all_properties_in_block(block: &PropertyDeclarationBlock, property: &PropertyId) -> bool {
1705        match property.as_shorthand() {
1706            Ok(id) => id
1707                .longhands()
1708                .all(|longhand| block.contains(PropertyDeclarationId::Longhand(longhand))),
1709            Err(longhand_or_custom) => block.contains(longhand_or_custom),
1710        }
1711    }
1712
1713    let mut error_string = Cow::Borrowed(slice);
1714    if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty) = error.kind {
1715        // The error no longer carries the property name, but `slice` is the
1716        // declaration text, i.e. `<name>: <value>`.
1717        let name = slice.split(':').next().unwrap_or("").trim();
1718        if is_non_mozilla_vendor_identifier(name) {
1719            // If the unrecognized property looks like a vendor-specific property,
1720            // silently ignore it instead of polluting the error output.
1721            return;
1722        }
1723        if let Some(alias) = alias_of_known_property(name) {
1724            // This is an unknown property, but its -moz-* version is known.
1725            // We don't want to report error if the -moz-* version is already
1726            // specified.
1727            if let Some(block) = block {
1728                if all_properties_in_block(block, &alias) {
1729                    return;
1730                }
1731            }
1732        }
1733        if !name.is_empty() {
1734            // We don't care about the whole declaration, just the property name
1735            error_string = Cow::Borrowed(name);
1736        }
1737    }
1738
1739    if let Some(ref property) = property {
1740        if let Some(block) = block {
1741            if all_properties_in_block(block, property) {
1742                return;
1743            }
1744        }
1745        // Was able to parse property ID - Either an invalid value, or is constrained
1746        // by the rule block it's in to be invalid. In the former case, we need to unwrap
1747        // the error to be more specific.
1748        if !matches!(
1749            error.kind,
1750            ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedImportantDeclaration)
1751        ) {
1752            error = ParseError::custom(StyleParseErrorKind::OtherInvalidValue);
1753        }
1754        if !slice.contains(':') {
1755            // For CSSOM we only have the value. If there's no `:`, prepend the property name.
1756            error_string = Cow::Owned(format!("{}: {slice}", property.to_css_string()));
1757        }
1758    }
1759
1760    let error =
1761        ContextualParseError::UnsupportedPropertyDeclaration(&error_string, error, selectors);
1762    context.log_css_error(location, error);
1763}
1764
1765/// Parse a list of property declarations and return a property declaration
1766/// block.
1767pub fn parse_property_declaration_list(
1768    context: &ParserContext,
1769    input: &mut Parser,
1770    selectors: &[SelectorList<SelectorImpl>],
1771) -> PropertyDeclarationBlock {
1772    let mut state = DeclarationParserState::default();
1773    let mut parser = PropertyDeclarationParser {
1774        context,
1775        state: &mut state,
1776    };
1777    let mut iter = RuleBodyParser::new(input, &mut parser);
1778    while let Some(declaration) = iter.next() {
1779        match declaration {
1780            Ok(()) => {},
1781            Err((error, slice, location)) => {
1782                iter.parser.state.did_error(context, error, slice, location)
1783            },
1784        }
1785    }
1786    parser.state.report_errors_if_needed(context, selectors);
1787    state.output_block
1788}