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 ];