diff options
-rw-r--r-- | components/style/bezier.rs | 6 | ||||
-rw-r--r-- | components/style/bloom.rs | 48 | ||||
-rw-r--r-- | components/style/cascade_info.rs | 21 | ||||
-rw-r--r-- | components/style/element_state.rs | 2 | ||||
-rw-r--r-- | components/style/error_reporting.rs | 13 | ||||
-rw-r--r-- | components/style/font_metrics.rs | 15 | ||||
-rw-r--r-- | components/style/matching.rs | 99 | ||||
-rw-r--r-- | components/style/properties/declaration_block.rs | 138 | ||||
-rw-r--r-- | components/style/properties/properties.mako.rs | 16 |
9 files changed, 282 insertions, 76 deletions
diff --git a/components/style/bezier.rs b/components/style/bezier.rs index ba3d33a2394..0d74bf5da42 100644 --- a/components/style/bezier.rs +++ b/components/style/bezier.rs @@ -6,10 +6,13 @@ //! //! This is based on `WebCore/platform/graphics/UnitBezier.h` in WebKit. +#![deny(missing_docs)] + use euclid::point::Point2D; const NEWTON_METHOD_ITERATIONS: u8 = 8; +/// A Bézier curve. pub struct Bezier { ax: f64, bx: f64, @@ -20,6 +23,7 @@ pub struct Bezier { } impl Bezier { + /// Create a Bézier curve from two control points. #[inline] pub fn new(p1: Point2D<f64>, p2: Point2D<f64>) -> Bezier { let cx = 3.0 * p1.x; @@ -96,6 +100,8 @@ impl Bezier { t } + /// Solve the bezier curve for a given `x` and an `epsilon`, that should be + /// between zero and one. #[inline] pub fn solve(&self, x: f64, epsilon: f64) -> f64 { self.sample_curve_y(self.solve_curve_x(x, epsilon)) diff --git a/components/style/bloom.rs b/components/style/bloom.rs index e2f45bb6233..72e7a173de8 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -5,10 +5,43 @@ //! The style bloom filter is used as an optimization when matching deep //! descendant selectors. +#![deny(missing_docs)] + use dom::{SendElement, TElement}; use matching::MatchMethods; use selectors::bloom::BloomFilter; +/// A struct that allows us to fast-reject deep descendant selectors avoiding +/// selector-matching. +/// +/// This is implemented using a counting bloom filter, and it's a standard +/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's +/// `SelectorFilter`. +/// +/// The constraints for Servo's style system are a bit different compared to +/// traditional style systems given Servo does a parallel breadth-first +/// traversal instead of a sequential depth-first traversal. +/// +/// This implies that we need to track a bit more state than other browsers to +/// ensure we're doing the correct thing during the traversal, and being able to +/// apply this optimization effectively. +/// +/// Concretely, we have a bloom filter instance per worker thread, and we track +/// the current DOM depth in order to find a common ancestor when it doesn't +/// match the previous element we've styled. +/// +/// This is usually a pretty fast operation (we use to be one level deeper than +/// the previous one), but in the case of work-stealing, we may needed to push +/// and pop multiple elements. +/// +/// See the `insert_parents_recovering`, where most of the magic happens. +/// +/// Regarding thread-safety, this struct is safe because: +/// +/// * We clear this after a restyle. +/// * The DOM shape and attributes (and every other thing we access here) are +/// immutable during a restyle. +/// pub struct StyleBloom<E: TElement> { /// The bloom filter per se. filter: Box<BloomFilter>, @@ -18,6 +51,7 @@ pub struct StyleBloom<E: TElement> { } impl<E: TElement> StyleBloom<E> { + /// Create an empty `StyleBloom`. pub fn new() -> Self { StyleBloom { filter: Box::new(BloomFilter::new()), @@ -25,19 +59,14 @@ impl<E: TElement> StyleBloom<E> { } } + /// Return the bloom filter used properly by the `selectors` crate. pub fn filter(&self) -> &BloomFilter { &*self.filter } - pub fn maybe_pop(&mut self, element: E) { - if self.elements.last().map(|el| **el) == Some(element) { - self.pop().unwrap(); - } - } - /// Push an element to the bloom filter, knowing that it's a child of the /// last element parent. - pub fn push(&mut self, element: E) { + fn push(&mut self, element: E) { if cfg!(debug_assertions) { if self.elements.is_empty() { assert!(element.parent_element().is_none()); @@ -78,6 +107,8 @@ impl<E: TElement> StyleBloom<E> { /// In debug builds, asserts that all the parents of `element` are in the /// bloom filter. + /// + /// Goes away in release builds. pub fn assert_complete(&self, mut element: E) { if cfg!(debug_assertions) { let mut checked = 0; @@ -96,7 +127,8 @@ impl<E: TElement> StyleBloom<E> { /// Gets the element depth in the dom, to make it efficient, or if not /// provided always rebuilds the filter from scratch. /// - /// Returns the new bloom filter depth. + /// Returns the new bloom filter depth, that the traversal code is + /// responsible to keep around if it wants to get an effective filter. pub fn insert_parents_recovering(&mut self, element: E, element_depth: Option<usize>) diff --git a/components/style/cascade_info.rs b/components/style/cascade_info.rs index d0c584e7e0f..5c7565909db 100644 --- a/components/style/cascade_info.rs +++ b/components/style/cascade_info.rs @@ -1,6 +1,11 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! A structure to collect information about the cascade. + +#![deny(missing_docs)] + use dom::TNode; use properties::{DeclaredValue, PropertyDeclaration}; use values::HasViewportPercentage; @@ -12,12 +17,17 @@ use values::HasViewportPercentage; /// non-inherited property is explicitly inherited, in order to cut-off the /// traversal. pub struct CascadeInfo { + /// Whether we've seen viewport units so far. pub saw_viewport_units: bool, + /// Whether the cascade has been marked as finished. This is a debug-only + /// flag to ensure `finish` is called, given it's optional to not pass a + /// `CascadeInfo`. #[cfg(debug_assertions)] finished: bool, } impl CascadeInfo { + /// Construct a new `CascadeInfo`. #[cfg(debug_assertions)] pub fn new() -> Self { CascadeInfo { @@ -26,6 +36,7 @@ impl CascadeInfo { } } + /// Construct a new `CascadeInfo`. #[cfg(not(debug_assertions))] pub fn new() -> Self { CascadeInfo { @@ -40,7 +51,7 @@ impl CascadeInfo { pub fn on_cascade_property<T>(&mut self, _property_declaration: &PropertyDeclaration, value: &DeclaredValue<T>) - where T: HasViewportPercentage + where T: HasViewportPercentage, { // TODO: we can be smarter and keep a property bitfield to keep track of // the last applying rule. @@ -57,6 +68,11 @@ impl CascadeInfo { #[cfg(not(debug_assertions))] fn mark_as_finished_if_appropriate(&mut self) {} + /// Called when the cascade is finished, in order to use the information + /// we've collected. + /// + /// Currently used for styling to mark a node as needing restyling when the + /// viewport size changes. #[allow(unsafe_code)] pub fn finish<N: TNode>(mut self, node: &N) { self.mark_as_finished_if_appropriate(); @@ -73,6 +89,7 @@ impl CascadeInfo { impl Drop for CascadeInfo { fn drop(&mut self) { debug_assert!(self.finished, - "Didn't use the result of CascadeInfo, if you don't need it, consider passing None"); + "Didn't use the result of CascadeInfo, if you don't need \ + it, consider passing None"); } } diff --git a/components/style/element_state.rs b/components/style/element_state.rs index ef6e9704170..9ccd08334cc 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -4,6 +4,8 @@ //! States elements can be in. +#![deny(missing_docs)] + bitflags! { #[doc = "Event-based element states."] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs index 17d4ce7bb00..3a802bdd6c6 100644 --- a/components/style/error_reporting.rs +++ b/components/style/error_reporting.rs @@ -4,14 +4,27 @@ //! Types used to report parsing errors. +#![deny(missing_docs)] + use cssparser::{Parser, SourcePosition}; use log; +/// A generic trait for an error reporter. pub trait ParseErrorReporter { + /// Called the style engine detects an error. + /// + /// Returns the current input being parsed, the source position it was + /// reported from, and a message. fn report_error(&self, input: &mut Parser, position: SourcePosition, message: &str); + /// Clone this error reporter. + /// + /// TODO(emilio): I'm pretty sure all the box shenanigans can go away. fn clone(&self) -> Box<ParseErrorReporter + Send + Sync>; } +/// An error reporter that reports the errors to the `info` log channel. +/// +/// TODO(emilio): The name of this reporter is a lie, and should be renamed! pub struct StdoutErrorReporter; impl ParseErrorReporter for StdoutErrorReporter { fn report_error(&self, input: &mut Parser, position: SourcePosition, message: &str) { diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs index dac7a69ee29..417c7999845 100644 --- a/components/style/font_metrics.rs +++ b/components/style/font_metrics.rs @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +//! Access to font metrics from the style system. + +#![deny(missing_docs)] + use Atom; use app_units::Au; use euclid::Size2D; @@ -11,13 +15,19 @@ use std::fmt; /// value of certain CSS units like `ex`. #[derive(Debug, PartialEq, Clone)] pub struct FontMetrics { + /// The x-height of the font. pub x_height: Au, + /// The zero advance. pub zero_advance_measure: Size2D<Au>, } +/// The result for querying font metrics for a given font family. #[derive(Debug, PartialEq, Clone)] pub enum FontMetricsQueryResult { + /// The font is available, but we may or may not have found any font metrics + /// for it. Available(Option<FontMetrics>), + /// The font is not available. NotAvailable, } @@ -26,9 +36,8 @@ pub trait FontMetricsProvider: Send + Sync + fmt::Debug { /// Obtain the metrics for given font family. /// /// TODO: We could make this take the full list, I guess, and save a few - /// virtual calls. - /// - /// This is not too common in practice though. + /// virtual calls in the case we are repeatedly unable to find font metrics? + /// That is not too common in practice though. fn query(&self, _font_name: &Atom) -> FontMetricsQueryResult { FontMetricsQueryResult::NotAvailable } diff --git a/components/style/matching.rs b/components/style/matching.rs index 0b06c998098..af8cd4bf627 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -5,6 +5,7 @@ //! High-level interface to CSS selector matching. #![allow(unsafe_code)] +#![deny(missing_docs)] use {Atom, LocalName}; use animation::{self, Animation, PropertyAnimation}; @@ -49,11 +50,22 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: & flags } +/// The rule nodes for each of the pseudo-elements of an element. +/// +/// TODO(emilio): Probably shouldn't be a `HashMap` by default, but a smaller +/// array. type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode, BuildHasherDefault<::fnv::FnvHasher>>; + +/// The results of selector matching on an element. pub struct MatchResults { + /// The rule node reference that represents the rules matched by the + /// element. pub primary: StrongRuleNode, + /// A set of style relations (different hints about what rules matched or + /// could have matched). pub relations: StyleRelations, + /// The results of selector-matching the pseudo-elements. pub per_pseudo: PseudoRuleNodes, } @@ -65,7 +77,11 @@ impl MatchResults { } } -/// Information regarding a candidate. +/// Information regarding a style sharing candidate. +/// +/// Note that this information is stored in TLS and cleared after the traversal, +/// and once here, the style information of the element is immutable, so it's +/// safe to access. /// /// TODO: We can stick a lot more info here. #[derive(Debug)] @@ -75,7 +91,7 @@ struct StyleSharingCandidate<E: TElement> { element: SendElement<E>, /// The cached common style affecting attribute info. common_style_affecting_attributes: Option<CommonStyleAffectingAttributes>, - /// the cached class names. + /// The cached class names. class_attributes: Option<Vec<Atom>>, } @@ -95,20 +111,39 @@ pub struct StyleSharingCandidateCache<E: TElement> { cache: LRUCache<StyleSharingCandidate<E>, ()>, } +/// A cache miss result. #[derive(Clone, Debug)] pub enum CacheMiss { + /// The parents don't match. Parent, + /// The local name of the element and the candidate don't match. LocalName, + /// The namespace of the element and the candidate don't match. Namespace, + /// One of the element or the candidate was a link, but the other one + /// wasn't. Link, + /// The element and the candidate match different kind of rules. This can + /// only happen in Gecko. UserAndAuthorRules, + /// The element and the candidate are in a different state. State, + /// The element had an id attribute, which qualifies for a unique style. IdAttr, + /// The element had a style attribute, which qualifies for a unique style. StyleAttr, + /// The element and the candidate class names didn't match. Class, + /// The element and the candidate common style affecting attributes didn't + /// match. CommonStyleAffectingAttributes, + /// The presentation hints didn't match. PresHints, + /// The element and the candidate didn't match the same set of + /// sibling-affecting rules. SiblingRules, + /// The element and the candidate didn't match the same set of non-common + /// style affecting attribute selectors. NonCommonAttrRules, } @@ -213,27 +248,43 @@ fn have_same_presentational_hints<E: TElement>(element: &E, candidate: &E) -> bo } bitflags! { + /// A set of common style-affecting attributes we check separately to + /// optimize the style sharing cache. pub flags CommonStyleAffectingAttributes: u8 { + /// The `hidden` attribute. const HIDDEN_ATTRIBUTE = 0x01, + /// The `nowrap` attribute. const NO_WRAP_ATTRIBUTE = 0x02, + /// The `align="left"` attribute. const ALIGN_LEFT_ATTRIBUTE = 0x04, + /// The `align="center"` attribute. const ALIGN_CENTER_ATTRIBUTE = 0x08, + /// The `align="right"` attribute. const ALIGN_RIGHT_ATTRIBUTE = 0x10, } } +/// The information of how to match a given common-style affecting attribute. pub struct CommonStyleAffectingAttributeInfo { + /// The attribute name. pub attr_name: LocalName, + /// The matching mode for the attribute. pub mode: CommonStyleAffectingAttributeMode, } +/// How should we match a given common style-affecting attribute? #[derive(Clone)] pub enum CommonStyleAffectingAttributeMode { + /// Just for presence? IsPresent(CommonStyleAffectingAttributes), + /// For presence and equality with a given value. IsEqual(Atom, CommonStyleAffectingAttributes), } -// NB: This must match the order in `selectors::matching::CommonStyleAffectingAttributes`. +/// The common style affecting attribute array. +/// +/// TODO: This should be a `const static` or similar, but couldn't be because +/// `Atom`s have destructors. #[inline] pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo; 5] { [ @@ -260,9 +311,14 @@ pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo ] } -/// Attributes that, if present, disable style sharing. All legacy HTML attributes must be in -/// either this list or `common_style_affecting_attributes`. See the comment in +/// Attributes that, if present, disable style sharing. All legacy HTML +/// attributes must be in either this list or +/// `common_style_affecting_attributes`. See the comment in /// `synthesize_presentational_hints_for_legacy_attributes`. +/// +/// TODO(emilio): This is not accurate now, we don't disable style sharing for +/// this now since we check for attribute selectors in the stylesheet. Consider +/// removing this. pub fn rare_style_affecting_attributes() -> [LocalName; 4] { [local_name!("bgcolor"), local_name!("border"), local_name!("colspan"), local_name!("rowspan")] } @@ -301,6 +357,7 @@ fn match_same_sibling_affecting_rules<E: TElement>(element: &E, static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 8; impl<E: TElement> StyleSharingCandidateCache<E> { + /// Create a new style sharing candidate cache. pub fn new() -> Self { StyleSharingCandidateCache { cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE), @@ -311,6 +368,9 @@ impl<E: TElement> StyleSharingCandidateCache<E> { self.cache.iter_mut() } + /// Tries to insert an element in the style sharing cache. + /// + /// Fails if we know it should never be in the cache. pub fn insert_if_possible(&mut self, element: &E, style: &Arc<ComputedValues>, @@ -353,10 +413,12 @@ impl<E: TElement> StyleSharingCandidateCache<E> { }, ()); } + /// Touch a given index in the style sharing candidate cache. pub fn touch(&mut self, index: usize) { self.cache.touch(index); } + /// Clear the style sharing candidate cache. pub fn clear(&mut self) { self.cache.evict_all() } @@ -367,16 +429,14 @@ pub enum StyleSharingResult { /// We didn't find anybody to share the style with. CannotShare, /// The node's style can be shared. The integer specifies the index in the - /// LRU cache that was hit and the damage that was done, and the restyle - /// result the original result of the candidate's styling, that is, whether - /// it should stop the traversal or not. + /// LRU cache that was hit and the damage that was done. StyleWasShared(usize), } -// Callers need to pass several boolean flags to cascade_node_pseudo_element. -// We encapsulate them in this struct to avoid mixing them up. -// -// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps? +/// Callers need to pass several boolean flags to cascade_node_pseudo_element. +/// We encapsulate them in this struct to avoid mixing them up. +/// +/// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps? struct CascadeBooleans { shareable: bool, animate: bool, @@ -511,7 +571,9 @@ fn compute_rule_node<E: TElement>(context: &StyleContext<E>, impl<E: TElement> PrivateMatchMethods for E {} +/// The public API that elements expose for selector matching. pub trait MatchMethods : TElement { + /// Runs selector matching of this element, and returns the result. fn match_element(&self, context: &StyleContext<Self>, parent_bf: Option<&BloomFilter>) -> MatchResults { @@ -556,9 +618,10 @@ pub trait MatchMethods : TElement { } } - /// Attempts to share a style with another node. This method is unsafe because it depends on - /// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to - /// guarantee that at the type system level yet. + /// Attempts to share a style with another node. This method is unsafe + /// because it depends on the `style_sharing_candidate_cache` having only + /// live nodes in it, and we have no way to guarantee that at the type + /// system level yet. unsafe fn share_style_if_possible(&self, style_sharing_candidate_cache: &mut StyleSharingCandidateCache<Self>, @@ -671,6 +734,9 @@ pub trait MatchMethods : TElement { self.each_class(|class| bf.remove(class)); } + /// Given the old and new style of this element, and whether it's a + /// pseudo-element, compute the restyle damage used to determine which + /// kind of layout or painting operations we'll need. fn compute_restyle_damage(&self, old_style: Option<&Arc<ComputedValues>>, new_style: &Arc<ComputedValues>, @@ -709,6 +775,8 @@ pub trait MatchMethods : TElement { } } + /// Given the results of selector matching, run the CSS cascade and style + /// the node, potentially starting any new transitions or animations. fn cascade_node(&self, context: &StyleContext<Self>, mut data: &mut AtomicRefMut<ElementData>, @@ -783,6 +851,7 @@ pub trait MatchMethods : TElement { data.finish_styling(new_styles, damage); } + /// Given the old and new styling results, compute the final restyle damage. fn compute_damage_and_cascade_pseudos( &self, old_primary: Option<&Arc<ComputedValues>>, diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 80ba5787d96..d8638284e14 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +//! A property declaration block. + +#![deny(missing_docs)] + use cssparser::{DeclarationListParser, parse_important}; use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter}; use error_reporting::ParseErrorReporter; @@ -13,7 +17,9 @@ use style_traits::ToCss; use stylesheets::Origin; use super::*; - +/// A declaration [importance][importance]. +/// +/// [importance]: https://drafts.csswg.org/css-cascade/#importance #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Importance { @@ -25,6 +31,7 @@ pub enum Importance { } impl Importance { + /// Return whether this is an important declaration. pub fn important(self) -> bool { match self { Importance::Normal => false, @@ -34,10 +41,12 @@ impl Importance { } /// Overridden declarations are skipped. -// FIXME (https://github.com/servo/servo/issues/3426) #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct PropertyDeclarationBlock { + /// The group of declarations, along with their importance. + /// + /// Only deduplicated declarations appear here. #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")] pub declarations: Vec<(PropertyDeclaration, Importance)>, @@ -64,6 +73,9 @@ impl PropertyDeclarationBlock { self.declarations.len() > self.important_count as usize } + /// Get a declaration for a given property. + /// + /// NOTE: This is linear time. pub fn get(&self, property: PropertyDeclarationId) -> Option< &(PropertyDeclaration, Importance)> { self.declarations.iter().find(|&&(ref decl, _)| decl.id() == property) } @@ -72,7 +84,8 @@ impl PropertyDeclarationBlock { /// /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue pub fn property_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result - where W: fmt::Write { + where W: fmt::Write, + { // Step 1: done when parsing a string to PropertyId // Step 2 @@ -149,7 +162,11 @@ impl PropertyDeclarationBlock { } } - pub fn set_parsed_declaration(&mut self, declaration: PropertyDeclaration, importance: Importance) { + /// Adds or overrides the declaration for a given property in this block, + /// without taking into account any kind of priority. + pub fn set_parsed_declaration(&mut self, + declaration: PropertyDeclaration, + importance: Importance) { for slot in &mut *self.declarations { if slot.0.id() == declaration.id() { match (slot.1, importance) { @@ -172,6 +189,7 @@ impl PropertyDeclarationBlock { } } + /// Set the declaration importance for a given property, if found. pub fn set_importance(&mut self, property: &PropertyId, new_importance: Importance) { for &mut (ref declaration, ref mut importance) in &mut self.declarations { if declaration.id().is_or_is_longhand_of(property) { @@ -203,7 +221,8 @@ impl PropertyDeclarationBlock { /// Take a declaration block known to contain a single property and serialize it. pub fn single_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result - where W: fmt::Write { + where W: fmt::Write, + { match property.as_shorthand() { Err(_longhand_or_custom) => { if self.declarations.len() == 1 { @@ -236,7 +255,9 @@ impl PropertyDeclarationBlock { impl ToCss for PropertyDeclarationBlock { // https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { let mut is_first_serialization = true; // trailing serializations should have a prepended space // Step 1 -> dest = result list @@ -356,15 +377,28 @@ impl ToCss for PropertyDeclarationBlock { } } +/// A convenient enum to represent different kinds of stuff that can represent a +/// _value_ in the serialization of a property declaration. pub enum AppendableValue<'a, I> -where I: Iterator<Item=&'a PropertyDeclaration> { + where I: Iterator<Item=&'a PropertyDeclaration>, +{ + /// A given declaration, of which we'll serialize just the value. Declaration(&'a PropertyDeclaration), + /// A set of declarations for a given shorthand. + /// + /// FIXME: This needs more docs, where are the shorthands expanded? We print + /// the property name before-hand, don't we? DeclarationsForShorthand(ShorthandId, I), + /// A raw CSS string, coming for example from a property with CSS variables. Css(&'a str) } -fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result where W: fmt::Write { - // after first serialization(key: value;) add whitespace between the pairs +/// Potentially appends whitespace after the first (property: value;) pair. +fn handle_first_serialization<W>(dest: &mut W, + is_first_serialization: &mut bool) + -> fmt::Result + where W: fmt::Write, +{ if !*is_first_serialization { try!(write!(dest, " ")); } else { @@ -374,45 +408,48 @@ fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool Ok(()) } -pub fn append_declaration_value<'a, W, I> - (dest: &mut W, - appendable_value: AppendableValue<'a, I>, - importance: Importance, - is_overflow_with_name: bool) - -> fmt::Result - where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { - match appendable_value { - AppendableValue::Css(css) => { - try!(write!(dest, "{}", css)) - }, - AppendableValue::Declaration(decl) => { - try!(decl.to_css(dest)); - }, - AppendableValue::DeclarationsForShorthand(shorthand, decls) => { - if is_overflow_with_name { - try!(shorthand.overflow_longhands_to_css(decls, dest)); - } else { - try!(shorthand.longhands_to_css(decls, dest)); - } - } - } - - if importance.important() { - try!(write!(dest, " !important")); - } - - Ok(()) +/// Append a given kind of appendable value to a serialization. +pub fn append_declaration_value<'a, W, I>(dest: &mut W, + appendable_value: AppendableValue<'a, I>, + importance: Importance, + is_overflow_with_name: bool) + -> fmt::Result + where W: fmt::Write, + I: Iterator<Item=&'a PropertyDeclaration>, +{ + match appendable_value { + AppendableValue::Css(css) => { + try!(write!(dest, "{}", css)) + }, + AppendableValue::Declaration(decl) => { + try!(decl.to_css(dest)); + }, + AppendableValue::DeclarationsForShorthand(shorthand, decls) => { + if is_overflow_with_name { + try!(shorthand.overflow_longhands_to_css(decls, dest)); + } else { + try!(shorthand.longhands_to_css(decls, dest)); + } + } + } + + if importance.important() { + try!(write!(dest, " !important")); + } + + Ok(()) } +/// Append a given property and value pair to a serialization. pub fn append_serialization<'a, W, I, N>(dest: &mut W, - property_name: &N, - appendable_value: AppendableValue<'a, I>, - importance: Importance, - is_first_serialization: &mut bool) - -> fmt::Result + property_name: &N, + appendable_value: AppendableValue<'a, I>, + importance: Importance, + is_first_serialization: &mut bool) + -> fmt::Result where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration>, - N: ToCss + N: ToCss, { try!(handle_first_serialization(dest, is_first_serialization)); @@ -443,6 +480,8 @@ pub fn append_serialization<'a, W, I, N>(dest: &mut W, write!(dest, ";") } +/// A helper to parse the style attribute of an element, in order for this to be +/// shared between Servo and Gecko. pub fn parse_style_attribute(input: &str, base_url: &ServoUrl, error_reporter: StdBox<ParseErrorReporter + Send>, @@ -452,6 +491,8 @@ pub fn parse_style_attribute(input: &str, parse_property_declaration_list(&context, &mut Parser::new(input)) } +/// Parse a given property declaration. Can result in multiple +/// `PropertyDeclaration`s when expanding a longhand, for example. pub fn parse_one_declaration(id: PropertyId, input: &str, base_url: &ServoUrl, @@ -466,6 +507,7 @@ pub fn parse_one_declaration(id: PropertyId, } } +/// A struct to parse property declarations. struct PropertyDeclarationParser<'a, 'b: 'a> { context: &'a ParserContext<'b>, } @@ -479,6 +521,11 @@ impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { + /// A single declaration may be expanded into multiple ones if it's a + /// shorthand for example, so that's why this is a vector. + /// + /// TODO(emilio): Seems like there's potentially a bunch of performance work + /// we could do here. type Declaration = (Vec<PropertyDeclaration>, Importance); fn parse_value(&mut self, name: &str, input: &mut Parser) @@ -500,7 +547,10 @@ impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { } -pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) +/// Parse a list of property declarations and return a property declaration +/// block. +pub fn parse_property_declaration_list(context: &ParserContext, + input: &mut Parser) -> PropertyDeclarationBlock { let mut declarations = Vec::new(); let mut important_count = 0; diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index ab25f16e87d..9d7247130f2 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -467,10 +467,18 @@ impl ShorthandId { } } - // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal - // values, they no longer use the shared property name "overflow". - pub fn overflow_longhands_to_css<'a, W, I>(&self, declarations: I, dest: &mut W) -> fmt::Result - where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { + /// Overflow does not behave like a normal shorthand. When overflow-x and + /// overflow-y are not of equal values, they no longer use the shared + /// property name "overflow". + /// + /// We use this function as a special-case for that. + pub fn overflow_longhands_to_css<'a, W, I>(&self, + declarations: I, + dest: &mut W) + -> fmt::Result + where W: fmt::Write, + I: Iterator<Item=&'a PropertyDeclaration>, + { match *self { ShorthandId::Overflow => { match shorthands::overflow::LonghandsToSerialize::from_iter(declarations) { |