mirror of
https://github.com/servo/servo.git
synced 2025-06-24 00:54:32 +01:00
Implement constraining as per DEVICE-ADAPT § 6
Spec: http://dev.w3.org/csswg/css-device-adapt/#constraining
This commit is contained in:
parent
3b14c07051
commit
8977316d3e
4 changed files with 280 additions and 0 deletions
|
@ -38,3 +38,4 @@ url = "0.2.16"
|
|||
mod_path = "0.1"
|
||||
bitflags = "*"
|
||||
cssparser = "0.3.1"
|
||||
num = "0.1.24"
|
||||
|
|
|
@ -34,6 +34,7 @@ extern crate selectors;
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate num;
|
||||
extern crate util;
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use cssparser::{Parser, DeclarationListParser, AtRuleParser, DeclarationParser, ToCss, parse_important};
|
||||
use geom::size::{Size2D, TypedSize2D};
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use parser::{ParserContext, log_css_error};
|
||||
use properties::longhands;
|
||||
use stylesheets::Origin;
|
||||
use util::geometry::{Au, PagePx, ViewportPx};
|
||||
use values::specified::{AllowedNumericType, Length, LengthOrPercentageOrAuto};
|
||||
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -300,3 +304,206 @@ impl<'a, I> ViewportDescriptorDeclarationCascade for I
|
|||
cascade(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ViewportConstraints {
|
||||
pub size: TypedSize2D<ViewportPx, f32>,
|
||||
|
||||
pub initial_zoom: ScaleFactor<PagePx, ViewportPx, f32>,
|
||||
pub min_zoom: Option<ScaleFactor<PagePx, ViewportPx, f32>>,
|
||||
pub max_zoom: Option<ScaleFactor<PagePx, ViewportPx, f32>>,
|
||||
|
||||
pub user_zoom: UserZoom,
|
||||
pub orientation: Orientation
|
||||
}
|
||||
|
||||
impl ToCss for ViewportConstraints {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write
|
||||
{
|
||||
try!(write!(dest, "@viewport {{"));
|
||||
try!(write!(dest, " width: {}px;", self.size.width.get()));
|
||||
try!(write!(dest, " height: {}px;", self.size.height.get()));
|
||||
try!(write!(dest, " zoom: {};", self.initial_zoom.get()));
|
||||
if let Some(min_zoom) = self.min_zoom {
|
||||
try!(write!(dest, " min-zoom: {};", min_zoom.get()));
|
||||
}
|
||||
if let Some(max_zoom) = self.max_zoom {
|
||||
try!(write!(dest, " max-zoom: {};", max_zoom.get()));
|
||||
}
|
||||
try!(write!(dest, " user-zoom: ")); try!(self.user_zoom.to_css(dest));
|
||||
try!(write!(dest, "; orientation: ")); try!(self.orientation.to_css(dest));
|
||||
write!(dest, "; }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewportConstraints {
|
||||
pub fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>,
|
||||
rule: &ViewportRule)
|
||||
-> Option<ViewportConstraints>
|
||||
{
|
||||
use std::cmp;
|
||||
use num::{Float, ToPrimitive};
|
||||
|
||||
if rule.declarations.is_empty() {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut min_width = None;
|
||||
let mut max_width = None;
|
||||
|
||||
let mut min_height = None;
|
||||
let mut max_height = None;
|
||||
|
||||
let mut initial_zoom = None;
|
||||
let mut min_zoom = None;
|
||||
let mut max_zoom = None;
|
||||
|
||||
let mut user_zoom = UserZoom::Zoom;
|
||||
let mut orientation = Orientation::Auto;
|
||||
|
||||
// collapse the list of declarations into descriptor values
|
||||
for declaration in rule.declarations.iter() {
|
||||
match declaration.descriptor {
|
||||
ViewportDescriptor::MinWidth(value) => min_width = Some(value),
|
||||
ViewportDescriptor::MaxWidth(value) => max_width = Some(value),
|
||||
|
||||
ViewportDescriptor::MinHeight(value) => min_height = Some(value),
|
||||
ViewportDescriptor::MaxHeight(value) => max_height = Some(value),
|
||||
|
||||
ViewportDescriptor::Zoom(value) => initial_zoom = value.to_f32(),
|
||||
ViewportDescriptor::MinZoom(value) => min_zoom = value.to_f32(),
|
||||
ViewportDescriptor::MaxZoom(value) => max_zoom = value.to_f32(),
|
||||
|
||||
ViewportDescriptor::UserZoom(value) => user_zoom = value,
|
||||
ViewportDescriptor::Orientation(value) => orientation = value
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: return `None` if all descriptors are either absent or initial value
|
||||
|
||||
macro_rules! choose {
|
||||
($op:ident, $opta:expr, $optb:expr) => {
|
||||
match ($opta, $optb) {
|
||||
(None, None) => None,
|
||||
(a, None) => a.clone(),
|
||||
(None, b) => b.clone(),
|
||||
(a, b) => Some(a.clone().unwrap().$op(b.clone().unwrap())),
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! min {
|
||||
($opta:expr, $optb:expr) => {
|
||||
choose!(min, $opta, $optb)
|
||||
}
|
||||
}
|
||||
macro_rules! max {
|
||||
($opta:expr, $optb:expr) => {
|
||||
choose!(max, $opta, $optb)
|
||||
}
|
||||
}
|
||||
|
||||
// DEVICE-ADAPT § 6.2.1 Resolve min-zoom and max-zoom values
|
||||
if min_zoom.is_some() && max_zoom.is_some() {
|
||||
max_zoom = Some(min_zoom.clone().unwrap().max(max_zoom.unwrap()))
|
||||
}
|
||||
|
||||
// DEVICE-ADAPT § 6.2.2 Constrain zoom value to the [min-zoom, max-zoom] range
|
||||
if initial_zoom.is_some() {
|
||||
initial_zoom = max!(min_zoom, min!(max_zoom, initial_zoom));
|
||||
}
|
||||
|
||||
// DEVICE-ADAPT § 6.2.3 Resolve non-auto lengths to pixel lengths
|
||||
//
|
||||
// Note: DEVICE-ADAPT § 5. states that relative length values are
|
||||
// resolved against initial values
|
||||
let initial_viewport = Size2D(Au::from_f32_px(initial_viewport.width.get()),
|
||||
Au::from_f32_px(initial_viewport.height.get()));
|
||||
|
||||
macro_rules! to_pixel_length {
|
||||
($value:ident, $dimension:ident) => {
|
||||
if let Some($value) = $value {
|
||||
match $value {
|
||||
LengthOrPercentageOrAuto::Length(ref value) => Some(match value {
|
||||
&Length::Absolute(length) => length,
|
||||
&Length::FontRelative(length) => {
|
||||
let initial_font_size = longhands::font_size::get_initial_value();
|
||||
length.to_computed_value(initial_font_size, initial_font_size)
|
||||
}
|
||||
&Length::ViewportPercentage(length) =>
|
||||
length.to_computed_value(initial_viewport),
|
||||
_ => unreachable!()
|
||||
}),
|
||||
LengthOrPercentageOrAuto::Percentage(value) => Some(initial_viewport.$dimension.scale_by(value)),
|
||||
LengthOrPercentageOrAuto::Auto => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let min_width = to_pixel_length!(min_width, width);
|
||||
let max_width = to_pixel_length!(max_width, width);
|
||||
let min_height = to_pixel_length!(min_height, height);
|
||||
let max_height = to_pixel_length!(max_height, height);
|
||||
|
||||
// DEVICE-ADAPT § 6.2.4 Resolve initial width and height from min/max descriptors
|
||||
macro_rules! resolve {
|
||||
($min:ident, $max:ident, $initial:expr) => {
|
||||
if $min.is_some() || $max.is_some() {
|
||||
let max = match $max {
|
||||
Some(max) => cmp::min(max, $initial),
|
||||
None => $initial
|
||||
};
|
||||
|
||||
Some(match $min {
|
||||
Some(min) => cmp::max(min, max),
|
||||
None => max
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let width = resolve!(min_width, max_width, initial_viewport.width);
|
||||
let height = resolve!(min_height, max_height, initial_viewport.height);
|
||||
|
||||
// DEVICE-ADAPT § 6.2.5 Resolve width value
|
||||
let width = if width.is_none() && height.is_none() {
|
||||
Some(initial_viewport.width)
|
||||
} else {
|
||||
width
|
||||
};
|
||||
|
||||
let width = width.unwrap_or_else(|| match initial_viewport.height {
|
||||
Au(0) => initial_viewport.width,
|
||||
initial_height => {
|
||||
let ratio = initial_viewport.width.to_f32_px() / initial_height.to_f32_px();
|
||||
Au::from_f32_px(height.clone().unwrap().to_f32_px() * ratio)
|
||||
}
|
||||
});
|
||||
|
||||
// DEVICE-ADAPT § 6.2.6 Resolve height value
|
||||
let height = height.unwrap_or_else(|| match initial_viewport.width {
|
||||
Au(0) => initial_viewport.height,
|
||||
initial_width => {
|
||||
let ratio = initial_viewport.height.to_f32_px() / initial_width.to_f32_px();
|
||||
Au::from_f32_px(width.to_f32_px() * ratio)
|
||||
}
|
||||
});
|
||||
|
||||
Some(ViewportConstraints {
|
||||
size: TypedSize2D(width.to_f32_px(), height.to_f32_px()),
|
||||
|
||||
// TODO: compute a zoom factor for 'auto' as suggested by DEVICE-ADAPT § 10.
|
||||
initial_zoom: ScaleFactor::new(initial_zoom.unwrap_or(1.)),
|
||||
min_zoom: min_zoom.map(ScaleFactor::new),
|
||||
max_zoom: max_zoom.map(ScaleFactor::new),
|
||||
|
||||
user_zoom: user_zoom,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue