mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
servo: Merge #14802 - Document more style modules (from emilio:no-missing-docs); r=Wafflespeanut
Source-Repo: https://github.com/servo/servo Source-Revision: 2ebcad10f58151a1c531d80794a0211ba12cbeb0
This commit is contained in:
parent
fe6309141e
commit
b5757b5057
@ -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))
|
||||
|
@ -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>)
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
//! States elements can be in.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
bitflags! {
|
||||
#[doc = "Event-based element states."]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
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
|
||||
/// 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>,
|
||||
N: ToCss
|
||||
{
|
||||
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
|
||||
where W: fmt::Write,
|
||||
I: Iterator<Item=&'a PropertyDeclaration>,
|
||||
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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user