servo: Merge #19126 - style: Introduce SourceSizeList (from emilio:source-size-list); r=nox
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 06 Nov 2017 09:37:14 -0600
changeset 390345 a156ab11569b74871f52e2e7024af0efbc182f0c
parent 390344 d0c21ca40a543227a1a1f2b1a075d9620abdefd5
child 390346 46ebff3824d4abf7b8233ad3e5c491b7dad7f1bb
push id32827
push userccoroiu@mozilla.com
push dateMon, 06 Nov 2017 23:02:00 +0000
treeherdermozilla-central@62aeebcc676e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #19126 - style: Introduce SourceSizeList (from emilio:source-size-list); r=nox This is part of the work for https://bugzilla.mozilla.org/show_bug.cgi?id=1408308. But this can just land now IMO, before I do the integration bits. Source-Repo: https://github.com/servo/servo Source-Revision: 0047c77f0374ec773fab1cb0c0b7fcfdbb6472d2
servo/components/style/gecko/media_queries.rs
servo/components/style/media_queries.rs
servo/components/style/servo/media_queries.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/source_size_list.rs
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -5,31 +5,28 @@
 //! Gecko's media-query device and expression representation.
 
 use app_units::AU_PER_PX;
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{CssStringWriter, Parser, RGBA, Token, BasicParseErrorKind};
 use euclid::ScaleFactor;
 use euclid::Size2D;
-use font_metrics::get_metrics_provider_for_product;
 use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
 use gecko_bindings::bindings;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType};
 use gecko_bindings::structs::{nsPresContext, RawGeckoPresContextOwned};
 use media_queries::MediaType;
 use parser::ParserContext;
-use properties::{ComputedValues, StyleBuilder};
+use properties::ComputedValues;
 use properties::longhands::font_size;
-use rule_cache::RuleCacheConditions;
 use servo_arc::Arc;
-use std::cell::RefCell;
 use std::fmt::{self, Write};
 use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
 use str::starts_with_ignore_ascii_case;
 use string_cache::Atom;
 use style_traits::{CSSPixel, DevicePixel};
 use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::viewport::ViewportConstraints;
 use stylesheets::Origin;
@@ -717,55 +714,45 @@ impl Expression {
     ) -> bool {
         use self::MediaExpressionValue::*;
         use std::cmp::Ordering;
 
         debug_assert!(self.range == nsMediaExpression_Range::eEqual ||
                       self.feature.mRangeType == nsMediaFeature_RangeType::eMinMaxAllowed,
                       "Whoops, wrong range");
 
-        let default_values = device.default_computed_values();
-
-
-        let provider = get_metrics_provider_for_product();
-
         // http://dev.w3.org/csswg/mediaqueries3/#units
         // em units are relative to the initial font-size.
-        let mut conditions = RuleCacheConditions::default();
-        let context = computed::Context {
-            is_root_element: false,
-            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
-            font_metrics_provider: &provider,
-            cached_system_font: None,
-            in_media_query: true,
-            quirks_mode,
-            for_smil_animation: false,
-            for_non_inherited_property: None,
-            rule_cache_conditions: RefCell::new(&mut conditions),
-        };
-
         let required_value = match self.value {
             Some(ref v) => v,
             None => {
                 // If there's no value, always match unless it's a zero length
                 // or a zero integer or boolean.
                 return match *actual_value {
                     BoolInteger(v) => v,
                     Integer(v) => v != 0,
-                    Length(ref l) => l.to_computed_value(&context).px() != 0.,
+                    Length(ref l) => {
+                        computed::Context::for_media_query_evaluation(
+                            device,
+                            quirks_mode,
+                            |context| l.to_computed_value(&context).px() != 0.,
+                        )
+                    },
                     _ => true,
                 }
             }
         };
 
         // FIXME(emilio): Handle the possible floating point errors?
         let cmp = match (required_value, actual_value) {
             (&Length(ref one), &Length(ref other)) => {
-                one.to_computed_value(&context).to_i32_au()
-                    .cmp(&other.to_computed_value(&context).to_i32_au())
+                computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
+                    one.to_computed_value(&context).to_i32_au()
+                        .cmp(&other.to_computed_value(&context).to_i32_au())
+                })
             }
             (&Integer(one), &Integer(ref other)) => one.cmp(other),
             (&BoolInteger(one), &BoolInteger(ref other)) => one.cmp(other),
             (&Float(one), &Float(ref other)) => one.partial_cmp(other).unwrap(),
             (&IntRatio(one_num, one_den), &IntRatio(other_num, other_den)) => {
                 // Extend to avoid overflow.
                 (one_num as u64 * other_den as u64).cmp(
                     &(other_num as u64 * one_den as u64))
--- a/servo/components/style/media_queries.rs
+++ b/servo/components/style/media_queries.rs
@@ -72,27 +72,20 @@ pub struct MediaQuery {
     /// The set of expressions that this media query contains.
     pub expressions: Vec<Expression>,
 }
 
 impl MediaQuery {
     /// Return a media query that never matches, used for when we fail to parse
     /// a given media query.
     fn never_matching() -> Self {
-        Self::new(Some(Qualifier::Not), MediaQueryType::All, vec![])
-    }
-
-    /// Trivially constructs a new media query.
-    pub fn new(qualifier: Option<Qualifier>,
-               media_type: MediaQueryType,
-               expressions: Vec<Expression>) -> MediaQuery {
-        MediaQuery {
-            qualifier: qualifier,
-            media_type: media_type,
-            expressions: expressions,
+        Self {
+            qualifier: Some(Qualifier::Not),
+            media_type: MediaQueryType::All,
+            expressions: vec![],
         }
     }
 }
 
 impl ToCss for MediaQuery {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
@@ -204,19 +197,22 @@ impl MediaQuery {
         } else if input.try(|input| input.expect_ident_matching("not")).is_ok() {
             Some(Qualifier::Not)
         } else {
             None
         };
 
         let media_type = match input.try(|i| i.expect_ident_cloned()) {
             Ok(ident) => {
-                let result: Result<_, ParseError> = MediaQueryType::parse(&*ident)
-                    .map_err(|()| input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
-                result?
+                MediaQueryType::parse(&*ident)
+                    .map_err(|()| {
+                        input.new_custom_error(
+                            SelectorParseErrorKind::UnexpectedIdent(ident.clone())
+                        )
+                    })?
             }
             Err(_) => {
                 // Media type is only optional if qualifier is not specified.
                 if qualifier.is_some() {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
 
                 // Without a media type, require at least one expression.
@@ -224,17 +220,17 @@ impl MediaQuery {
 
                 MediaQueryType::All
             }
         };
 
         // Parse any subsequent expressions
         loop {
             if input.try(|input| input.expect_ident_matching("and")).is_err() {
-                return Ok(MediaQuery::new(qualifier, media_type, expressions))
+                return Ok(MediaQuery { qualifier, media_type, expressions })
             }
             expressions.push(Expression::parse(context, input)?)
         }
     }
 }
 
 /// Parse a media query list from CSS.
 ///
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -3,24 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Servo's media-query device and expression representation.
 
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{Parser, RGBA};
 use euclid::{ScaleFactor, Size2D, TypedSize2D};
-use font_metrics::ServoMetricsProvider;
 use media_queries::MediaType;
 use parser::ParserContext;
-use properties::{ComputedValues, StyleBuilder};
+use properties::ComputedValues;
 use properties::longhands::font_size;
-use rule_cache::RuleCacheConditions;
 use selectors::parser::SelectorParseErrorKind;
-use std::cell::RefCell;
 use std::fmt;
 use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
 use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
 use style_traits::viewport::ViewportConstraints;
 use values::computed::{self, ToComputedValue};
 use values::specified;
 
 /// A device is a structure that represents the current media a given document
@@ -247,34 +244,17 @@ pub enum Range<T> {
     /// At most the inner value.
     Max(T),
     /// Exactly the inner value.
     Eq(T),
 }
 
 impl Range<specified::Length> {
     fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
-        let default_values = device.default_computed_values();
-        let mut conditions = RuleCacheConditions::default();
-        // http://dev.w3.org/csswg/mediaqueries3/#units
-        // em units are relative to the initial font-size.
-        let context = computed::Context {
-            is_root_element: false,
-            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
-            // Servo doesn't support font metrics
-            // A real provider will be needed here once we do; since
-            // ch units can exist in media queries.
-            font_metrics_provider: &ServoMetricsProvider,
-            in_media_query: true,
-            cached_system_font: None,
-            quirks_mode,
-            for_smil_animation: false,
-            for_non_inherited_property: None,
-            rule_cache_conditions: RefCell::new(&mut conditions),
-        };
-
-        match *self {
-            Range::Min(ref width) => Range::Min(Au::from(width.to_computed_value(&context))),
-            Range::Max(ref width) => Range::Max(Au::from(width.to_computed_value(&context))),
-            Range::Eq(ref width) => Range::Eq(Au::from(width.to_computed_value(&context)))
-        }
+        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
+            match *self {
+                Range::Min(ref width) => Range::Min(Au::from(width.to_computed_value(&context))),
+                Range::Max(ref width) => Range::Max(Au::from(width.to_computed_value(&context))),
+                Range::Eq(ref width) => Range::Eq(Au::from(width.to_computed_value(&context)))
+            }
+        })
     }
 }
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 //! Computed values.
 
 use {Atom, Namespace};
 use context::QuirksMode;
 use euclid::Size2D;
-use font_metrics::FontMetricsProvider;
+use font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
 use media_queries::Device;
 #[cfg(feature = "gecko")]
 use properties;
 use properties::{ComputedValues, LonghandId, StyleBuilder};
 use rule_cache::RuleCacheConditions;
 #[cfg(feature = "servo")]
 use servo_url::ServoUrl;
 use std::{f32, fmt};
@@ -131,16 +131,46 @@ pub struct Context<'a> {
 
     /// The conditions to cache a rule node on the rule cache.
     ///
     /// FIXME(emilio): Drop the refcell.
     pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
 }
 
 impl<'a> Context<'a> {
+    /// Creates a suitable context for media query evaluation, in which
+    /// font-relative units compute against the system_font, and executes `f`
+    /// with it.
+    pub fn for_media_query_evaluation<F, R>(
+        device: &Device,
+        quirks_mode: QuirksMode,
+        f: F,
+    ) -> R
+    where
+        F: FnOnce(&Context) -> R
+    {
+        let mut conditions = RuleCacheConditions::default();
+        let default_values = device.default_computed_values();
+        let provider = get_metrics_provider_for_product();
+
+        let context = Context {
+            is_root_element: false,
+            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
+            font_metrics_provider: &provider,
+            cached_system_font: None,
+            in_media_query: true,
+            quirks_mode,
+            for_smil_animation: false,
+            for_non_inherited_property: None,
+            rule_cache_conditions: RefCell::new(&mut conditions),
+        };
+
+        f(&context)
+    }
+
     /// Whether the current element is the root element.
     pub fn is_root_element(&self) -> bool {
         self.is_root_element
     }
 
     /// The current device.
     pub fn device(&self) -> &Device {
         self.builder.device
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -71,16 +71,17 @@ pub mod font;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod grid;
 pub mod image;
 pub mod length;
 pub mod percentage;
 pub mod position;
 pub mod rect;
+pub mod source_size_list;
 pub mod svg;
 pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 
 /// Common handling for the specified value CSS url() values.
 pub mod url {
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/source_size_list.rs
@@ -0,0 +1,79 @@
+/* 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/. */
+
+//! https://html.spec.whatwg.org/multipage/#source-size-list
+
+use app_units::Au;
+use cssparser::Parser;
+use media_queries::{Device, Expression as MediaExpression};
+use parser::{Parse, ParserContext};
+use selectors::context::QuirksMode;
+use style_traits::ParseError;
+use values::computed::{self, ToComputedValue};
+use values::specified::Length;
+
+/// A value for a `<source-size>`:
+///
+/// https://html.spec.whatwg.org/multipage/#source-size
+pub struct SourceSize {
+    condition: MediaExpression,
+    value: Length,
+}
+
+impl Parse for SourceSize {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        let condition = MediaExpression::parse(context, input)?;
+        let value = Length::parse_non_negative(context, input)?;
+
+        Ok(Self { condition, value })
+    }
+}
+
+/// A value for a `<source-size-list>`:
+///
+/// https://html.spec.whatwg.org/multipage/#source-size-list
+pub struct SourceSizeList {
+    source_sizes: Vec<SourceSize>,
+    value: Length,
+}
+
+impl SourceSizeList {
+    /// Evaluate this <source-size-list> to get the final viewport length.
+    pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au {
+        let matching_source_size = self.source_sizes.iter().find(|source_size| {
+            source_size.condition.matches(device, quirks_mode)
+        });
+
+        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
+            match matching_source_size {
+                Some(source_size) => {
+                    source_size.value.to_computed_value(context)
+                }
+                None => {
+                    self.value.to_computed_value(context)
+                }
+            }
+        }).into()
+    }
+}
+
+impl Parse for SourceSizeList {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        let source_sizes = input.try(|input| {
+            input.parse_comma_separated(|input| {
+                SourceSize::parse(context, input)
+            })
+        }).unwrap_or(vec![]);
+
+        let value = Length::parse_non_negative(context, input)?;
+
+        Ok(Self { source_sizes, value })
+    }
+}