stylo: Integrate Servo SourceSizeList in ResponsiveImageSelector.

This needs to dumb down the parsing in order to match what we do in Gecko and
pass more tests.

The remaining tests are just because of calc() in media queries and "or" media
expressions.

Bug: 1408308
Reviewed-by: Manishearth
MozReview-Commit-ID: CXGdYVbojBL
This commit is contained in:
Emilio Cobos Álvarez 2017-11-09 15:18:08 +01:00
parent bc58e18761
commit a76cb65751
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 131 additions and 15 deletions

View file

@ -23,7 +23,7 @@
//! [cssparser]: ../cssparser/index.html //! [cssparser]: ../cssparser/index.html
//! [selectors]: ../selectors/index.html //! [selectors]: ../selectors/index.html
#![deny(warnings)] // #![deny(warnings)]
#![deny(missing_docs)] #![deny(missing_docs)]
// FIXME(bholley): We need to blanket-allow unsafe code in order to make the // FIXME(bholley): We need to blanket-allow unsafe code in order to make the

View file

@ -5,18 +5,22 @@
//! https://html.spec.whatwg.org/multipage/#source-size-list //! https://html.spec.whatwg.org/multipage/#source-size-list
use app_units::Au; use app_units::Au;
use cssparser::Parser; use cssparser::{Delimiter, Parser, Token};
#[cfg(feature = "gecko")]
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use media_queries::{Device, Expression as MediaExpression}; use media_queries::{Device, Expression as MediaExpression};
use parser::{Parse, ParserContext}; use parser::{Parse, ParserContext};
use selectors::context::QuirksMode; use selectors::context::QuirksMode;
use style_traits::ParseError; use style_traits::ParseError;
use values::computed::{self, ToComputedValue}; use values::computed::{self, ToComputedValue};
use values::specified::Length; use values::specified::{Length, NoCalcLength, ViewportPercentageLength};
/// A value for a `<source-size>`: /// A value for a `<source-size>`:
/// ///
/// https://html.spec.whatwg.org/multipage/#source-size /// https://html.spec.whatwg.org/multipage/#source-size
pub struct SourceSize { pub struct SourceSize {
// FIXME(emilio): This should be a `MediaCondition`, and support `and` and
// `or`.
condition: MediaExpression, condition: MediaExpression,
value: Length, value: Length,
} }
@ -38,10 +42,18 @@ impl Parse for SourceSize {
/// https://html.spec.whatwg.org/multipage/#source-size-list /// https://html.spec.whatwg.org/multipage/#source-size-list
pub struct SourceSizeList { pub struct SourceSizeList {
source_sizes: Vec<SourceSize>, source_sizes: Vec<SourceSize>,
value: Length, value: Option<Length>,
} }
impl SourceSizeList { impl SourceSizeList {
/// Create an empty `SourceSizeList`, which can be used as a fall-back.
pub fn empty() -> Self {
Self {
source_sizes: vec![],
value: None,
}
}
/// Evaluate this <source-size-list> to get the final viewport length. /// Evaluate this <source-size-list> to get the final viewport length.
pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au { pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au {
let matching_source_size = self.source_sizes.iter().find(|source_size| { let matching_source_size = self.source_sizes.iter().find(|source_size| {
@ -54,26 +66,80 @@ impl SourceSizeList {
source_size.value.to_computed_value(context) source_size.value.to_computed_value(context)
} }
None => { None => {
self.value.to_computed_value(context) match self.value {
Some(ref v) => v.to_computed_value(context),
None => {
Length::NoCalc(NoCalcLength::ViewportPercentage(
ViewportPercentageLength::Vw(100.)
)).to_computed_value(context)
}
}
} }
} }
}).into() }).into()
} }
} }
impl Parse for SourceSizeList { enum SourceSizeOrLength {
SourceSize(SourceSize),
Length(Length),
}
impl Parse for SourceSizeOrLength {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let source_sizes = input.try(|input| { if let Ok(size) = input.try(|input| SourceSize::parse(context, input)) {
input.parse_comma_separated(|input| { return Ok(SourceSizeOrLength::SourceSize(size));
SourceSize::parse(context, input) }
})
}).unwrap_or(vec![]);
let value = Length::parse_non_negative(context, input)?; let length = Length::parse_non_negative(context, input)?;
Ok(SourceSizeOrLength::Length(length))
Ok(Self { source_sizes, value })
} }
} }
impl SourceSizeList {
/// NOTE(emilio): This doesn't match the grammar in the spec, see:
///
/// https://html.spec.whatwg.org/multipage/#parsing-a-sizes-attribute
pub fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Self {
let mut source_sizes = vec![];
loop {
let result = input.parse_until_before(Delimiter::Comma, |input| {
SourceSizeOrLength::parse(context, input)
});
match result {
Ok(SourceSizeOrLength::Length(value)) => {
return Self { source_sizes, value: Some(value) }
}
Ok(SourceSizeOrLength::SourceSize(source_size)) => {
source_sizes.push(source_size);
}
Err(..) => {}
}
match input.next() {
Ok(&Token::Comma) => {},
Err(..) => break,
_ => unreachable!(),
}
}
SourceSizeList { source_sizes, value: None }
}
}
#[cfg(feature = "gecko")]
unsafe impl HasFFI for SourceSizeList {
type FFIType = ::gecko_bindings::structs::RawServoSourceSizeList;
}
#[cfg(feature = "gecko")]
unsafe impl HasSimpleFFI for SourceSizeList {}
#[cfg(feature = "gecko")]
unsafe impl HasBoxFFI for SourceSizeList {}

View file

@ -44,6 +44,7 @@ use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrow
use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
use style::gecko_bindings::bindings::{RawServoSelectorListBorrowed, RawServoSelectorListOwned}; use style::gecko_bindings::bindings::{RawServoSelectorListBorrowed, RawServoSelectorListOwned};
use style::gecko_bindings::bindings::{RawServoSourceSizeListBorrowedOrNull, RawServoSourceSizeListOwned};
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetBorrowedOrNull, RawServoStyleSetOwned}; use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetBorrowedOrNull, RawServoStyleSetOwned};
use style::gecko_bindings::bindings::{RawServoStyleSheetContentsBorrowed, ServoComputedDataBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetContentsBorrowed, ServoComputedDataBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed};
@ -95,6 +96,7 @@ use style::gecko_bindings::structs::OriginFlags_UserAgent;
use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4; use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::RawServoSelectorList; use style::gecko_bindings::structs::RawServoSelectorList;
use style::gecko_bindings::structs::RawServoSourceSizeList;
use style::gecko_bindings::structs::SeenPtrs; use style::gecko_bindings::structs::SeenPtrs;
use style::gecko_bindings::structs::ServoElementSnapshotTable; use style::gecko_bindings::structs::ServoElementSnapshotTable;
use style::gecko_bindings::structs::ServoStyleSetSizes; use style::gecko_bindings::structs::ServoStyleSetSizes;
@ -147,6 +149,7 @@ use style::values::computed::{Context, ToComputedValue};
use style::values::distance::ComputeSquaredDistance; use style::values::distance::ComputeSquaredDistance;
use style::values::specified; use style::values::specified;
use style::values::specified::gecko::IntersectionObserverRootMargin; use style::values::specified::gecko::IntersectionObserverRootMargin;
use style::values::specified::source_size_list::SourceSizeList;
use style_traits::{ParsingMode, ToCss}; use style_traits::{ParsingMode, ToCss};
use super::error_reporter::ErrorReporter; use super::error_reporter::ErrorReporter;
use super::stylesheet_loader::StylesheetLoader; use super::stylesheet_loader::StylesheetLoader;
@ -4515,7 +4518,7 @@ pub unsafe extern "C" fn Servo_SelectorList_Parse(
debug_assert!(!selector_list.is_null()); debug_assert!(!selector_list.is_null());
let input = ::std::str::from_utf8_unchecked(&**selector_list); let input = (*selector_list).as_str_unchecked();
let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input) { let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input) {
Ok(selector_list) => selector_list, Ok(selector_list) => selector_list,
Err(..) => return ptr::null_mut(), Err(..) => return ptr::null_mut(),
@ -4627,3 +4630,50 @@ pub extern "C" fn Servo_ParseIntersectionObserverRootMargin(
Err(..) => false, Err(..) => false,
} }
} }
#[no_mangle]
pub unsafe extern "C" fn Servo_SourceSizeList_Parse(
value: *const nsACString,
) -> *mut RawServoSourceSizeList {
let value = (*value).as_str_unchecked();
let mut input = ParserInput::new(value);
let mut parser = Parser::new(&mut input);
let context = ParserContext::new(
Origin::Author,
dummy_url_data(),
Some(CssRuleType::Style),
ParsingMode::DEFAULT,
QuirksMode::NoQuirks,
);
// NB: Intentionally not calling parse_entirely.
let list = SourceSizeList::parse(&context, &mut parser);
Box::into_raw(Box::new(list)) as *mut _
}
#[no_mangle]
pub unsafe extern "C" fn Servo_SourceSizeList_Evaluate(
raw_data: RawServoStyleSetBorrowed,
list: RawServoSourceSizeListBorrowedOrNull,
) -> i32 {
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let device = doc_data.stylist.device();
let quirks_mode = doc_data.stylist.quirks_mode();
let result = match list {
Some(list) => {
SourceSizeList::from_ffi(list).evaluate(device, quirks_mode)
}
None => {
SourceSizeList::empty().evaluate(device, quirks_mode)
}
};
result.0
}
#[no_mangle]
pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: RawServoSourceSizeListOwned) {
let _ = list.into_box::<SourceSizeList>();
}