tor-browser

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

media_features.rs (31392B)


      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 //! Gecko's media feature list and evaluator.
      6 
      7 use crate::derives::*;
      8 use crate::gecko_bindings::bindings;
      9 use crate::gecko_bindings::structs;
     10 use crate::media_queries::{Device, MediaType};
     11 use crate::parser::ParserContext;
     12 use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
     13 use crate::queries::values::{Orientation, PrefersColorScheme};
     14 use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution};
     15 use crate::values::specified::color::ForcedColors;
     16 use app_units::Au;
     17 use euclid::default::Size2D;
     18 
     19 fn device_size(device: &Device) -> Size2D<Au> {
     20    let mut width = 0;
     21    let mut height = 0;
     22    unsafe {
     23        bindings::Gecko_MediaFeatures_GetDeviceSize(device.document(), &mut width, &mut height);
     24    }
     25    Size2D::new(Au(width), Au(height))
     26 }
     27 
     28 /// https://drafts.csswg.org/mediaqueries-4/#width
     29 fn eval_width(context: &Context) -> CSSPixelLength {
     30    CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())
     31 }
     32 
     33 /// https://drafts.csswg.org/mediaqueries-4/#device-width
     34 fn eval_device_width(context: &Context) -> CSSPixelLength {
     35    CSSPixelLength::new(device_size(context.device()).width.to_f32_px())
     36 }
     37 
     38 /// https://drafts.csswg.org/mediaqueries-4/#height
     39 fn eval_height(context: &Context) -> CSSPixelLength {
     40    CSSPixelLength::new(context.device().au_viewport_size().height.to_f32_px())
     41 }
     42 
     43 /// https://drafts.csswg.org/mediaqueries-4/#device-height
     44 fn eval_device_height(context: &Context) -> CSSPixelLength {
     45    CSSPixelLength::new(device_size(context.device()).height.to_f32_px())
     46 }
     47 
     48 fn eval_aspect_ratio_for<F>(context: &Context, get_size: F) -> Ratio
     49 where
     50    F: FnOnce(&Device) -> Size2D<Au>,
     51 {
     52    let size = get_size(context.device());
     53    Ratio::new(size.width.0 as f32, size.height.0 as f32)
     54 }
     55 
     56 /// https://drafts.csswg.org/mediaqueries-4/#aspect-ratio
     57 fn eval_aspect_ratio(context: &Context) -> Ratio {
     58    eval_aspect_ratio_for(context, Device::au_viewport_size)
     59 }
     60 
     61 /// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio
     62 fn eval_device_aspect_ratio(context: &Context) -> Ratio {
     63    eval_aspect_ratio_for(context, device_size)
     64 }
     65 
     66 /// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio
     67 fn eval_device_pixel_ratio(context: &Context) -> f32 {
     68    eval_resolution(context).dppx()
     69 }
     70 
     71 /// https://drafts.csswg.org/mediaqueries-4/#orientation
     72 fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
     73    Orientation::eval(context.device().au_viewport_size(), value)
     74 }
     75 
     76 /// FIXME: There's no spec for `-moz-device-orientation`.
     77 fn eval_device_orientation(context: &Context, value: Option<Orientation>) -> bool {
     78    Orientation::eval(device_size(context.device()), value)
     79 }
     80 
     81 fn document_picture_in_picture_enabled(_: &ParserContext) -> bool {
     82    static_prefs::pref!("dom.documentpip.enabled")
     83 }
     84 
     85 /// Values for the display-mode media feature.
     86 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
     87 #[repr(u8)]
     88 #[allow(missing_docs)]
     89 pub enum DisplayMode {
     90    Browser = 0,
     91    MinimalUi,
     92    Standalone,
     93    Fullscreen,
     94    #[parse(condition = "document_picture_in_picture_enabled")]
     95    PictureInPicture,
     96 }
     97 
     98 /// https://w3c.github.io/manifest/#the-display-mode-media-feature
     99 fn eval_display_mode(context: &Context, query_value: Option<DisplayMode>) -> bool {
    100    match query_value {
    101        Some(v) => {
    102            v == unsafe {
    103                bindings::Gecko_MediaFeatures_GetDisplayMode(context.device().document())
    104            }
    105        },
    106        None => true,
    107    }
    108 }
    109 
    110 /// https://drafts.csswg.org/mediaqueries-4/#grid
    111 fn eval_grid(_: &Context) -> bool {
    112    // Gecko doesn't support grid devices (e.g., ttys), so the 'grid' feature
    113    // is always 0.
    114    false
    115 }
    116 
    117 /// https://compat.spec.whatwg.org/#css-media-queries-webkit-transform-3d
    118 fn eval_transform_3d(_: &Context) -> bool {
    119    true
    120 }
    121 
    122 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    123 #[repr(u8)]
    124 enum Scan {
    125    Progressive,
    126    Interlace,
    127 }
    128 
    129 /// https://drafts.csswg.org/mediaqueries-4/#scan
    130 fn eval_scan(_: &Context, _: Option<Scan>) -> bool {
    131    // Since Gecko doesn't support the 'tv' media type, the 'scan' feature never
    132    // matches.
    133    false
    134 }
    135 
    136 /// https://drafts.csswg.org/mediaqueries-4/#color
    137 fn eval_color(context: &Context) -> i32 {
    138    unsafe { bindings::Gecko_MediaFeatures_GetColorDepth(context.device().document()) }
    139 }
    140 
    141 /// https://drafts.csswg.org/mediaqueries-4/#color-index
    142 fn eval_color_index(_: &Context) -> i32 {
    143    // We should return zero if the device does not use a color lookup table.
    144    0
    145 }
    146 
    147 /// https://drafts.csswg.org/mediaqueries-4/#monochrome
    148 fn eval_monochrome(context: &Context) -> i32 {
    149    // For color devices we should return 0.
    150    unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(context.device().document()) }
    151 }
    152 
    153 /// Values for the color-gamut media feature.
    154 /// This implements PartialOrd so that lower values will correctly match
    155 /// higher capabilities.
    156 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]
    157 #[repr(u8)]
    158 pub enum ColorGamut {
    159    /// The sRGB gamut.
    160    Srgb,
    161    /// The gamut specified by the Display P3 Color Space.
    162    P3,
    163    /// The gamut specified by the ITU-R Recommendation BT.2020 Color Space.
    164    Rec2020,
    165 }
    166 
    167 /// https://drafts.csswg.org/mediaqueries-4/#color-gamut
    168 fn eval_color_gamut(context: &Context, query_value: Option<ColorGamut>) -> bool {
    169    let query_value = match query_value {
    170        Some(v) => v,
    171        None => return false,
    172    };
    173    let color_gamut =
    174        unsafe { bindings::Gecko_MediaFeatures_ColorGamut(context.device().document()) };
    175    // Match if our color gamut is at least as wide as the query value
    176    query_value <= color_gamut
    177 }
    178 
    179 /// https://drafts.csswg.org/mediaqueries-4/#resolution
    180 fn eval_resolution(context: &Context) -> Resolution {
    181    let resolution_dppx =
    182        unsafe { bindings::Gecko_MediaFeatures_GetResolution(context.device().document()) };
    183    Resolution::from_dppx(resolution_dppx)
    184 }
    185 
    186 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    187 #[repr(u8)]
    188 enum PrefersReducedMotion {
    189    NoPreference,
    190    Reduce,
    191 }
    192 
    193 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    194 #[repr(u8)]
    195 enum PrefersReducedTransparency {
    196    NoPreference,
    197    Reduce,
    198 }
    199 
    200 /// Values for the dynamic-range and video-dynamic-range media features.
    201 /// https://drafts.csswg.org/mediaqueries-5/#dynamic-range
    202 /// This implements PartialOrd so that lower values will correctly match
    203 /// higher capabilities.
    204 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]
    205 #[repr(u8)]
    206 #[allow(missing_docs)]
    207 pub enum DynamicRange {
    208    Standard,
    209    High,
    210 }
    211 
    212 /// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion
    213 fn eval_prefers_reduced_motion(
    214    context: &Context,
    215    query_value: Option<PrefersReducedMotion>,
    216 ) -> bool {
    217    let prefers_reduced =
    218        unsafe { bindings::Gecko_MediaFeatures_PrefersReducedMotion(context.device().document()) };
    219    let query_value = match query_value {
    220        Some(v) => v,
    221        None => return prefers_reduced,
    222    };
    223 
    224    match query_value {
    225        PrefersReducedMotion::NoPreference => !prefers_reduced,
    226        PrefersReducedMotion::Reduce => prefers_reduced,
    227    }
    228 }
    229 
    230 /// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency
    231 fn eval_prefers_reduced_transparency(
    232    context: &Context,
    233    query_value: Option<PrefersReducedTransparency>,
    234 ) -> bool {
    235    let prefers_reduced = unsafe {
    236        bindings::Gecko_MediaFeatures_PrefersReducedTransparency(context.device().document())
    237    };
    238    let query_value = match query_value {
    239        Some(v) => v,
    240        None => return prefers_reduced,
    241    };
    242 
    243    match query_value {
    244        PrefersReducedTransparency::NoPreference => !prefers_reduced,
    245        PrefersReducedTransparency::Reduce => prefers_reduced,
    246    }
    247 }
    248 
    249 /// Possible values for prefers-contrast media query.
    250 /// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
    251 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
    252 #[repr(u8)]
    253 pub enum PrefersContrast {
    254    /// More contrast is preferred.
    255    More,
    256    /// Low contrast is preferred.
    257    Less,
    258    /// Custom (not more, not less).
    259    Custom,
    260    /// The default value if neither high or low contrast is enabled.
    261    NoPreference,
    262 }
    263 
    264 /// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
    265 fn eval_prefers_contrast(context: &Context, query_value: Option<PrefersContrast>) -> bool {
    266    let prefers_contrast =
    267        unsafe { bindings::Gecko_MediaFeatures_PrefersContrast(context.device().document()) };
    268    match query_value {
    269        Some(v) => v == prefers_contrast,
    270        None => prefers_contrast != PrefersContrast::NoPreference,
    271    }
    272 }
    273 
    274 /// https://drafts.csswg.org/mediaqueries-5/#forced-colors
    275 fn eval_forced_colors(context: &Context, query_value: Option<ForcedColors>) -> bool {
    276    let forced = context.device().forced_colors();
    277    match query_value {
    278        Some(query_value) => query_value == forced,
    279        None => forced != ForcedColors::None,
    280    }
    281 }
    282 
    283 /// Possible values for the inverted-colors media query.
    284 /// https://drafts.csswg.org/mediaqueries-5/#inverted
    285 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    286 #[repr(u8)]
    287 enum InvertedColors {
    288    /// Colors are displayed normally.
    289    None,
    290    /// All pixels within the displayed area have been inverted.
    291    Inverted,
    292 }
    293 
    294 /// https://drafts.csswg.org/mediaqueries-5/#inverted
    295 fn eval_inverted_colors(context: &Context, query_value: Option<InvertedColors>) -> bool {
    296    let inverted_colors =
    297        unsafe { bindings::Gecko_MediaFeatures_InvertedColors(context.device().document()) };
    298    let query_value = match query_value {
    299        Some(v) => v,
    300        None => return inverted_colors,
    301    };
    302 
    303    match query_value {
    304        InvertedColors::None => !inverted_colors,
    305        InvertedColors::Inverted => inverted_colors,
    306    }
    307 }
    308 
    309 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    310 #[repr(u8)]
    311 enum OverflowBlock {
    312    None,
    313    Scroll,
    314    Paged,
    315 }
    316 
    317 /// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-block
    318 fn eval_overflow_block(context: &Context, query_value: Option<OverflowBlock>) -> bool {
    319    // For the time being, assume that printing (including previews)
    320    // is the only time when we paginate, and we are otherwise always
    321    // scrolling. This is true at the moment in Firefox, but may need
    322    // updating in the future (e.g., ebook readers built with Stylo, a
    323    // billboard mode that doesn't support overflow at all).
    324    //
    325    // If this ever changes, don't forget to change eval_overflow_inline too.
    326    let scrolling = context.device().media_type() != MediaType::print();
    327    let query_value = match query_value {
    328        Some(v) => v,
    329        None => return true,
    330    };
    331 
    332    match query_value {
    333        OverflowBlock::None => false,
    334        OverflowBlock::Scroll => scrolling,
    335        OverflowBlock::Paged => !scrolling,
    336    }
    337 }
    338 
    339 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    340 #[repr(u8)]
    341 enum OverflowInline {
    342    None,
    343    Scroll,
    344 }
    345 
    346 /// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-inline
    347 fn eval_overflow_inline(context: &Context, query_value: Option<OverflowInline>) -> bool {
    348    // See the note in eval_overflow_block.
    349    let scrolling = context.device().media_type() != MediaType::print();
    350    let query_value = match query_value {
    351        Some(v) => v,
    352        None => return scrolling,
    353    };
    354 
    355    match query_value {
    356        OverflowInline::None => !scrolling,
    357        OverflowInline::Scroll => scrolling,
    358    }
    359 }
    360 
    361 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    362 #[repr(u8)]
    363 enum Update {
    364    None,
    365    Slow,
    366    Fast,
    367 }
    368 
    369 /// https://drafts.csswg.org/mediaqueries-4/#update
    370 fn eval_update(context: &Context, query_value: Option<Update>) -> bool {
    371    // This has similar caveats to those described in eval_overflow_block.
    372    // For now, we report that print (incl. print media simulation,
    373    // which can in fact update but is limited to the developer tools)
    374    // is `update: none` and that all other contexts are `update: fast`,
    375    // which may not be true for future platforms, like e-ink devices.
    376    let can_update = context.device().media_type() != MediaType::print();
    377    let query_value = match query_value {
    378        Some(v) => v,
    379        None => return can_update,
    380    };
    381 
    382    match query_value {
    383        Update::None => !can_update,
    384        Update::Slow => false,
    385        Update::Fast => can_update,
    386    }
    387 }
    388 
    389 fn do_eval_prefers_color_scheme(
    390    context: &Context,
    391    use_content: bool,
    392    query_value: Option<PrefersColorScheme>,
    393 ) -> bool {
    394    let prefers_color_scheme = unsafe {
    395        bindings::Gecko_MediaFeatures_PrefersColorScheme(context.device().document(), use_content)
    396    };
    397    match query_value {
    398        Some(v) => prefers_color_scheme == v,
    399        None => true,
    400    }
    401 }
    402 
    403 /// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
    404 fn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {
    405    do_eval_prefers_color_scheme(context, /* use_content = */ false, query_value)
    406 }
    407 
    408 fn eval_content_prefers_color_scheme(
    409    context: &Context,
    410    query_value: Option<PrefersColorScheme>,
    411 ) -> bool {
    412    do_eval_prefers_color_scheme(context, /* use_content = */ true, query_value)
    413 }
    414 
    415 /// https://drafts.csswg.org/mediaqueries-5/#dynamic-range
    416 fn eval_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {
    417    let dynamic_range =
    418        unsafe { bindings::Gecko_MediaFeatures_DynamicRange(context.device().document()) };
    419    match query_value {
    420        Some(v) => dynamic_range >= v,
    421        None => false,
    422    }
    423 }
    424 /// https://drafts.csswg.org/mediaqueries-5/#video-dynamic-range
    425 fn eval_video_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {
    426    let dynamic_range =
    427        unsafe { bindings::Gecko_MediaFeatures_VideoDynamicRange(context.device().document()) };
    428    match query_value {
    429        Some(v) => dynamic_range >= v,
    430        None => false,
    431    }
    432 }
    433 
    434 bitflags! {
    435    /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
    436    struct PointerCapabilities: u8 {
    437        const COARSE = structs::PointerCapabilities_Coarse;
    438        const FINE = structs::PointerCapabilities_Fine;
    439        const HOVER = structs::PointerCapabilities_Hover;
    440    }
    441 }
    442 
    443 fn primary_pointer_capabilities(context: &Context) -> PointerCapabilities {
    444    PointerCapabilities::from_bits_truncate(unsafe {
    445        bindings::Gecko_MediaFeatures_PrimaryPointerCapabilities(context.device().document())
    446    })
    447 }
    448 
    449 fn all_pointer_capabilities(context: &Context) -> PointerCapabilities {
    450    PointerCapabilities::from_bits_truncate(unsafe {
    451        bindings::Gecko_MediaFeatures_AllPointerCapabilities(context.device().document())
    452    })
    453 }
    454 
    455 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    456 #[repr(u8)]
    457 enum Pointer {
    458    None,
    459    Coarse,
    460    Fine,
    461 }
    462 
    463 fn eval_pointer_capabilities(
    464    query_value: Option<Pointer>,
    465    pointer_capabilities: PointerCapabilities,
    466 ) -> bool {
    467    let query_value = match query_value {
    468        Some(v) => v,
    469        None => return !pointer_capabilities.is_empty(),
    470    };
    471 
    472    match query_value {
    473        Pointer::None => pointer_capabilities.is_empty(),
    474        Pointer::Coarse => pointer_capabilities.intersects(PointerCapabilities::COARSE),
    475        Pointer::Fine => pointer_capabilities.intersects(PointerCapabilities::FINE),
    476    }
    477 }
    478 
    479 /// https://drafts.csswg.org/mediaqueries-4/#pointer
    480 fn eval_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
    481    eval_pointer_capabilities(query_value, primary_pointer_capabilities(context))
    482 }
    483 
    484 /// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-pointer
    485 fn eval_any_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
    486    eval_pointer_capabilities(query_value, all_pointer_capabilities(context))
    487 }
    488 
    489 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    490 #[repr(u8)]
    491 enum Hover {
    492    None,
    493    Hover,
    494 }
    495 
    496 fn eval_hover_capabilities(
    497    query_value: Option<Hover>,
    498    pointer_capabilities: PointerCapabilities,
    499 ) -> bool {
    500    let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER);
    501    let query_value = match query_value {
    502        Some(v) => v,
    503        None => return can_hover,
    504    };
    505 
    506    match query_value {
    507        Hover::None => !can_hover,
    508        Hover::Hover => can_hover,
    509    }
    510 }
    511 
    512 /// https://drafts.csswg.org/mediaqueries-4/#hover
    513 fn eval_hover(context: &Context, query_value: Option<Hover>) -> bool {
    514    eval_hover_capabilities(query_value, primary_pointer_capabilities(context))
    515 }
    516 
    517 /// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-hover
    518 fn eval_any_hover(context: &Context, query_value: Option<Hover>) -> bool {
    519    eval_hover_capabilities(query_value, all_pointer_capabilities(context))
    520 }
    521 
    522 fn eval_moz_is_glyph(context: &Context) -> bool {
    523    context.device().document().mIsSVGGlyphsDocument()
    524 }
    525 
    526 fn eval_moz_in_android_pip_mode(context: &Context) -> bool {
    527    unsafe { bindings::Gecko_MediaFeatures_InAndroidPipMode(context.device().document()) }
    528 }
    529 
    530 fn eval_moz_print_preview(context: &Context) -> bool {
    531    let is_print_preview = context.device().is_print_preview();
    532    if is_print_preview {
    533        debug_assert_eq!(context.device().media_type(), MediaType::print());
    534    }
    535    is_print_preview
    536 }
    537 
    538 fn eval_moz_is_resource_document(context: &Context) -> bool {
    539    unsafe { bindings::Gecko_MediaFeatures_IsResourceDocument(context.device().document()) }
    540 }
    541 
    542 /// Allows front-end CSS to discern platform via media queries.
    543 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
    544 #[repr(u8)]
    545 pub enum Platform {
    546    /// Matches any Android version.
    547    Android,
    548    /// For our purposes here, "linux" is just "gtk" (so unix-but-not-mac).
    549    /// There's no need for our front-end code to differentiate between those
    550    /// platforms and they already use the "linux" string elsewhere (e.g.,
    551    /// toolkit/themes/linux).
    552    Linux,
    553    /// Matches any iOS version.
    554    Ios,
    555    /// Matches any macOS version.
    556    Macos,
    557    /// Matches any Windows version.
    558    Windows,
    559 }
    560 
    561 fn eval_moz_platform(_: &Context, query_value: Option<Platform>) -> bool {
    562    let query_value = match query_value {
    563        Some(v) => v,
    564        None => return false,
    565    };
    566 
    567    unsafe { bindings::Gecko_MediaFeatures_MatchesPlatform(query_value) }
    568 }
    569 
    570 /// Allows front-end CSS to discern gtk theme via media queries.
    571 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
    572 #[repr(u8)]
    573 pub enum GtkThemeFamily {
    574    /// Unknown theme family.
    575    Unknown = 0,
    576    /// Adwaita, the default GTK theme.
    577    Adwaita,
    578    /// Breeze, the default KDE theme.
    579    Breeze,
    580    /// Yaru, the default Ubuntu theme.
    581    Yaru,
    582 }
    583 
    584 fn eval_gtk_theme_family(_: &Context, query_value: Option<GtkThemeFamily>) -> bool {
    585    let family = unsafe { bindings::Gecko_MediaFeatures_GtkThemeFamily() };
    586    match query_value {
    587        Some(v) => v == family,
    588        None => return family != GtkThemeFamily::Unknown,
    589    }
    590 }
    591 
    592 /// Values for the scripting media feature.
    593 /// https://drafts.csswg.org/mediaqueries-5/#scripting
    594 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
    595 #[repr(u8)]
    596 pub enum Scripting {
    597    /// Scripting is not supported or not enabled
    598    None,
    599    /// Scripting is supported and enabled, but only for initial page load
    600    /// We will never match this value as it is intended for non-browser user agents,
    601    /// but it is part of the spec so we should still parse it.
    602    /// See: https://github.com/w3c/csswg-drafts/issues/8621
    603    InitialOnly,
    604    /// Scripting is supported and enabled
    605    Enabled,
    606 }
    607 
    608 /// https://drafts.csswg.org/mediaqueries-5/#scripting
    609 fn eval_scripting(context: &Context, query_value: Option<Scripting>) -> bool {
    610    let scripting = unsafe { bindings::Gecko_MediaFeatures_Scripting(context.device().document()) };
    611    match query_value {
    612        Some(v) => v == scripting,
    613        None => scripting != Scripting::None,
    614    }
    615 }
    616 
    617 fn eval_moz_overlay_scrollbars(context: &Context) -> bool {
    618    unsafe { bindings::Gecko_MediaFeatures_UseOverlayScrollbars(context.device().document()) }
    619 }
    620 
    621 fn eval_moz_mac_rtl(context: &Context) -> bool {
    622    unsafe { bindings::Gecko_MediaFeatures_MacRTL(context.device().document()) }
    623 }
    624 
    625 fn eval_moz_native_theme(context: &Context) -> bool {
    626    if context.device().document().mForceNonNativeTheme() {
    627        return false;
    628    }
    629    static_prefs::pref!("browser.theme.native-theme")
    630 }
    631 
    632 fn get_lnf_int(int_id: i32) -> i32 {
    633    unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
    634 }
    635 
    636 fn get_lnf_int_as_bool(int_id: i32) -> bool {
    637    get_lnf_int(int_id) != 0
    638 }
    639 
    640 macro_rules! lnf_int_feature {
    641    ($feature_name:expr, $int_id:ident, $get_value:ident) => {{
    642        fn __eval(_: &Context) -> bool {
    643            $get_value(bindings::LookAndFeel_IntID::$int_id as i32)
    644        }
    645 
    646        feature!(
    647            $feature_name,
    648            AllowsRanges::No,
    649            Evaluator::BoolInteger(__eval),
    650            FeatureFlags::CHROME_AND_UA_ONLY,
    651        )
    652    }};
    653    ($feature_name:expr, $int_id:ident) => {{
    654        lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool)
    655    }};
    656 }
    657 
    658 /// Adding new media features requires (1) adding the new feature to this
    659 /// array, with appropriate entries (and potentially any new code needed
    660 /// to support new types in these entries and (2) ensuring that either
    661 /// nsPresContext::MediaFeatureValuesChanged is called when the value that
    662 /// would be returned by the evaluator function could change.
    663 pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [
    664    feature!(
    665        atom!("width"),
    666        AllowsRanges::Yes,
    667        Evaluator::Length(eval_width),
    668        FeatureFlags::VIEWPORT_DEPENDENT,
    669    ),
    670    feature!(
    671        atom!("height"),
    672        AllowsRanges::Yes,
    673        Evaluator::Length(eval_height),
    674        FeatureFlags::VIEWPORT_DEPENDENT,
    675    ),
    676    feature!(
    677        atom!("aspect-ratio"),
    678        AllowsRanges::Yes,
    679        Evaluator::NumberRatio(eval_aspect_ratio),
    680        FeatureFlags::VIEWPORT_DEPENDENT,
    681    ),
    682    feature!(
    683        atom!("orientation"),
    684        AllowsRanges::No,
    685        keyword_evaluator!(eval_orientation, Orientation),
    686        FeatureFlags::VIEWPORT_DEPENDENT,
    687    ),
    688    feature!(
    689        atom!("device-width"),
    690        AllowsRanges::Yes,
    691        Evaluator::Length(eval_device_width),
    692        FeatureFlags::empty(),
    693    ),
    694    feature!(
    695        atom!("device-height"),
    696        AllowsRanges::Yes,
    697        Evaluator::Length(eval_device_height),
    698        FeatureFlags::empty(),
    699    ),
    700    feature!(
    701        atom!("device-aspect-ratio"),
    702        AllowsRanges::Yes,
    703        Evaluator::NumberRatio(eval_device_aspect_ratio),
    704        FeatureFlags::empty(),
    705    ),
    706    feature!(
    707        atom!("-moz-device-orientation"),
    708        AllowsRanges::No,
    709        keyword_evaluator!(eval_device_orientation, Orientation),
    710        FeatureFlags::empty(),
    711    ),
    712    // Webkit extensions that we support for de-facto web compatibility.
    713    // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):
    714    feature!(
    715        atom!("device-pixel-ratio"),
    716        AllowsRanges::Yes,
    717        Evaluator::Float(eval_device_pixel_ratio),
    718        FeatureFlags::WEBKIT_PREFIX,
    719    ),
    720    // -webkit-transform-3d.
    721    feature!(
    722        atom!("transform-3d"),
    723        AllowsRanges::No,
    724        Evaluator::BoolInteger(eval_transform_3d),
    725        FeatureFlags::WEBKIT_PREFIX,
    726    ),
    727    feature!(
    728        atom!("-moz-device-pixel-ratio"),
    729        AllowsRanges::Yes,
    730        Evaluator::Float(eval_device_pixel_ratio),
    731        FeatureFlags::empty(),
    732    ),
    733    feature!(
    734        atom!("resolution"),
    735        AllowsRanges::Yes,
    736        Evaluator::Resolution(eval_resolution),
    737        FeatureFlags::empty(),
    738    ),
    739    feature!(
    740        atom!("display-mode"),
    741        AllowsRanges::No,
    742        keyword_evaluator!(eval_display_mode, DisplayMode),
    743        FeatureFlags::empty(),
    744    ),
    745    feature!(
    746        atom!("grid"),
    747        AllowsRanges::No,
    748        Evaluator::BoolInteger(eval_grid),
    749        FeatureFlags::empty(),
    750    ),
    751    feature!(
    752        atom!("scan"),
    753        AllowsRanges::No,
    754        keyword_evaluator!(eval_scan, Scan),
    755        FeatureFlags::empty(),
    756    ),
    757    feature!(
    758        atom!("color"),
    759        AllowsRanges::Yes,
    760        Evaluator::Integer(eval_color),
    761        FeatureFlags::empty(),
    762    ),
    763    feature!(
    764        atom!("color-index"),
    765        AllowsRanges::Yes,
    766        Evaluator::Integer(eval_color_index),
    767        FeatureFlags::empty(),
    768    ),
    769    feature!(
    770        atom!("monochrome"),
    771        AllowsRanges::Yes,
    772        Evaluator::Integer(eval_monochrome),
    773        FeatureFlags::empty(),
    774    ),
    775    feature!(
    776        atom!("color-gamut"),
    777        AllowsRanges::No,
    778        keyword_evaluator!(eval_color_gamut, ColorGamut),
    779        FeatureFlags::empty(),
    780    ),
    781    feature!(
    782        atom!("prefers-reduced-motion"),
    783        AllowsRanges::No,
    784        keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
    785        FeatureFlags::empty(),
    786    ),
    787    feature!(
    788        atom!("prefers-reduced-transparency"),
    789        AllowsRanges::No,
    790        keyword_evaluator!(
    791            eval_prefers_reduced_transparency,
    792            PrefersReducedTransparency
    793        ),
    794        FeatureFlags::empty(),
    795    ),
    796    feature!(
    797        atom!("prefers-contrast"),
    798        AllowsRanges::No,
    799        keyword_evaluator!(eval_prefers_contrast, PrefersContrast),
    800        FeatureFlags::empty(),
    801    ),
    802    feature!(
    803        atom!("forced-colors"),
    804        AllowsRanges::No,
    805        keyword_evaluator!(eval_forced_colors, ForcedColors),
    806        FeatureFlags::empty(),
    807    ),
    808    feature!(
    809        atom!("inverted-colors"),
    810        AllowsRanges::No,
    811        keyword_evaluator!(eval_inverted_colors, InvertedColors),
    812        FeatureFlags::empty(),
    813    ),
    814    feature!(
    815        atom!("overflow-block"),
    816        AllowsRanges::No,
    817        keyword_evaluator!(eval_overflow_block, OverflowBlock),
    818        FeatureFlags::empty(),
    819    ),
    820    feature!(
    821        atom!("overflow-inline"),
    822        AllowsRanges::No,
    823        keyword_evaluator!(eval_overflow_inline, OverflowInline),
    824        FeatureFlags::empty(),
    825    ),
    826    feature!(
    827        atom!("update"),
    828        AllowsRanges::No,
    829        keyword_evaluator!(eval_update, Update),
    830        FeatureFlags::empty(),
    831    ),
    832    feature!(
    833        atom!("prefers-color-scheme"),
    834        AllowsRanges::No,
    835        keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),
    836        FeatureFlags::empty(),
    837    ),
    838    feature!(
    839        atom!("dynamic-range"),
    840        AllowsRanges::No,
    841        keyword_evaluator!(eval_dynamic_range, DynamicRange),
    842        FeatureFlags::empty(),
    843    ),
    844    feature!(
    845        atom!("video-dynamic-range"),
    846        AllowsRanges::No,
    847        keyword_evaluator!(eval_video_dynamic_range, DynamicRange),
    848        FeatureFlags::empty(),
    849    ),
    850    feature!(
    851        atom!("scripting"),
    852        AllowsRanges::No,
    853        keyword_evaluator!(eval_scripting, Scripting),
    854        FeatureFlags::empty(),
    855    ),
    856    // Evaluates to the preferred color scheme for content. Only useful in
    857    // chrome context, where the chrome color-scheme and the content
    858    // color-scheme might differ.
    859    feature!(
    860        atom!("-moz-content-prefers-color-scheme"),
    861        AllowsRanges::No,
    862        keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme),
    863        FeatureFlags::CHROME_AND_UA_ONLY,
    864    ),
    865    feature!(
    866        atom!("pointer"),
    867        AllowsRanges::No,
    868        keyword_evaluator!(eval_pointer, Pointer),
    869        FeatureFlags::empty(),
    870    ),
    871    feature!(
    872        atom!("any-pointer"),
    873        AllowsRanges::No,
    874        keyword_evaluator!(eval_any_pointer, Pointer),
    875        FeatureFlags::empty(),
    876    ),
    877    feature!(
    878        atom!("hover"),
    879        AllowsRanges::No,
    880        keyword_evaluator!(eval_hover, Hover),
    881        FeatureFlags::empty(),
    882    ),
    883    feature!(
    884        atom!("any-hover"),
    885        AllowsRanges::No,
    886        keyword_evaluator!(eval_any_hover, Hover),
    887        FeatureFlags::empty(),
    888    ),
    889    // Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
    890    // Internal because it is really only useful in the user agent anyway
    891    // and therefore not worth standardizing.
    892    feature!(
    893        atom!("-moz-is-glyph"),
    894        AllowsRanges::No,
    895        Evaluator::BoolInteger(eval_moz_is_glyph),
    896        FeatureFlags::CHROME_AND_UA_ONLY,
    897    ),
    898    feature!(
    899        atom!("-moz-in-android-pip-mode"),
    900        AllowsRanges::No,
    901        Evaluator::BoolInteger(eval_moz_in_android_pip_mode),
    902        FeatureFlags::CHROME_AND_UA_ONLY,
    903    ),
    904    feature!(
    905        atom!("-moz-is-resource-document"),
    906        AllowsRanges::No,
    907        Evaluator::BoolInteger(eval_moz_is_resource_document),
    908        FeatureFlags::CHROME_AND_UA_ONLY,
    909    ),
    910    feature!(
    911        atom!("-moz-platform"),
    912        AllowsRanges::No,
    913        keyword_evaluator!(eval_moz_platform, Platform),
    914        FeatureFlags::CHROME_AND_UA_ONLY,
    915    ),
    916    feature!(
    917        atom!("-moz-gtk-theme-family"),
    918        AllowsRanges::No,
    919        keyword_evaluator!(eval_gtk_theme_family, GtkThemeFamily),
    920        FeatureFlags::CHROME_AND_UA_ONLY,
    921    ),
    922    feature!(
    923        atom!("-moz-print-preview"),
    924        AllowsRanges::No,
    925        Evaluator::BoolInteger(eval_moz_print_preview),
    926        FeatureFlags::CHROME_AND_UA_ONLY,
    927    ),
    928    feature!(
    929        atom!("-moz-overlay-scrollbars"),
    930        AllowsRanges::No,
    931        Evaluator::BoolInteger(eval_moz_overlay_scrollbars),
    932        FeatureFlags::CHROME_AND_UA_ONLY,
    933    ),
    934    lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag),
    935    lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme),
    936    lnf_int_feature!(atom!("-moz-mac-tahoe-theme"), MacTahoeTheme),
    937    feature!(
    938        atom!("-moz-mac-rtl"),
    939        AllowsRanges::No,
    940        Evaluator::BoolInteger(eval_moz_mac_rtl),
    941        FeatureFlags::CHROME_AND_UA_ONLY,
    942    ),
    943    feature!(
    944        atom!("-moz-native-theme"),
    945        AllowsRanges::No,
    946        Evaluator::BoolInteger(eval_moz_native_theme),
    947        FeatureFlags::CHROME_AND_UA_ONLY,
    948    ),
    949    lnf_int_feature!(
    950        atom!("-moz-windows-accent-color-in-titlebar"),
    951        WindowsAccentColorInTitlebar
    952    ),
    953    lnf_int_feature!(atom!("-moz-windows-mica"), WindowsMica),
    954    lnf_int_feature!(atom!("-moz-windows-mica-popups"), WindowsMicaPopups),
    955    lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled),
    956    lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable),
    957    lnf_int_feature!(
    958        atom!("-moz-gtk-csd-transparency-available"),
    959        GTKCSDTransparencyAvailable
    960    ),
    961    lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton),
    962    lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton),
    963    lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
    964    lnf_int_feature!(
    965        atom!("-moz-gtk-csd-reversed-placement"),
    966        GTKCSDReversedPlacement
    967    ),
    968    lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
    969    lnf_int_feature!(atom!("-moz-panel-animations"), PanelAnimations),
    970 ];