tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ui.mako.rs (21601B)


      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 <%namespace name="helpers" file="/helpers.mako.rs" />
      6 
      7 macro_rules! try_parse_one {
      8    ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
      9        if $var.is_none() {
     10            if let Ok(value) = $input.try_parse(|i| {
     11                $prop_module::single_value::parse($context, i)
     12            }) {
     13                $var = Some(value);
     14                continue;
     15            }
     16        }
     17    };
     18 }
     19 
     20 <%helpers:shorthand name="transition"
     21                    engines="gecko servo"
     22                    extra_prefixes="moz:layout.css.prefixes.transitions webkit"
     23                    sub_properties="transition-property transition-duration
     24                                    transition-timing-function
     25                                    transition-delay transition-behavior"
     26                    spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
     27    use crate::parser::Parse;
     28    % for prop in "delay duration property timing_function behavior".split():
     29    use crate::properties::longhands::transition_${prop};
     30    % endfor
     31    use crate::values::specified::TransitionProperty;
     32 
     33    pub fn parse_value<'i, 't>(
     34        context: &ParserContext,
     35        input: &mut Parser<'i, 't>,
     36    ) -> Result<Longhands, ParseError<'i>> {
     37        struct SingleTransition {
     38            % for prop in "property duration timing_function delay behavior".split():
     39            transition_${prop}: transition_${prop}::SingleSpecifiedValue,
     40            % endfor
     41        }
     42 
     43        fn parse_one_transition<'i, 't>(
     44            context: &ParserContext,
     45            input: &mut Parser<'i, 't>,
     46            first: bool,
     47        ) -> Result<SingleTransition,ParseError<'i>> {
     48            % for prop in "property duration timing_function delay behavior".split():
     49            let mut ${prop} = None;
     50            % endfor
     51 
     52            let mut parsed = 0;
     53            loop {
     54                parsed += 1;
     55 
     56                try_parse_one!(context, input, duration, transition_duration);
     57                try_parse_one!(context, input, timing_function, transition_timing_function);
     58                try_parse_one!(context, input, delay, transition_delay);
     59                try_parse_one!(context, input, behavior, transition_behavior);
     60                // Must check 'transition-property' after 'transition-timing-function' since
     61                // 'transition-property' accepts any keyword.
     62                if property.is_none() {
     63                    if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
     64                        property = Some(value);
     65                        continue;
     66                    }
     67 
     68                    // 'none' is not a valid value for <single-transition-property>,
     69                    // so it's only acceptable as the first item.
     70                    if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
     71                        property = Some(TransitionProperty::none());
     72                        continue;
     73                    }
     74                }
     75 
     76                parsed -= 1;
     77                break
     78            }
     79 
     80            if parsed != 0 {
     81                Ok(SingleTransition {
     82                    % for prop in "property duration timing_function delay behavior".split():
     83                    transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
     84                                                                                 ::get_initial_specified_value),
     85                    % endfor
     86                })
     87            } else {
     88                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     89            }
     90        }
     91 
     92        % for prop in "property duration timing_function delay behavior".split():
     93        let mut ${prop}s = Vec::new();
     94        % endfor
     95 
     96        let mut first = true;
     97        let mut has_transition_property_none = false;
     98        let results = input.parse_comma_separated(|i| {
     99            if has_transition_property_none {
    100                // If you specify transition-property: none, multiple items are invalid.
    101                return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    102            }
    103            let transition = parse_one_transition(context, i, first)?;
    104            first = false;
    105            has_transition_property_none = transition.transition_property.is_none();
    106            Ok(transition)
    107        })?;
    108        for result in results {
    109            % for prop in "property duration timing_function delay behavior".split():
    110            ${prop}s.push(result.transition_${prop});
    111            % endfor
    112        }
    113 
    114        Ok(expanded! {
    115            % for prop in "property duration timing_function delay behavior".split():
    116            transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()),
    117            % endfor
    118        })
    119    }
    120 
    121    impl<'a> ToCss for LonghandsToSerialize<'a>  {
    122        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
    123            use crate::Zero;
    124            use style_traits::values::SequenceWriter;
    125 
    126            let property_len = self.transition_property.0.len();
    127 
    128            // There are two cases that we can do shorthand serialization:
    129            // * when all value lists have the same length, or
    130            // * when transition-property is none, and other value lists have exactly one item.
    131            if property_len == 0 {
    132                % for name in "duration delay timing_function".split():
    133                    if self.transition_${name}.0.len() != 1 {
    134                        return Ok(());
    135                    }
    136                % endfor
    137 
    138                if self.transition_behavior.0.len() != 1 {
    139                    return Ok(());
    140                }
    141            } else {
    142                % for name in "duration delay timing_function".split():
    143                    if self.transition_${name}.0.len() != property_len {
    144                        return Ok(());
    145                    }
    146                % endfor
    147 
    148                if self.transition_behavior.0.len() != property_len {
    149                    return Ok(());
    150                }
    151            }
    152 
    153            // Representative length.
    154            let len = self.transition_duration.0.len();
    155 
    156            for i in 0..len {
    157                if i != 0 {
    158                    dest.write_str(", ")?;
    159                }
    160 
    161                let has_duration = !self.transition_duration.0[i].is_zero();
    162                let has_timing = !self.transition_timing_function.0[i].is_ease();
    163                let has_delay = !self.transition_delay.0[i].is_zero();
    164                let has_behavior = !self.transition_behavior.0[i].is_normal();
    165                let has_any = has_duration || has_timing || has_delay || has_behavior;
    166 
    167                let mut writer = SequenceWriter::new(dest, " ");
    168 
    169                if property_len == 0 {
    170                    writer.raw_item("none")?;
    171                } else if !self.transition_property.0[i].is_all() || !has_any {
    172                    writer.item(&self.transition_property.0[i])?;
    173                }
    174 
    175                // In order to avoid ambiguity, we have to serialize duration if we have delay.
    176                if has_duration || has_delay {
    177                    writer.item(&self.transition_duration.0[i])?;
    178                }
    179 
    180                if has_timing {
    181                    writer.item(&self.transition_timing_function.0[i])?;
    182                }
    183 
    184                if has_delay {
    185                    writer.item(&self.transition_delay.0[i])?;
    186                }
    187 
    188                if has_behavior {
    189                    writer.item(&self.transition_behavior.0[i])?;
    190                }
    191            }
    192            Ok(())
    193        }
    194    }
    195 </%helpers:shorthand>
    196 
    197 <%helpers:shorthand name="animation"
    198                    engines="gecko servo"
    199                    extra_prefixes="moz:layout.css.prefixes.animations webkit"
    200                    sub_properties="animation-name animation-duration
    201                                    animation-timing-function animation-delay
    202                                    animation-iteration-count animation-direction
    203                                    animation-fill-mode animation-play-state animation-timeline"
    204                    rule_types_allowed="Style"
    205                    spec="https://drafts.csswg.org/css-animations/#propdef-animation">
    206    <%
    207        props = "name duration timing_function delay iteration_count \
    208                 direction fill_mode play_state".split()
    209    %>
    210    % for prop in props:
    211    use crate::properties::longhands::animation_${prop};
    212    % endfor
    213    use crate::properties::longhands::animation_timeline;
    214 
    215    pub fn parse_value<'i, 't>(
    216        context: &ParserContext,
    217        input: &mut Parser<'i, 't>,
    218    ) -> Result<Longhands, ParseError<'i>> {
    219        struct SingleAnimation {
    220            % for prop in props:
    221            animation_${prop}: animation_${prop}::SingleSpecifiedValue,
    222            % endfor
    223        }
    224 
    225        fn parse_one_animation<'i, 't>(
    226            context: &ParserContext,
    227            input: &mut Parser<'i, 't>,
    228        ) -> Result<SingleAnimation, ParseError<'i>> {
    229            % for prop in props:
    230            let mut ${prop} = None;
    231            % endfor
    232 
    233            let mut parsed = 0;
    234            // NB: Name must be the last one here so that keywords valid for other
    235            // longhands are not interpreted as names.
    236            //
    237            // Also, duration must be before delay, see
    238            // https://drafts.csswg.org/css-animations/#typedef-single-animation
    239            loop {
    240                parsed += 1;
    241                try_parse_one!(context, input, duration, animation_duration);
    242                try_parse_one!(context, input, timing_function, animation_timing_function);
    243                try_parse_one!(context, input, delay, animation_delay);
    244                try_parse_one!(context, input, iteration_count, animation_iteration_count);
    245                try_parse_one!(context, input, direction, animation_direction);
    246                try_parse_one!(context, input, fill_mode, animation_fill_mode);
    247                try_parse_one!(context, input, play_state, animation_play_state);
    248                try_parse_one!(context, input, name, animation_name);
    249 
    250                // Note: per spec issue discussion, all animation longhands not defined in
    251                // Animations 1 are defined as reset-only sub-properties for now.
    252                // https://github.com/w3c/csswg-drafts/issues/6946#issuecomment-1233190360
    253                //
    254                // FIXME: Bug 1824261. We should revisit this when the spec gets updated with the
    255                // new syntax.
    256                // https://github.com/w3c/csswg-drafts/issues/6946
    257 
    258                parsed -= 1;
    259                break
    260            }
    261 
    262            // If nothing is parsed, this is an invalid entry.
    263            if parsed == 0 {
    264                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    265            } else {
    266                Ok(SingleAnimation {
    267                    % for prop in props:
    268                    animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
    269                                                              ::get_initial_specified_value),
    270                    % endfor
    271                })
    272            }
    273        }
    274 
    275        % for prop in props:
    276        let mut ${prop}s = vec![];
    277        % endfor
    278 
    279        let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
    280        for result in results.into_iter() {
    281            % for prop in props:
    282            ${prop}s.push(result.animation_${prop});
    283            % endfor
    284        }
    285 
    286        Ok(expanded! {
    287            % for prop in props:
    288            animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()),
    289            % endfor
    290            // FIXME: Bug 1824261. animation-timeline is reset-only for now.
    291            animation_timeline: animation_timeline::SpecifiedValue(
    292                vec![animation_timeline::single_value::get_initial_specified_value()].into()
    293            ),
    294        })
    295    }
    296 
    297    impl<'a> ToCss for LonghandsToSerialize<'a>  {
    298        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
    299            use crate::values::specified::easing::TimingFunction;
    300            use crate::values::specified::{
    301                AnimationDirection, AnimationFillMode, AnimationPlayState,
    302            };
    303            use crate::Zero;
    304            use style_traits::values::SequenceWriter;
    305 
    306            let len = self.animation_name.0.len();
    307            // There should be at least one declared value
    308            if len == 0 {
    309                return Ok(());
    310            }
    311 
    312            // If any value list length is differs then we don't do a shorthand serialization
    313            // either.
    314            % for name in props[1:]:
    315                if len != self.animation_${name}.0.len() {
    316                    return Ok(())
    317                }
    318            % endfor
    319 
    320            // FIXME: Bug 1824261. We don't serialize this shorthand if the animation-timeline is
    321            // speficied, per the wpt update: https://github.com/web-platform-tests/wpt/pull/38848.
    322            if self.animation_timeline.map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto()) {
    323                return Ok(());
    324            }
    325 
    326            for i in 0..len {
    327                if i != 0 {
    328                    dest.write_str(", ")?;
    329                }
    330 
    331                // We follow the order of this syntax:
    332                // <single-animation> =
    333                //   <animation-duration> ||
    334                //   <easing-function> ||
    335                //   <animation-delay> ||
    336                //   <single-animation-iteration-count> ||
    337                //   <single-animation-direction> ||
    338                //   <single-animation-fill-mode> ||
    339                //   <single-animation-play-state> ||
    340                //   [ none | <keyframes-name> ] ||
    341                //   <single-animation-timeline>
    342                //
    343                // https://drafts.csswg.org/css-animations-2/#animation-shorthand
    344                //
    345                // Note: animation-timeline is not serialized for now because it is always the
    346                // initial value in this loop. Therefore, animation-duration is always resolved as
    347                // 0s if it is auto because animation-timeline is the initial value, i.e.
    348                // time-driven animations. In conclusion, we don't serialize animation-duration if
    349                // it is auto (for specified value) or if it is 0s (for resolved value).
    350                // https://drafts.csswg.org/css-animations-2/#animation-duration
    351                let has_duration = !self.animation_duration.0[i].is_auto()
    352                    && !self.animation_duration.0[i].is_zero();
    353                let has_timing_function = !self.animation_timing_function.0[i].is_ease();
    354                let has_delay = !self.animation_delay.0[i].is_zero();
    355                let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
    356                let has_direction =
    357                    !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
    358                let has_fill_mode =
    359                    !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
    360                let has_play_state =
    361                    !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
    362                let animation_name = &self.animation_name.0[i];
    363                let has_name = !animation_name.is_none();
    364 
    365                let mut writer = SequenceWriter::new(dest, " ");
    366 
    367                // To avoid ambiguity, we have to serialize duration if duration is initial
    368                // but delay is not. (In other words, it's ambiguous if we serialize delay only.)
    369                if has_duration || has_delay {
    370                    writer.item(&self.animation_duration.0[i])?;
    371                }
    372 
    373                if has_timing_function || TimingFunction::match_keywords(animation_name) {
    374                    writer.item(&self.animation_timing_function.0[i])?;
    375                }
    376 
    377                // For animation-delay and animation-iteration-count.
    378                % for name in props[3:5]:
    379                if has_${name} {
    380                    writer.item(&self.animation_${name}.0[i])?;
    381                }
    382                % endfor
    383 
    384                if has_direction || AnimationDirection::match_keywords(animation_name) {
    385                    writer.item(&self.animation_direction.0[i])?;
    386                }
    387 
    388                if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
    389                    writer.item(&self.animation_fill_mode.0[i])?;
    390                }
    391 
    392                if has_play_state || AnimationPlayState::match_keywords(animation_name) {
    393                    writer.item(&self.animation_play_state.0[i])?;
    394                }
    395 
    396                // If all values are initial, we must serialize animation-name.
    397                let has_any = {
    398                    has_duration
    399                % for name in props[2:]:
    400                        || has_${name}
    401                % endfor
    402                };
    403                if has_name || !has_any {
    404                    writer.item(animation_name)?;
    405                }
    406            }
    407            Ok(())
    408        }
    409    }
    410 </%helpers:shorthand>
    411 
    412 <%helpers:shorthand
    413    engines="gecko"
    414    name="scroll-timeline"
    415    sub_properties="scroll-timeline-name scroll-timeline-axis"
    416    gecko_pref="layout.css.scroll-driven-animations.enabled",
    417    spec="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand"
    418 >
    419    pub fn parse_value<'i>(
    420        context: &ParserContext,
    421        input: &mut Parser<'i, '_>,
    422    ) -> Result<Longhands, ParseError<'i>> {
    423        use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};
    424 
    425        let mut names = Vec::with_capacity(1);
    426        let mut axes = Vec::with_capacity(1);
    427        input.parse_comma_separated(|input| {
    428            let name = scroll_timeline_name::single_value::parse(context, input)?;
    429            let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));
    430 
    431            names.push(name);
    432            axes.push(axis.unwrap_or_default());
    433 
    434            Ok(())
    435        })?;
    436 
    437        Ok(expanded! {
    438            scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),
    439            scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),
    440        })
    441    }
    442 
    443    impl<'a> ToCss for LonghandsToSerialize<'a>  {
    444        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
    445            // If any value list length is differs then we don't do a shorthand serialization
    446            // either.
    447            let len = self.scroll_timeline_name.0.len();
    448            if len != self.scroll_timeline_axis.0.len() {
    449                return Ok(());
    450            }
    451 
    452            for i in 0..len {
    453                if i != 0 {
    454                    dest.write_str(", ")?;
    455                }
    456 
    457                self.scroll_timeline_name.0[i].to_css(dest)?;
    458 
    459                if self.scroll_timeline_axis.0[i] != Default::default() {
    460                    dest.write_char(' ')?;
    461                    self.scroll_timeline_axis.0[i].to_css(dest)?;
    462                }
    463 
    464            }
    465            Ok(())
    466        }
    467    }
    468 </%helpers:shorthand>
    469 
    470 // Note: view-timeline shorthand doesn't take view-timeline-inset into account.
    471 <%helpers:shorthand
    472    engines="gecko"
    473    name="view-timeline"
    474    sub_properties="view-timeline-name view-timeline-axis"
    475    gecko_pref="layout.css.scroll-driven-animations.enabled",
    476    spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand"
    477 >
    478    pub fn parse_value<'i>(
    479        context: &ParserContext,
    480        input: &mut Parser<'i, '_>,
    481    ) -> Result<Longhands, ParseError<'i>> {
    482        use crate::properties::longhands::{view_timeline_axis, view_timeline_name};
    483 
    484        let mut names = Vec::with_capacity(1);
    485        let mut axes = Vec::with_capacity(1);
    486        input.parse_comma_separated(|input| {
    487            let name = view_timeline_name::single_value::parse(context, input)?;
    488            let axis = input.try_parse(|i| view_timeline_axis::single_value::parse(context, i));
    489 
    490            names.push(name);
    491            axes.push(axis.unwrap_or_default());
    492 
    493            Ok(())
    494        })?;
    495 
    496        Ok(expanded! {
    497            view_timeline_name: view_timeline_name::SpecifiedValue(names.into()),
    498            view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()),
    499        })
    500    }
    501 
    502    impl<'a> ToCss for LonghandsToSerialize<'a>  {
    503        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
    504            // If any value list length is differs then we don't do a shorthand serialization
    505            // either.
    506            let len = self.view_timeline_name.0.len();
    507            if len != self.view_timeline_axis.0.len() {
    508                return Ok(());
    509            }
    510 
    511            for i in 0..len {
    512                if i != 0 {
    513                    dest.write_str(", ")?;
    514                }
    515 
    516                self.view_timeline_name.0[i].to_css(dest)?;
    517 
    518                if self.view_timeline_axis.0[i] != Default::default() {
    519                    dest.write_char(' ')?;
    520                    self.view_timeline_axis.0[i].to_css(dest)?;
    521                }
    522 
    523            }
    524            Ok(())
    525        }
    526    }
    527 </%helpers:shorthand>