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/. */
5 //! A property declaration block.
10 use crate::context::QuirksMode;
11 use crate::custom_properties::CustomPropertiesBuilder;
12 use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
13 use crate::parser::ParserContext;
14 use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
15 use crate::selector_parser::SelectorImpl;
16 use crate::shared_lock::Locked;
17 use crate::str::{CssString, CssStringBorrow, CssStringWriter};
18 use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
19 use crate::values::computed::Context;
20 use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
21 use cssparser::{AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind, Parser};
22 use itertools::Itertools;
23 use selectors::SelectorList;
24 use smallbitvec::{self, SmallBitVec};
25 use smallvec::SmallVec;
26 use std::fmt::{self, Write};
27 use std::iter::{DoubleEndedIterator, Zip};
29 use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
31 /// The animation rules.
33 /// The first one is for Animation cascade level, and the second one is for
34 /// Transition cascade level.
35 pub struct AnimationRules(
36 pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
37 pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
41 /// Returns whether these animation rules represents an actual rule or not.
42 pub fn is_empty(&self) -> bool {
43 self.0.is_none() && self.1.is_none()
47 /// An enum describes how a declaration should update
48 /// the declaration block.
49 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
50 enum DeclarationUpdate {
51 /// The given declaration doesn't update anything.
53 /// The given declaration is new, and should be append directly.
55 /// The given declaration can be updated in-place at the given position.
56 UpdateInPlace { pos: usize },
57 /// The given declaration cannot be updated in-place, and an existing
58 /// one needs to be removed at the given position.
59 AppendAndRemove { pos: usize },
62 /// A struct describes how a declaration block should be updated by
63 /// a `SourcePropertyDeclaration`.
65 pub struct SourcePropertyDeclarationUpdate {
66 updates: SubpropertiesVec<DeclarationUpdate>,
71 /// A declaration [importance][importance].
73 /// [importance]: https://drafts.csswg.org/css-cascade/#importance
74 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
76 /// Indicates a declaration without `!important`.
79 /// Indicates a declaration with `!important`.
84 /// Return whether this is an important declaration.
85 pub fn important(self) -> bool {
87 Importance::Normal => false,
88 Importance::Important => true,
93 /// Overridden declarations are skipped.
94 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
95 #[derive(Clone, ToShmem)]
96 pub struct PropertyDeclarationBlock {
97 /// The group of declarations, along with their importance.
99 /// Only deduplicated declarations appear here.
100 declarations: Vec<PropertyDeclaration>,
102 /// The "important" flag for each declaration in `declarations`.
103 declarations_importance: SmallBitVec,
105 longhands: LonghandIdSet,
108 /// Iterator over `(PropertyDeclaration, Importance)` pairs.
109 pub struct DeclarationImportanceIterator<'a> {
110 iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,
113 impl<'a> Default for DeclarationImportanceIterator<'a> {
114 fn default() -> Self {
116 iter: [].iter().zip(smallbitvec::Iter::default()),
121 impl<'a> DeclarationImportanceIterator<'a> {
123 fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
124 DeclarationImportanceIterator {
125 iter: declarations.iter().zip(important.iter()),
130 impl<'a> Iterator for DeclarationImportanceIterator<'a> {
131 type Item = (&'a PropertyDeclaration, Importance);
134 fn next(&mut self) -> Option<Self::Item> {
135 self.iter.next().map(|(decl, important)| {
139 Importance::Important
148 fn size_hint(&self) -> (usize, Option<usize>) {
149 self.iter.size_hint()
153 impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> {
155 fn next_back(&mut self) -> Option<Self::Item> {
156 self.iter.next_back().map(|(decl, important)| {
160 Importance::Important
169 /// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
170 pub struct AnimationValueIterator<'a, 'cx, 'cx_a: 'cx> {
171 iter: DeclarationImportanceIterator<'a>,
172 context: &'cx mut Context<'cx_a>,
173 default_values: &'a ComputedValues,
174 /// Custom properties in a keyframe if exists.
175 extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
178 impl<'a, 'cx, 'cx_a: 'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
180 declarations: &'a PropertyDeclarationBlock,
181 context: &'cx mut Context<'cx_a>,
182 default_values: &'a ComputedValues,
183 extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
184 ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
185 AnimationValueIterator {
186 iter: declarations.declaration_importance_iter(),
189 extra_custom_properties,
194 impl<'a, 'cx, 'cx_a: 'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
195 type Item = AnimationValue;
197 fn next(&mut self) -> Option<Self::Item> {
199 let (decl, importance) = self.iter.next()?;
201 if importance.important() {
205 let animation = AnimationValue::from_declaration(
208 self.extra_custom_properties,
212 if let Some(anim) = animation {
219 impl fmt::Debug for PropertyDeclarationBlock {
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 self.declarations.fmt(f)
225 impl PropertyDeclarationBlock {
226 /// Returns the number of declarations in the block.
228 pub fn len(&self) -> usize {
229 self.declarations.len()
232 /// Create an empty block
234 pub fn new() -> Self {
235 PropertyDeclarationBlock {
236 declarations: Vec::new(),
237 declarations_importance: SmallBitVec::new(),
238 longhands: LonghandIdSet::new(),
242 /// Create a block with a single declaration
243 pub fn with_one(declaration: PropertyDeclaration, importance: Importance) -> Self {
244 let mut longhands = LonghandIdSet::new();
245 if let PropertyDeclarationId::Longhand(id) = declaration.id() {
246 longhands.insert(id);
248 PropertyDeclarationBlock {
249 declarations: vec![declaration],
250 declarations_importance: SmallBitVec::from_elem(1, importance.important()),
255 /// The declarations in this block
257 pub fn declarations(&self) -> &[PropertyDeclaration] {
261 /// The `important` flags for declarations in this block
263 pub fn declarations_importance(&self) -> &SmallBitVec {
264 &self.declarations_importance
267 /// Iterate over `(PropertyDeclaration, Importance)` pairs
269 pub fn declaration_importance_iter(&self) -> DeclarationImportanceIterator {
270 DeclarationImportanceIterator::new(&self.declarations, &self.declarations_importance)
273 /// Iterate over `PropertyDeclaration` for Importance::Normal
275 pub fn normal_declaration_iter<'a>(
277 ) -> impl DoubleEndedIterator<Item = &'a PropertyDeclaration> {
278 self.declaration_importance_iter()
279 .filter(|(_, importance)| !importance.important())
280 .map(|(declaration, _)| declaration)
283 /// Return an iterator of (AnimatableLonghand, AnimationValue).
285 pub fn to_animation_value_iter<'a, 'cx, 'cx_a: 'cx>(
287 context: &'cx mut Context<'cx_a>,
288 default_values: &'a ComputedValues,
289 extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
290 ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
291 AnimationValueIterator::new(self, context, default_values, extra_custom_properties)
294 /// Returns whether this block contains any declaration with `!important`.
296 /// This is based on the `declarations_importance` bit-vector,
297 /// which should be maintained whenever `declarations` is changed.
299 pub fn any_important(&self) -> bool {
300 !self.declarations_importance.all_false()
303 /// Returns whether this block contains any declaration without `!important`.
305 /// This is based on the `declarations_importance` bit-vector,
306 /// which should be maintained whenever `declarations` is changed.
308 pub fn any_normal(&self) -> bool {
309 !self.declarations_importance.all_true()
312 /// Returns whether this block contains a declaration of a given longhand.
314 pub fn contains(&self, id: LonghandId) -> bool {
315 self.longhands.contains(id)
318 /// Returns whether this block contains any reset longhand.
320 pub fn contains_any_reset(&self) -> bool {
321 self.longhands.contains_any_reset()
324 /// Get a declaration for a given property.
326 /// NOTE: This is linear time in the case of custom properties or in the
327 /// case the longhand is actually in the declaration block.
331 property: PropertyDeclarationId,
332 ) -> Option<(&PropertyDeclaration, Importance)> {
333 if let PropertyDeclarationId::Longhand(id) = property {
334 if !self.contains(id) {
339 self.declaration_importance_iter()
340 .find(|(declaration, _)| declaration.id() == property)
343 /// Get a declaration for a given property with the specified importance.
345 pub fn get_at_importance(
347 property: PropertyDeclarationId,
348 importance: Importance,
349 ) -> Option<&PropertyDeclaration> {
350 let (declaration, i) = self.get(property)?;
358 /// Tries to serialize a given shorthand from the declarations in this
360 pub fn shorthand_to_css(
362 shorthand: ShorthandId,
363 dest: &mut CssStringWriter,
366 // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
367 let mut list = SmallVec::<[&_; 10]>::new();
368 let mut important_count = 0;
371 for longhand in shorthand.longhands() {
373 let declaration = self.get(PropertyDeclarationId::Longhand(longhand));
375 // Step 1.2.2.2 & 1.2.2.3
377 Some((declaration, importance)) => {
378 list.push(declaration);
379 if importance.important() {
380 important_count += 1;
383 None => return Ok(()),
387 // If there is one or more longhand with important, and one or more
388 // without important, we don't serialize it as a shorthand.
389 if important_count > 0 && important_count != list.len() {
394 // We don't print !important when serializing individual properties,
395 // so we treat this as a normal-importance property
396 match shorthand.get_shorthand_appendable_value(list.iter().cloned()) {
397 Some(appendable_value) => append_declaration_value(dest, appendable_value),
398 None => return Ok(()),
402 /// Find the value of the given property in this block and serialize it
404 /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue>
405 pub fn property_value_to_css(
407 property: &PropertyId,
408 dest: &mut CssStringWriter,
410 // Step 1.1: done when parsing a string to PropertyId
413 let longhand_or_custom = match property.as_shorthand() {
414 Ok(shorthand) => return self.shorthand_to_css(shorthand, dest),
415 Err(longhand_or_custom) => longhand_or_custom,
418 if let Some((value, _importance)) = self.get(longhand_or_custom) {
427 /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority>
428 pub fn property_priority(&self, property: &PropertyId) -> Importance {
429 // Step 1: done when parsing a string to PropertyId
432 match property.as_shorthand() {
434 // Step 2.1 & 2.2 & 2.3
435 if shorthand.longhands().all(|l| {
436 self.get(PropertyDeclarationId::Longhand(l))
437 .map_or(false, |(_, importance)| importance.important())
439 Importance::Important
444 Err(longhand_or_custom) => {
446 self.get(longhand_or_custom)
447 .map_or(Importance::Normal, |(_, importance)| importance)
452 /// Returns whether the property is definitely new for this declaration
453 /// block. It returns true when the declaration is a non-custom longhand
454 /// and it doesn't exist in the block, and returns false otherwise.
456 fn is_definitely_new(&self, decl: &PropertyDeclaration) -> bool {
459 .map_or(false, |id| !self.longhands.contains(id))
462 /// Adds or overrides the declaration for a given property in this block.
464 /// See the documentation of `push` to see what impact `source` has when the
465 /// property is already there.
468 mut drain: SourcePropertyDeclarationDrain,
469 importance: Importance,
471 let all_shorthand_len = match drain.all_shorthand {
472 AllShorthand::NotSet => 0,
473 AllShorthand::CSSWideKeyword(_) | AllShorthand::WithVariables(_) => {
474 shorthands::ALL_SHORTHAND_MAX_LEN
477 let push_calls_count = drain.declarations.len() + all_shorthand_len;
479 // With deduplication the actual length increase may be less than this.
480 self.declarations.reserve(push_calls_count);
482 let mut changed = false;
483 for decl in &mut drain.declarations {
484 changed |= self.push(decl, importance);
489 .fold(changed, |changed, decl| {
490 changed | self.push(decl, importance)
494 /// Adds or overrides the declaration for a given property in this block.
496 /// Returns whether the declaration has changed.
498 /// This is only used for parsing and internal use.
499 pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) -> bool {
500 if !self.is_definitely_new(&declaration) {
501 let mut index_to_remove = None;
502 for (i, slot) in self.declarations.iter_mut().enumerate() {
503 if slot.id() != declaration.id() {
507 let important = self.declarations_importance[i];
509 // For declarations from parsing, non-important declarations
510 // shouldn't override existing important one.
511 if important && !importance.important() {
515 index_to_remove = Some(i);
519 if let Some(index) = index_to_remove {
520 self.declarations.remove(index);
521 self.declarations_importance.remove(index);
522 self.declarations.push(declaration);
523 self.declarations_importance.push(importance.important());
528 if let PropertyDeclarationId::Longhand(id) = declaration.id() {
529 self.longhands.insert(id);
531 self.declarations.push(declaration);
532 self.declarations_importance.push(importance.important());
536 /// Prepares updating this declaration block with the given
537 /// `SourcePropertyDeclaration` and importance, and returns whether
538 /// there is something to update.
539 pub fn prepare_for_update(
541 source_declarations: &SourcePropertyDeclaration,
542 importance: Importance,
543 updates: &mut SourcePropertyDeclarationUpdate,
545 debug_assert!(updates.updates.is_empty());
546 // Check whether we are updating for an all shorthand change.
547 if !matches!(source_declarations.all_shorthand, AllShorthand::NotSet) {
548 debug_assert!(source_declarations.declarations.is_empty());
549 return source_declarations
553 self.is_definitely_new(&decl) ||
557 .find(|&(_, ref d)| d.id() == decl.id())
558 .map_or(true, |(i, d)| {
559 let important = self.declarations_importance[i];
560 *d != decl || important != importance.important()
564 // Fill `updates` with update information.
565 let mut any_update = false;
566 let new_count = &mut updates.new_count;
567 let any_removal = &mut updates.any_removal;
568 let updates = &mut updates.updates;
574 if self.is_definitely_new(declaration) {
575 return DeclarationUpdate::Append;
577 let longhand_id = declaration.id().as_longhand();
578 if let Some(longhand_id) = longhand_id {
579 if let Some(logical_group) = longhand_id.logical_group() {
580 let mut needs_append = false;
581 for (pos, decl) in self.declarations.iter().enumerate().rev() {
582 let id = match decl.id().as_longhand() {
586 if id == longhand_id {
588 return DeclarationUpdate::AppendAndRemove { pos };
590 let important = self.declarations_importance[pos];
591 if decl == declaration && important == importance.important() {
592 return DeclarationUpdate::None;
594 return DeclarationUpdate::UpdateInPlace { pos };
597 id.logical_group() == Some(logical_group) &&
598 id.is_logical() != longhand_id.is_logical()
603 unreachable!("Longhand should be found in loop above");
609 .find(|&(_, ref decl)| decl.id() == declaration.id())
610 .map_or(DeclarationUpdate::Append, |(pos, decl)| {
611 let important = self.declarations_importance[pos];
612 if decl == declaration && important == importance.important() {
613 DeclarationUpdate::None
615 DeclarationUpdate::UpdateInPlace { pos }
620 if matches!(update, DeclarationUpdate::None) {
625 DeclarationUpdate::Append => {
628 DeclarationUpdate::AppendAndRemove { .. } => {
638 /// Update this declaration block with the given data.
641 drain: SourcePropertyDeclarationDrain,
642 importance: Importance,
643 updates: &mut SourcePropertyDeclarationUpdate,
645 let important = importance.important();
646 if !matches!(drain.all_shorthand, AllShorthand::NotSet) {
647 debug_assert!(updates.updates.is_empty());
648 for decl in drain.all_shorthand.declarations() {
649 if self.is_definitely_new(&decl) {
650 let longhand_id = decl.id().as_longhand().unwrap();
651 self.declarations.push(decl);
652 self.declarations_importance.push(important);
653 self.longhands.insert(longhand_id);
655 let (idx, slot) = self
659 .find(|&(_, ref d)| d.id() == decl.id())
662 self.declarations_importance.set(idx, important);
668 self.declarations.reserve(updates.new_count);
669 if updates.any_removal {
670 // Prepare for removal and fixing update positions.
671 struct UpdateOrRemoval<'a> {
672 item: &'a mut DeclarationUpdate,
676 let mut updates_and_removals: SubpropertiesVec<UpdateOrRemoval> = updates
680 let (pos, remove) = match *item {
681 DeclarationUpdate::UpdateInPlace { pos } => (pos, false),
682 DeclarationUpdate::AppendAndRemove { pos } => (pos, true),
685 Some(UpdateOrRemoval { item, pos, remove })
688 // Execute removals. It's important to do it in reverse index order,
689 // so that removing doesn't invalidate following positions.
690 updates_and_removals.sort_unstable_by_key(|update| update.pos);
694 .filter(|update| update.remove)
696 self.declarations.remove(update.pos);
697 self.declarations_importance.remove(update.pos);
699 // Fixup pos field for updates.
700 let mut removed_count = 0;
701 for update in updates_and_removals.iter_mut() {
708 DeclarationUpdate::UpdateInPlace { pos: update.pos }
710 *update.item = DeclarationUpdate::UpdateInPlace {
711 pos: update.pos - removed_count,
715 // Execute updates and appends.
716 for (decl, update) in drain.declarations.zip_eq(updates.updates.iter()) {
718 DeclarationUpdate::None => {},
719 DeclarationUpdate::Append | DeclarationUpdate::AppendAndRemove { .. } => {
720 if let Some(id) = decl.id().as_longhand() {
721 self.longhands.insert(id);
723 self.declarations.push(decl);
724 self.declarations_importance.push(important);
726 DeclarationUpdate::UpdateInPlace { pos } => {
727 self.declarations[pos] = decl;
728 self.declarations_importance.set(pos, important);
732 updates.updates.clear();
735 /// Returns the first declaration that would be removed by removing
738 pub fn first_declaration_to_remove(&self, property: &PropertyId) -> Option<usize> {
739 if let Some(id) = property.longhand_id() {
740 if !self.longhands.contains(id) {
747 .position(|declaration| declaration.id().is_or_is_longhand_of(property))
750 /// Removes a given declaration at a given index.
752 fn remove_declaration_at(&mut self, i: usize) {
754 let id = self.declarations[i].id();
755 if let PropertyDeclarationId::Longhand(id) = id {
756 self.longhands.remove(id);
758 self.declarations_importance.remove(i);
760 self.declarations.remove(i);
763 /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty>
765 /// `first_declaration` needs to be the result of
766 /// `first_declaration_to_remove`.
768 pub fn remove_property(&mut self, property: &PropertyId, first_declaration: usize) {
770 Some(first_declaration),
771 self.first_declaration_to_remove(property)
773 debug_assert!(self.declarations[first_declaration]
775 .is_or_is_longhand_of(property));
777 self.remove_declaration_at(first_declaration);
779 let shorthand = match property.as_shorthand() {
781 Err(_longhand_or_custom) => return,
784 let mut i = first_declaration;
785 let mut len = self.len();
787 if !self.declarations[i].id().is_longhand_of(shorthand) {
792 self.remove_declaration_at(i);
797 /// Take a declaration block known to contain a single property and serialize it.
798 pub fn single_value_to_css(
800 property: &PropertyId,
801 dest: &mut CssStringWriter,
802 computed_values: Option<&ComputedValues>,
803 custom_properties_block: Option<&PropertyDeclarationBlock>,
806 if let Ok(shorthand) = property.as_shorthand() {
807 return self.shorthand_to_css(shorthand, dest);
810 // FIXME(emilio): Should this assert, or assert that the declaration is
811 // the property we expect?
812 let declaration = match self.declarations.get(0) {
814 None => return Err(fmt::Error),
817 let custom_properties = if let Some(cv) = computed_values {
818 // If there are extra custom properties for this declaration block,
819 // factor them in too.
820 if let Some(block) = custom_properties_block {
821 // FIXME(emilio): This is not super-efficient here, and all this
822 // feels like a hack anyway...
823 block.cascade_custom_properties(cv.custom_properties(), device)
825 cv.custom_properties().cloned()
831 match (declaration, computed_values) {
832 // If we have a longhand declaration with variables, those variables
833 // will be stored as unparsed values.
835 // As a temporary measure to produce sensible results in Gecko's
836 // getKeyframes() implementation for CSS animations, if
837 // |computed_values| is supplied, we use it to expand such variable
838 // declarations. This will be fixed properly in Gecko bug 1391537.
839 (&PropertyDeclaration::WithVariables(ref declaration), Some(ref _computed_values)) => {
842 .substitute_variables(
844 custom_properties.as_ref(),
845 QuirksMode::NoQuirks,
850 (ref d, _) => d.to_css(dest),
854 /// Convert AnimationValueMap to PropertyDeclarationBlock.
855 pub fn from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self {
856 let len = animation_value_map.len();
857 let mut declarations = Vec::with_capacity(len);
858 let mut longhands = LonghandIdSet::new();
860 for (property, animation_value) in animation_value_map.iter() {
861 longhands.insert(*property);
862 declarations.push(animation_value.uncompute());
865 PropertyDeclarationBlock {
868 declarations_importance: SmallBitVec::from_elem(len, false),
872 /// Returns true if the declaration block has a CSSWideKeyword for the given
874 pub fn has_css_wide_keyword(&self, property: &PropertyId) -> bool {
875 if let Some(id) = property.longhand_id() {
876 if !self.longhands.contains(id) {
880 self.declarations.iter().any(|decl| {
881 decl.id().is_or_is_longhand_of(property) && decl.get_css_wide_keyword().is_some()
885 /// Returns a custom properties map which is the result of cascading custom
886 /// properties in this declaration block along with context's custom
888 pub fn cascade_custom_properties_with_context(
891 ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
892 self.cascade_custom_properties(
893 context.style().custom_properties(),
898 /// Returns a custom properties map which is the result of cascading custom
899 /// properties in this declaration block along with the given custom
901 fn cascade_custom_properties(
903 inherited_custom_properties: Option<&Arc<crate::custom_properties::CustomPropertiesMap>>,
905 ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
906 let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties, device);
908 for declaration in self.normal_declaration_iter() {
909 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
910 builder.cascade(declaration, Origin::Author);
917 /// Like the method on ToCss, but without the type parameter to avoid
918 /// accidentally monomorphizing this large function multiple times for
919 /// different writers.
921 /// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
922 pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
923 use std::iter::Cloned;
926 let mut is_first_serialization = true; // trailing serializations should have a prepended space
928 // Step 1 -> dest = result list
932 // NOTE(emilio): We reuse this set for both longhands and shorthands
933 // with subtly different meaning. For longhands, only longhands that
934 // have actually been serialized (either by themselves, or as part of a
935 // shorthand) appear here. For shorthands, all the shorthands that we've
936 // attempted to serialize appear here.
937 let mut already_serialized = NonCustomPropertyIdSet::new();
940 for (declaration, importance) in self.declaration_importance_iter() {
942 let property = declaration.id();
943 let longhand_id = match property {
944 PropertyDeclarationId::Longhand(id) => id,
945 PropertyDeclarationId::Custom(..) => {
946 // Given the invariants that there are no duplicate
947 // properties in a declaration block, and that custom
948 // properties can't be part of a shorthand, we can just care
950 append_serialization::<Cloned<slice::Iter<_>>, _>(
953 AppendableValue::Declaration(declaration),
955 &mut is_first_serialization,
962 if already_serialized.contains(longhand_id.into()) {
967 // Step 3.3.1 is done by checking already_serialized while
971 for shorthand in longhand_id.shorthands() {
972 // We already attempted to serialize this shorthand before.
973 if already_serialized.contains(shorthand.into()) {
976 already_serialized.insert(shorthand.into());
978 if shorthand.is_legacy_shorthand() {
983 let mut current_longhands = SmallVec::<[_; 10]>::new();
984 let mut important_count = 0;
985 let mut found_system = None;
987 let is_system_font = shorthand == ShorthandId::Font &&
988 self.declarations.iter().any(|l| match l.id() {
989 PropertyDeclarationId::Longhand(id) => {
990 if already_serialized.contains(id.into()) {
994 l.get_system().is_some()
996 PropertyDeclarationId::Custom(..) => {
997 debug_assert!(l.get_system().is_none());
1003 for (longhand, importance) in self.declaration_importance_iter() {
1004 if longhand.get_system().is_some() || longhand.is_default_line_height() {
1005 current_longhands.push(longhand);
1006 if found_system.is_none() {
1007 found_system = longhand.get_system();
1009 if importance.important() {
1010 important_count += 1;
1015 let mut contains_all_longhands = true;
1016 for longhand in shorthand.longhands() {
1017 match self.get(PropertyDeclarationId::Longhand(longhand)) {
1018 Some((declaration, importance)) => {
1019 current_longhands.push(declaration);
1020 if importance.important() {
1021 important_count += 1;
1025 contains_all_longhands = false;
1032 if !contains_all_longhands {
1038 let is_important = important_count > 0;
1039 if is_important && important_count != current_longhands.len() {
1042 let importance = if is_important {
1043 Importance::Important
1048 // Substep 5 - Let value be the result of invoking serialize
1049 // a CSS value of current longhands.
1050 let appendable_value = match shorthand
1051 .get_shorthand_appendable_value(current_longhands.iter().cloned())
1054 Some(appendable_value) => appendable_value,
1057 // We avoid re-serializing if we're already an
1058 // AppendableValue::Css.
1059 let mut v = CssString::new();
1060 let value = match (appendable_value, found_system) {
1062 AppendableValue::Css {
1068 debug_assert!(!css.is_empty());
1069 AppendableValue::Css {
1071 with_variables: with_variables,
1074 #[cfg(feature = "gecko")]
1076 sys.to_css(&mut CssWriter::new(&mut v))?;
1077 AppendableValue::Css {
1078 css: CssStringBorrow::from(&v),
1079 with_variables: false,
1083 append_declaration_value(&mut v, other)?;
1090 AppendableValue::Css {
1091 css: CssStringBorrow::from(&v),
1092 with_variables: false,
1098 append_serialization::<Cloned<slice::Iter<_>>, _>(
1103 &mut is_first_serialization,
1106 for current_longhand in ¤t_longhands {
1107 let longhand_id = match current_longhand.id() {
1108 PropertyDeclarationId::Longhand(id) => id,
1109 PropertyDeclarationId::Custom(..) => unreachable!(),
1113 already_serialized.insert(longhand_id.into());
1116 // FIXME(https://github.com/w3c/csswg-drafts/issues/1774)
1117 // The specification does not include an instruction to abort
1118 // the shorthand loop at this point, but doing so both matches
1119 // Gecko and makes sense since shorthands are checked in
1125 if already_serialized.contains(longhand_id.into()) {
1129 // Steps 3.3.5, 3.3.6 & 3.3.7
1130 // Need to specify an iterator type here even though it’s unused to work around
1131 // "error: unable to infer enough type information about `_`;
1132 // type annotations or generic parameter binding required [E0282]"
1133 // Use the same type as earlier call to reuse generated code.
1134 append_serialization::<Cloned<slice::Iter<_>>, _>(
1137 AppendableValue::Declaration(declaration),
1139 &mut is_first_serialization,
1143 already_serialized.insert(longhand_id.into());
1151 /// A convenient enum to represent different kinds of stuff that can represent a
1152 /// _value_ in the serialization of a property declaration.
1153 pub enum AppendableValue<'a, I>
1155 I: Iterator<Item = &'a PropertyDeclaration>,
1157 /// A given declaration, of which we'll serialize just the value.
1158 Declaration(&'a PropertyDeclaration),
1159 /// A set of declarations for a given shorthand.
1161 /// FIXME: This needs more docs, where are the shorthands expanded? We print
1162 /// the property name before-hand, don't we?
1163 DeclarationsForShorthand(ShorthandId, I),
1164 /// A raw CSS string, coming for example from a property with CSS variables,
1165 /// or when storing a serialized shorthand value before appending directly.
1167 /// The raw CSS string.
1168 css: CssStringBorrow<'a>,
1169 /// Whether the original serialization contained variables or not.
1170 with_variables: bool,
1174 /// Potentially appends whitespace after the first (property: value;) pair.
1175 fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result
1179 if !*is_first_serialization {
1182 *is_first_serialization = false;
1187 /// Append a given kind of appendable value to a serialization.
1188 pub fn append_declaration_value<'a, I>(
1189 dest: &mut CssStringWriter,
1190 appendable_value: AppendableValue<'a, I>,
1193 I: Iterator<Item = &'a PropertyDeclaration>,
1195 match appendable_value {
1196 AppendableValue::Css { css, .. } => css.append_to(dest),
1197 AppendableValue::Declaration(decl) => decl.to_css(dest),
1198 AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
1199 shorthand.longhands_to_css(decls, &mut CssWriter::new(dest))
1204 /// Append a given property and value pair to a serialization.
1205 pub fn append_serialization<'a, I, N>(
1206 dest: &mut CssStringWriter,
1208 appendable_value: AppendableValue<'a, I>,
1209 importance: Importance,
1210 is_first_serialization: &mut bool,
1213 I: Iterator<Item = &'a PropertyDeclaration>,
1216 handle_first_serialization(dest, is_first_serialization)?;
1218 property_name.to_css(&mut CssWriter::new(dest))?;
1219 dest.write_char(':')?;
1221 // for normal parsed values, add a space between key: and value
1222 match appendable_value {
1223 AppendableValue::Declaration(decl) => {
1224 if !decl.value_is_unparsed() {
1225 // For normal parsed values, add a space between key: and value.
1226 dest.write_str(" ")?
1229 AppendableValue::Css { with_variables, .. } => {
1230 if !with_variables {
1231 dest.write_str(" ")?
1234 // Currently append_serialization is only called with a Css or
1235 // a Declaration AppendableValue.
1236 AppendableValue::DeclarationsForShorthand(..) => unreachable!(),
1239 append_declaration_value(dest, appendable_value)?;
1241 if importance.important() {
1242 dest.write_str(" !important")?;
1245 dest.write_char(';')
1248 /// A helper to parse the style attribute of an element, in order for this to be
1249 /// shared between Servo and Gecko.
1251 /// Inline because we call this cross-crate.
1253 pub fn parse_style_attribute(
1255 url_data: &UrlExtraData,
1256 error_reporter: Option<&dyn ParseErrorReporter>,
1257 quirks_mode: QuirksMode,
1258 ) -> PropertyDeclarationBlock {
1259 let context = ParserContext::new(
1262 Some(CssRuleType::Style),
1263 ParsingMode::DEFAULT,
1269 let mut input = ParserInput::new(input);
1270 parse_property_declaration_list(&context, &mut Parser::new(&mut input), None)
1273 /// Parse a given property declaration. Can result in multiple
1274 /// `PropertyDeclaration`s when expanding a shorthand, for example.
1276 /// This does not attempt to parse !important at all.
1278 pub fn parse_one_declaration_into(
1279 declarations: &mut SourcePropertyDeclaration,
1282 url_data: &UrlExtraData,
1283 error_reporter: Option<&dyn ParseErrorReporter>,
1284 parsing_mode: ParsingMode,
1285 quirks_mode: QuirksMode,
1286 ) -> Result<(), ()> {
1287 let context = ParserContext::new(
1290 Some(CssRuleType::Style),
1297 let property_id_for_error_reporting = if context.error_reporting_enabled() {
1303 let mut input = ParserInput::new(input);
1304 let mut parser = Parser::new(&mut input);
1305 let start_position = parser.position();
1307 .parse_entirely(|parser| {
1308 PropertyDeclaration::parse_into(declarations, id, &context, parser)
1311 if context.error_reporting_enabled() {
1312 report_one_css_error(
1317 parser.slice_from(start_position),
1318 property_id_for_error_reporting,
1324 /// A struct to parse property declarations.
1325 struct PropertyDeclarationParser<'a, 'b: 'a> {
1326 context: &'a ParserContext<'b>,
1327 declarations: &'a mut SourcePropertyDeclaration,
1328 /// The last parsed property id if any.
1329 last_parsed_property_id: Option<PropertyId>,
1332 /// Default methods reject all at rules.
1333 impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> {
1334 type PreludeNoBlock = ();
1335 type PreludeBlock = ();
1336 type AtRule = Importance;
1337 type Error = StyleParseErrorKind<'i>;
1340 /// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.
1341 fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
1342 (name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_")
1345 impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
1346 type Declaration = Importance;
1347 type Error = StyleParseErrorKind<'i>;
1352 input: &mut Parser<'i, 't>,
1353 ) -> Result<Importance, ParseError<'i>> {
1354 let id = match PropertyId::parse(&name, self.context) {
1357 return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));
1360 if self.context.error_reporting_enabled() {
1361 self.last_parsed_property_id = Some(id.clone());
1363 input.parse_until_before(Delimiter::Bang, |input| {
1364 PropertyDeclaration::parse_into(self.declarations, id, self.context, input)
1366 let importance = match input.try(parse_important) {
1367 Ok(()) => Importance::Important,
1368 Err(_) => Importance::Normal,
1370 // In case there is still unparsed text in the declaration, we should roll back.
1371 input.expect_exhausted()?;
1376 type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>;
1378 fn alias_of_known_property(name: &str) -> Option<PropertyId> {
1379 let mut prefixed = String::with_capacity(name.len() + 5);
1380 prefixed.push_str("-moz-");
1381 prefixed.push_str(name);
1382 PropertyId::parse_enabled_for_all_content(&prefixed).ok()
1386 fn report_one_css_error<'i>(
1387 context: &ParserContext,
1388 block: Option<&PropertyDeclarationBlock>,
1389 selectors: Option<&SelectorList<SelectorImpl>>,
1390 mut error: ParseError<'i>,
1392 property: Option<PropertyId>,
1394 debug_assert!(context.error_reporting_enabled());
1396 fn all_properties_in_block(block: &PropertyDeclarationBlock, property: &PropertyId) -> bool {
1398 PropertyId::LonghandAlias(id, _) | PropertyId::Longhand(id) => block.contains(id),
1399 PropertyId::ShorthandAlias(id, _) | PropertyId::Shorthand(id) => {
1400 id.longhands().all(|longhand| block.contains(longhand))
1402 // NOTE(emilio): We could do this, but it seems of limited utility,
1403 // and it's linear on the size of the declaration block, so let's
1405 PropertyId::Custom(..) => false,
1409 if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(ref name)) = error.kind {
1410 if is_non_mozilla_vendor_identifier(name) {
1411 // If the unrecognized property looks like a vendor-specific property,
1412 // silently ignore it instead of polluting the error output.
1415 if let Some(alias) = alias_of_known_property(name) {
1416 // This is an unknown property, but its -moz-* version is known.
1417 // We don't want to report error if the -moz-* version is already
1419 if let Some(block) = block {
1420 if all_properties_in_block(block, &alias) {
1427 if let Some(ref property) = property {
1428 if let Some(block) = block {
1429 if all_properties_in_block(block, property) {
1433 error = match *property {
1434 PropertyId::Custom(ref c) => {
1435 StyleParseErrorKind::new_invalid(format!("--{}", c), error)
1437 _ => StyleParseErrorKind::new_invalid(property.non_custom_id().unwrap().name(), error),
1441 let location = error.location;
1442 let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);
1443 context.log_css_error(location, error);
1447 fn report_css_errors(
1448 context: &ParserContext,
1449 block: &PropertyDeclarationBlock,
1450 selectors: Option<&SelectorList<SelectorImpl>>,
1451 errors: &mut SmallParseErrorVec,
1453 for (error, slice, property) in errors.drain(..) {
1454 report_one_css_error(context, Some(block), selectors, error, slice, property)
1458 /// Parse a list of property declarations and return a property declaration
1460 pub fn parse_property_declaration_list(
1461 context: &ParserContext,
1463 selectors: Option<&SelectorList<SelectorImpl>>,
1464 ) -> PropertyDeclarationBlock {
1465 let mut declarations = SourcePropertyDeclaration::new();
1466 let mut block = PropertyDeclarationBlock::new();
1467 let parser = PropertyDeclarationParser {
1469 last_parsed_property_id: None,
1470 declarations: &mut declarations,
1472 let mut iter = DeclarationListParser::new(input, parser);
1473 let mut errors = SmallParseErrorVec::new();
1474 while let Some(declaration) = iter.next() {
1477 block.extend(iter.parser.declarations.drain(), importance);
1478 // We've successfully parsed a declaration, so forget about
1479 // `last_parsed_property_id`. It'd be wrong to associate any
1480 // following error with this property.
1481 iter.parser.last_parsed_property_id = None;
1483 Err((error, slice)) => {
1484 iter.parser.declarations.clear();
1486 if context.error_reporting_enabled() {
1487 let property = iter.parser.last_parsed_property_id.take();
1488 errors.push((error, slice, property));
1494 if !errors.is_empty() {
1495 report_css_errors(context, &block, selectors, &mut errors)