Auto merge of #19126 - emilio:source-size-list, r=nox

style: Introduce SourceSizeList.

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.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19126)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-11-06 09:37:14 -06:00 committed by GitHub
commit 0047c77f03
6 changed files with 142 additions and 69 deletions

View file

@ -10,7 +10,6 @@ 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;
@ -20,11 +19,9 @@ 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;
@ -722,26 +719,8 @@ impl Expression {
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 => {
@ -750,7 +729,13 @@ impl Expression {
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,
}
}
@ -759,8 +744,10 @@ impl Expression {
// 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),

View file

@ -77,17 +77,10 @@ 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![],
}
}
}
@ -209,9 +202,12 @@ impl MediaQuery {
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.
@ -229,7 +225,7 @@ impl MediaQuery {
// 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)?)
}

View file

@ -8,14 +8,11 @@ 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};
@ -252,29 +249,12 @@ pub enum Range<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)))
}
})
}
}

View file

@ -7,7 +7,7 @@
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;
@ -136,6 +136,36 @@ pub struct Context<'a> {
}
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

View file

@ -76,6 +76,7 @@ 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;

View file

@ -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 })
}
}