1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Helper types for the `@viewport` rule. 6 7 use crate::{CSSPixel, CssWriter, ParseError, PinchZoomFactor, ToCss}; 8 use cssparser::Parser; 9 use euclid::Size2D; 10 use std::fmt::{self, Write}; 11 12 define_css_keyword_enum! { 13 pub enum UserZoom { 14 Zoom = "zoom", 15 Fixed = "fixed", 16 } 17 } 18 19 define_css_keyword_enum! { 20 pub enum Orientation { 21 Auto = "auto", 22 Portrait = "portrait", 23 Landscape = "landscape", 24 } 25 } 26 27 /// A set of viewport descriptors: 28 /// 29 /// <https://drafts.csswg.org/css-device-adapt/#viewport-desc> 30 #[derive(Clone, Debug, PartialEq)] 31 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, MallocSizeOf))] 32 pub struct ViewportConstraints { 33 /// Width and height: 34 /// * https://drafts.csswg.org/css-device-adapt/#width-desc 35 /// * https://drafts.csswg.org/css-device-adapt/#height-desc 36 pub size: Size2D<f32, CSSPixel>, 37 /// <https://drafts.csswg.org/css-device-adapt/#zoom-desc> 38 pub initial_zoom: PinchZoomFactor, 39 /// <https://drafts.csswg.org/css-device-adapt/#min-max-width-desc> 40 pub min_zoom: Option<PinchZoomFactor>, 41 /// <https://drafts.csswg.org/css-device-adapt/#min-max-width-desc> 42 pub max_zoom: Option<PinchZoomFactor>, 43 /// <https://drafts.csswg.org/css-device-adapt/#user-zoom-desc> 44 pub user_zoom: UserZoom, 45 /// <https://drafts.csswg.org/css-device-adapt/#orientation-desc> 46 pub orientation: Orientation, 47 } 48 49 impl ToCss for ViewportConstraints { to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,50 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 51 where 52 W: Write, 53 { 54 dest.write_str("@viewport { width: ")?; 55 self.size.width.to_css(dest)?; 56 57 dest.write_str("px; height: ")?; 58 self.size.height.to_css(dest)?; 59 60 dest.write_str("px; zoom: ")?; 61 self.initial_zoom.get().to_css(dest)?; 62 63 if let Some(min_zoom) = self.min_zoom { 64 dest.write_str("; min-zoom: ")?; 65 min_zoom.get().to_css(dest)?; 66 } 67 68 if let Some(max_zoom) = self.max_zoom { 69 dest.write_str("; max-zoom: ")?; 70 max_zoom.get().to_css(dest)?; 71 } 72 73 dest.write_str("; user-zoom: ")?; 74 self.user_zoom.to_css(dest)?; 75 76 dest.write_str("; orientation: ")?; 77 self.orientation.to_css(dest)?; 78 dest.write_str("; }") 79 } 80 } 81 82 /// <https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom> 83 #[derive(Clone, Copy, Debug, PartialEq, ToShmem)] 84 #[cfg_attr(feature = "servo", derive(MallocSizeOf))] 85 pub enum Zoom { 86 /// A number value. 87 Number(f32), 88 /// A percentage value. 89 Percentage(f32), 90 /// The `auto` keyword. 91 Auto, 92 } 93 94 impl ToCss for Zoom { to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,95 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 96 where 97 W: fmt::Write, 98 { 99 match *self { 100 Zoom::Number(number) => number.to_css(dest), 101 Zoom::Auto => dest.write_str("auto"), 102 Zoom::Percentage(percentage) => { 103 (percentage * 100.).to_css(dest)?; 104 dest.write_char('%') 105 }, 106 } 107 } 108 } 109 110 impl Zoom { 111 /// Parse a zoom value per: 112 /// 113 /// <https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom> parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Zoom, ParseError<'i>>114 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Zoom, ParseError<'i>> { 115 use crate::values::specified::AllowedNumericType::NonNegative; 116 use crate::ParsingMode; 117 use cssparser::Token; 118 119 let location = input.current_source_location(); 120 match *input.next()? { 121 // TODO: This parse() method should take ParserContext as an 122 // argument, and pass ParsingMode owned by the ParserContext to 123 // is_ok() instead of using ParsingMode::DEFAULT directly. 124 // In order to do so, we might want to move these stuff into style::stylesheets::viewport_rule. 125 Token::Percentage { unit_value, .. } 126 if NonNegative.is_ok(ParsingMode::DEFAULT, unit_value) => 127 { 128 Ok(Zoom::Percentage(unit_value)) 129 }, 130 Token::Number { value, .. } if NonNegative.is_ok(ParsingMode::DEFAULT, value) => { 131 Ok(Zoom::Number(value)) 132 }, 133 Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => Ok(Zoom::Auto), 134 ref t => Err(location.new_unexpected_token_error(t.clone())), 135 } 136 } 137 138 /// Get this zoom value as a float value. Returns `None` if the value is the 139 /// `auto` keyword. 140 #[inline] to_f32(&self) -> Option<f32>141 pub fn to_f32(&self) -> Option<f32> { 142 match *self { 143 Zoom::Number(number) => Some(number as f32), 144 Zoom::Percentage(percentage) => Some(percentage as f32), 145 Zoom::Auto => None, 146 } 147 } 148 } 149