media_queries.rs (28832B)
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-query device and expression representation. 6 7 use crate::color::AbsoluteColor; 8 use crate::context::QuirksMode; 9 use crate::custom_properties::CssEnvironment; 10 use crate::font_metrics::FontMetrics; 11 use crate::gecko::values::{convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color}; 12 use crate::gecko_bindings::bindings; 13 use crate::gecko_bindings::structs; 14 use crate::logical_geometry::WritingMode; 15 use crate::media_queries::MediaType; 16 use crate::properties::ComputedValues; 17 use crate::string_cache::Atom; 18 use crate::values::computed::font::GenericFontFamily; 19 use crate::values::computed::{ColorScheme, Length, NonNegativeLength}; 20 use crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor}; 21 use crate::values::specified::font::{ 22 QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX, 23 FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX, 24 }; 25 use crate::values::specified::ViewportVariant; 26 use crate::values::{CustomIdent, KeyframesName}; 27 use app_units::{Au, AU_PER_PX}; 28 use euclid::default::Size2D; 29 use euclid::{Scale, SideOffsets2D}; 30 use parking_lot::RwLock; 31 use servo_arc::Arc; 32 use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; 33 use std::{cmp, fmt, mem}; 34 use style_traits::{CSSPixel, DevicePixel}; 35 36 /// The `Device` in Gecko wraps a pres context, has a default values computed, 37 /// and contains all the viewport rule state. 38 /// 39 /// This structure also contains atomics used for computing root font-relative 40 /// units. These atomics use relaxed ordering, since when computing the style 41 /// of the root element, there can't be any other style being computed at the 42 /// same time (given we need the style of the parent to compute everything else). 43 pub struct Device { 44 /// NB: The document owns the styleset, who owns the stylist, and thus the 45 /// `Device`, so having a raw document pointer here is fine. 46 document: *const structs::Document, 47 default_values: Arc<ComputedValues>, 48 /// Current computed style of the root element, used for calculations of 49 /// root font-relative units. 50 root_style: RwLock<Arc<ComputedValues>>, 51 /// Font size of the root element, used for rem units in other elements. 52 root_font_size: AtomicU32, 53 /// Line height of the root element, used for rlh units in other elements. 54 root_line_height: AtomicU32, 55 /// X-height of the root element, used for rex units in other elements. 56 root_font_metrics_ex: AtomicU32, 57 /// Cap-height of the root element, used for rcap units in other elements. 58 root_font_metrics_cap: AtomicU32, 59 /// Advance measure (ch) of the root element, used for rch units in other elements. 60 root_font_metrics_ch: AtomicU32, 61 /// Ideographic advance measure of the root element, used for ric units in other elements. 62 root_font_metrics_ic: AtomicU32, 63 /// The body text color, stored as an `nscolor`, used for the "tables 64 /// inherit from body" quirk. 65 /// 66 /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk> 67 body_text_color: AtomicUsize, 68 /// Whether any styles computed in the document relied on the root font-size 69 /// by using rem units. 70 used_root_font_size: AtomicBool, 71 /// Whether any styles computed in the document relied on the root line-height 72 /// by using rlh units. 73 used_root_line_height: AtomicBool, 74 /// Whether any styles computed in the document relied on the root font metrics 75 /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic 76 /// in order to prevent concurrent writes to the root font metric values. 77 used_root_font_metrics: RwLock<bool>, 78 /// Whether any styles computed in the document relied on font metrics. 79 used_font_metrics: AtomicBool, 80 /// Whether any styles computed in the document relied on the viewport size 81 /// by using vw/vh/vmin/vmax units. 82 used_viewport_size: AtomicBool, 83 /// Whether any styles computed in the document relied on the viewport size 84 /// by using dvw/dvh/dvmin/dvmax units. 85 used_dynamic_viewport_size: AtomicBool, 86 /// The CssEnvironment object responsible of getting CSS environment 87 /// variables. 88 environment: CssEnvironment, 89 } 90 91 impl fmt::Debug for Device { 92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 93 use nsstring::nsCString; 94 95 let mut doc_uri = nsCString::new(); 96 unsafe { 97 bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri) 98 }; 99 100 f.debug_struct("Device") 101 .field("document_url", &doc_uri) 102 .finish() 103 } 104 } 105 106 unsafe impl Sync for Device {} 107 unsafe impl Send for Device {} 108 109 impl Device { 110 /// Trivially constructs a new `Device`. 111 pub fn new(document: *const structs::Document) -> Self { 112 assert!(!document.is_null()); 113 let doc = unsafe { &*document }; 114 let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) }; 115 let default_values = ComputedValues::default_values(doc); 116 let root_style = RwLock::new(Arc::clone(&default_values)); 117 Device { 118 document, 119 default_values: default_values, 120 root_style: root_style, 121 root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), 122 root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()), 123 root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()), 124 root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()), 125 root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()), 126 root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()), 127 128 // This gets updated when we see the <body>, so it doesn't really 129 // matter which color-scheme we look at here. 130 body_text_color: AtomicUsize::new(prefs.mLightColors.mDefault as usize), 131 used_root_font_size: AtomicBool::new(false), 132 used_root_line_height: AtomicBool::new(false), 133 used_root_font_metrics: RwLock::new(false), 134 used_font_metrics: AtomicBool::new(false), 135 used_viewport_size: AtomicBool::new(false), 136 used_dynamic_viewport_size: AtomicBool::new(false), 137 environment: CssEnvironment, 138 } 139 } 140 141 /// Get the relevant environment to resolve `env()` functions. 142 #[inline] 143 pub fn environment(&self) -> &CssEnvironment { 144 &self.environment 145 } 146 147 /// Returns the computed line-height for the font in a given computed values instance. 148 /// 149 /// If you pass down an element, then the used line-height is returned. 150 pub fn calc_line_height( 151 &self, 152 font: &crate::properties::style_structs::Font, 153 writing_mode: WritingMode, 154 element: Option<super::wrapper::GeckoElement>, 155 ) -> NonNegativeLength { 156 let pres_context = self.pres_context(); 157 let line_height = font.clone_line_height(); 158 let au = Au(unsafe { 159 bindings::Gecko_CalcLineHeight( 160 &line_height, 161 pres_context.map_or(std::ptr::null(), |pc| pc), 162 writing_mode.is_text_vertical(), 163 &**font, 164 element.map_or(std::ptr::null(), |e| e.0), 165 ) 166 }); 167 NonNegativeLength::new(au.to_f32_px()) 168 } 169 170 /// Whether any animation name may be referenced from the style of any 171 /// element. 172 pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool { 173 let pc = match self.pres_context() { 174 Some(pc) => pc, 175 None => return false, 176 }; 177 178 unsafe { 179 bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr()) 180 } 181 } 182 183 /// Returns the default computed values as a reference, in order to match 184 /// Servo. 185 pub fn default_computed_values(&self) -> &ComputedValues { 186 &self.default_values 187 } 188 189 /// Returns the default computed values as an `Arc`. 190 pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> { 191 &self.default_values 192 } 193 194 /// Store a pointer to the root element's computed style, for use in 195 /// calculation of root font-relative metrics. 196 pub fn set_root_style(&self, style: &Arc<ComputedValues>) { 197 *self.root_style.write() = style.clone(); 198 } 199 200 /// Get the font size of the root element (for rem) 201 pub fn root_font_size(&self) -> Length { 202 self.used_root_font_size.store(true, Ordering::Relaxed); 203 Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) 204 } 205 206 /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. 207 pub fn set_root_font_size(&self, size: f32) { 208 self.root_font_size.store(size.to_bits(), Ordering::Relaxed) 209 } 210 211 /// Get the line height of the root element (for rlh) 212 pub fn root_line_height(&self) -> Length { 213 self.used_root_line_height.store(true, Ordering::Relaxed); 214 Length::new(f32::from_bits( 215 self.root_line_height.load(Ordering::Relaxed), 216 )) 217 } 218 219 /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. 220 pub fn set_root_line_height(&self, size: f32) { 221 self.root_line_height 222 .store(size.to_bits(), Ordering::Relaxed); 223 } 224 225 /// Get the x-height of the root element (for rex) 226 pub fn root_font_metrics_ex(&self) -> Length { 227 self.ensure_root_font_metrics_updated(); 228 Length::new(f32::from_bits( 229 self.root_font_metrics_ex.load(Ordering::Relaxed), 230 )) 231 } 232 233 /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels. 234 pub fn set_root_font_metrics_ex(&self, size: f32) -> bool { 235 let size = size.to_bits(); 236 let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed); 237 previous != size 238 } 239 240 /// Get the cap-height of the root element (for rcap) 241 pub fn root_font_metrics_cap(&self) -> Length { 242 self.ensure_root_font_metrics_updated(); 243 Length::new(f32::from_bits( 244 self.root_font_metrics_cap.load(Ordering::Relaxed), 245 )) 246 } 247 248 /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels. 249 pub fn set_root_font_metrics_cap(&self, size: f32) -> bool { 250 let size = size.to_bits(); 251 let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed); 252 previous != size 253 } 254 255 /// Get the advance measure of the root element (for rch) 256 pub fn root_font_metrics_ch(&self) -> Length { 257 self.ensure_root_font_metrics_updated(); 258 Length::new(f32::from_bits( 259 self.root_font_metrics_ch.load(Ordering::Relaxed), 260 )) 261 } 262 263 /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels. 264 pub fn set_root_font_metrics_ch(&self, size: f32) -> bool { 265 let size = size.to_bits(); 266 let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed); 267 previous != size 268 } 269 270 /// Get the ideographic advance measure of the root element (for ric) 271 pub fn root_font_metrics_ic(&self) -> Length { 272 self.ensure_root_font_metrics_updated(); 273 Length::new(f32::from_bits( 274 self.root_font_metrics_ic.load(Ordering::Relaxed), 275 )) 276 } 277 278 /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels. 279 pub fn set_root_font_metrics_ic(&self, size: f32) -> bool { 280 let size = size.to_bits(); 281 let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed); 282 previous != size 283 } 284 285 /// The quirks mode of the document. 286 pub fn quirks_mode(&self) -> QuirksMode { 287 self.document().mCompatMode.into() 288 } 289 290 /// Sets the body text color for the "inherit color from body" quirk. 291 /// 292 /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk> 293 pub fn set_body_text_color(&self, color: AbsoluteColor) { 294 self.body_text_color.store( 295 convert_absolute_color_to_nscolor(&color) as usize, 296 Ordering::Relaxed, 297 ) 298 } 299 300 /// Gets the base size given a generic font family and a language. 301 pub fn base_size_for_generic(&self, language: &Atom, generic: GenericFontFamily) -> Length { 302 unsafe { bindings::Gecko_GetBaseSize(self.document(), language.as_ptr(), generic) } 303 } 304 305 /// Gets the size of the scrollbar in CSS pixels. 306 pub fn scrollbar_inline_size(&self) -> Length { 307 let pc = match self.pres_context() { 308 Some(pc) => pc, 309 // XXX: we could have a more reasonable default perhaps. 310 None => return Length::new(0.0), 311 }; 312 Length::new(unsafe { bindings::Gecko_GetScrollbarInlineSize(pc) }) 313 } 314 315 /// Queries font metrics 316 pub fn query_font_metrics( 317 &self, 318 vertical: bool, 319 font: &crate::properties::style_structs::Font, 320 base_size: Length, 321 flags: QueryFontMetricsFlags, 322 track_usage: bool, 323 ) -> FontMetrics { 324 if track_usage { 325 self.used_font_metrics.store(true, Ordering::Relaxed); 326 } 327 let pc = match self.pres_context() { 328 Some(pc) => pc, 329 None => return Default::default(), 330 }; 331 let gecko_metrics = 332 unsafe { bindings::Gecko_GetFontMetrics(pc, vertical, &**font, base_size, flags) }; 333 FontMetrics { 334 x_height: Some(gecko_metrics.mXSize), 335 zero_advance_measure: if gecko_metrics.mChSize.px() >= 0. { 336 Some(gecko_metrics.mChSize) 337 } else { 338 None 339 }, 340 cap_height: if gecko_metrics.mCapHeight.px() >= 0. { 341 Some(gecko_metrics.mCapHeight) 342 } else { 343 None 344 }, 345 ic_width: if gecko_metrics.mIcWidth.px() >= 0. { 346 Some(gecko_metrics.mIcWidth) 347 } else { 348 None 349 }, 350 ascent: gecko_metrics.mAscent, 351 script_percent_scale_down: if gecko_metrics.mScriptPercentScaleDown > 0. { 352 Some(gecko_metrics.mScriptPercentScaleDown) 353 } else { 354 None 355 }, 356 script_script_percent_scale_down: if gecko_metrics.mScriptScriptPercentScaleDown > 0. { 357 Some(gecko_metrics.mScriptScriptPercentScaleDown) 358 } else { 359 None 360 }, 361 } 362 } 363 364 fn ensure_root_font_metrics_updated(&self) { 365 let mut guard = self.used_root_font_metrics.write(); 366 let previously_computed = mem::replace(&mut *guard, true); 367 if !previously_computed { 368 self.update_root_font_metrics(); 369 } 370 } 371 372 /// Compute the root element's font metrics, and returns a bool indicating whether 373 /// the font metrics have changed since the previous restyle. 374 pub fn update_root_font_metrics(&self) -> bool { 375 let root_style = self.root_style.read(); 376 let root_effective_zoom = (*root_style).effective_zoom; 377 let root_font_size = (*root_style).get_font().clone_font_size().computed_size(); 378 379 let root_font_metrics = self.query_font_metrics( 380 (*root_style).writing_mode.is_upright(), 381 &(*root_style).get_font(), 382 root_font_size, 383 QueryFontMetricsFlags::USE_USER_FONT_SET 384 | QueryFontMetricsFlags::NEEDS_CH 385 | QueryFontMetricsFlags::NEEDS_IC, 386 /* track_usage = */ false, 387 ); 388 389 let mut root_font_metrics_changed = false; 390 root_font_metrics_changed |= self.set_root_font_metrics_ex( 391 root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()), 392 ); 393 root_font_metrics_changed |= self.set_root_font_metrics_ch( 394 root_effective_zoom.unzoom( 395 root_font_metrics 396 .zero_advance_measure_or_default( 397 root_font_size, 398 (*root_style).writing_mode.is_upright(), 399 ) 400 .px(), 401 ), 402 ); 403 root_font_metrics_changed |= self.set_root_font_metrics_cap( 404 root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()), 405 ); 406 root_font_metrics_changed |= self.set_root_font_metrics_ic( 407 root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()), 408 ); 409 410 root_font_metrics_changed 411 } 412 413 /// Returns the body text color. 414 pub fn body_text_color(&self) -> AbsoluteColor { 415 convert_nscolor_to_absolute_color(self.body_text_color.load(Ordering::Relaxed) as u32) 416 } 417 418 /// Gets the document pointer. 419 #[inline] 420 pub fn document(&self) -> &structs::Document { 421 unsafe { &*self.document } 422 } 423 424 /// Gets the pres context associated with this document. 425 #[inline] 426 pub fn pres_context(&self) -> Option<&structs::nsPresContext> { 427 unsafe { 428 self.document() 429 .mPresShell 430 .as_ref()? 431 .mPresContext 432 .mRawPtr 433 .as_ref() 434 } 435 } 436 437 /// Gets the preference stylesheet prefs for our document. 438 #[inline] 439 pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs { 440 unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) } 441 } 442 443 /// Recreates the default computed values. 444 pub fn reset_computed_values(&mut self) { 445 self.default_values = ComputedValues::default_values(self.document()); 446 } 447 448 /// Rebuild all the cached data. 449 pub fn rebuild_cached_data(&mut self) { 450 self.reset_computed_values(); 451 self.used_root_font_size.store(false, Ordering::Relaxed); 452 self.used_root_line_height.store(false, Ordering::Relaxed); 453 self.used_root_font_metrics = RwLock::new(false); 454 self.used_font_metrics.store(false, Ordering::Relaxed); 455 self.used_viewport_size.store(false, Ordering::Relaxed); 456 self.used_dynamic_viewport_size 457 .store(false, Ordering::Relaxed); 458 } 459 460 /// Returns whether we ever looked up the root font size of the device. 461 pub fn used_root_font_size(&self) -> bool { 462 self.used_root_font_size.load(Ordering::Relaxed) 463 } 464 465 /// Returns whether we ever looked up the root line-height of the device. 466 pub fn used_root_line_height(&self) -> bool { 467 self.used_root_line_height.load(Ordering::Relaxed) 468 } 469 470 /// Returns whether we ever looked up the root font metrics of the device. 471 pub fn used_root_font_metrics(&self) -> bool { 472 *self.used_root_font_metrics.read() 473 } 474 475 /// Recreates all the temporary state that the `Device` stores. 476 /// 477 /// This includes the viewport override from `@viewport` rules, and also the 478 /// default computed values. 479 pub fn reset(&mut self) { 480 self.reset_computed_values(); 481 } 482 483 /// Returns whether this document is in print preview. 484 pub fn is_print_preview(&self) -> bool { 485 let pc = match self.pres_context() { 486 Some(pc) => pc, 487 None => return false, 488 }; 489 pc.mType == structs::nsPresContext_nsPresContextType_eContext_PrintPreview 490 } 491 492 /// Returns the current media type of the device. 493 pub fn media_type(&self) -> MediaType { 494 let pc = match self.pres_context() { 495 Some(pc) => pc, 496 None => return MediaType::screen(), 497 }; 498 499 // Gecko allows emulating random media with mMediaEmulationData.mMedium. 500 let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() { 501 pc.mMediaEmulationData.mMedium.mRawPtr 502 } else { 503 pc.mMedium as *const structs::nsAtom as *mut _ 504 }; 505 506 MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) })) 507 } 508 509 // It may make sense to account for @page rule margins here somehow, however 510 // it's not clear how that'd work, see: 511 // https://github.com/w3c/csswg-drafts/issues/5437 512 fn page_size_minus_default_margin(&self, pc: &structs::nsPresContext) -> Size2D<Au> { 513 debug_assert!(pc.mIsRootPaginatedDocument() != 0); 514 let area = &pc.mPageSize; 515 let margin = &pc.mDefaultPageMargin; 516 let width = area.width - margin.left - margin.right; 517 let height = area.height - margin.top - margin.bottom; 518 Size2D::new(Au(cmp::max(width, 0)), Au(cmp::max(height, 0))) 519 } 520 521 /// Returns the current viewport size in app units. 522 pub fn au_viewport_size(&self) -> Size2D<Au> { 523 let pc = match self.pres_context() { 524 Some(pc) => pc, 525 None => return Size2D::new(Au(0), Au(0)), 526 }; 527 528 if pc.mIsRootPaginatedDocument() != 0 { 529 return self.page_size_minus_default_margin(pc); 530 } 531 532 let area = &pc.mVisibleArea; 533 Size2D::new(Au(area.width), Au(area.height)) 534 } 535 536 /// Returns the current viewport size in app units, recording that it's been 537 /// used for viewport unit resolution. 538 pub fn au_viewport_size_for_viewport_unit_resolution( 539 &self, 540 variant: ViewportVariant, 541 ) -> Size2D<Au> { 542 self.used_viewport_size.store(true, Ordering::Relaxed); 543 let pc = match self.pres_context() { 544 Some(pc) => pc, 545 None => return Size2D::new(Au(0), Au(0)), 546 }; 547 548 if pc.mIsRootPaginatedDocument() != 0 { 549 return self.page_size_minus_default_margin(pc); 550 } 551 552 match variant { 553 ViewportVariant::UADefault => { 554 let size = &pc.mSizeForViewportUnits; 555 Size2D::new(Au(size.width), Au(size.height)) 556 }, 557 ViewportVariant::Small => { 558 let size = &pc.mVisibleArea; 559 Size2D::new(Au(size.width), Au(size.height)) 560 }, 561 ViewportVariant::Large => { 562 let size = &pc.mVisibleArea; 563 // Looks like IntCoordTyped is treated as if it's u32 in Rust. 564 debug_assert!( 565 /* pc.mDynamicToolbarMaxHeight >=0 && */ 566 pc.mDynamicToolbarMaxHeight < i32::MAX as u32 567 ); 568 Size2D::new( 569 Au(size.width), 570 Au(size.height 571 + pc.mDynamicToolbarMaxHeight as i32 * pc.mCurAppUnitsPerDevPixel), 572 ) 573 }, 574 ViewportVariant::Dynamic => { 575 self.used_dynamic_viewport_size 576 .store(true, Ordering::Relaxed); 577 let size = &pc.mVisibleArea; 578 // Looks like IntCoordTyped is treated as if it's u32 in Rust. 579 debug_assert!( 580 /* pc.mDynamicToolbarHeight >=0 && */ 581 pc.mDynamicToolbarHeight < i32::MAX as u32 582 ); 583 Size2D::new( 584 Au(size.width), 585 Au(size.height 586 + (pc.mDynamicToolbarMaxHeight - pc.mDynamicToolbarHeight) as i32 587 * pc.mCurAppUnitsPerDevPixel), 588 ) 589 }, 590 } 591 } 592 593 /// Returns whether we ever looked up the viewport size of the Device. 594 pub fn used_viewport_size(&self) -> bool { 595 self.used_viewport_size.load(Ordering::Relaxed) 596 } 597 598 /// Returns whether we ever looked up the dynamic viewport size of the Device. 599 pub fn used_dynamic_viewport_size(&self) -> bool { 600 self.used_dynamic_viewport_size.load(Ordering::Relaxed) 601 } 602 603 /// Returns whether font metrics have been queried. 604 pub fn used_font_metrics(&self) -> bool { 605 self.used_font_metrics.load(Ordering::Relaxed) 606 } 607 608 /// Returns whether visited styles are enabled. 609 pub fn visited_styles_enabled(&self) -> bool { 610 unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) } 611 } 612 613 /// Returns the number of app units per device pixel we're using currently. 614 pub fn app_units_per_device_pixel(&self) -> i32 { 615 match self.pres_context() { 616 Some(pc) => pc.mCurAppUnitsPerDevPixel, 617 None => AU_PER_PX, 618 } 619 } 620 621 /// Returns app units per pixel at 1x full-zoom. 622 fn app_units_per_device_pixel_at_unit_full_zoom(&self) -> i32 { 623 match self.pres_context() { 624 Some(pc) => unsafe { (*pc.mDeviceContext.mRawPtr).mAppUnitsPerDevPixelAtUnitFullZoom }, 625 None => AU_PER_PX, 626 } 627 } 628 629 /// Returns the device pixel ratio, ignoring the full zoom factor. 630 pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> { 631 let au_per_px = AU_PER_PX as f32; 632 Scale::new(au_per_px / self.app_units_per_device_pixel_at_unit_full_zoom() as f32) 633 } 634 635 /// Returns the device pixel ratio. 636 pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> { 637 let pc = match self.pres_context() { 638 Some(pc) => pc, 639 None => return Scale::new(1.), 640 }; 641 642 if pc.mMediaEmulationData.mDPPX > 0.0 { 643 return Scale::new(pc.mMediaEmulationData.mDPPX); 644 } 645 646 let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32; 647 let au_per_px = AU_PER_PX as f32; 648 Scale::new(au_per_px / au_per_dpx) 649 } 650 651 /// Returns whether document colors are enabled. 652 #[inline] 653 pub fn forced_colors(&self) -> ForcedColors { 654 self.pres_context() 655 .map_or(ForcedColors::None, |pc| pc.mForcedColors) 656 } 657 658 /// Computes a system color and returns it as an nscolor. 659 pub(crate) fn system_nscolor( 660 &self, 661 system_color: SystemColor, 662 color_scheme: ColorSchemeFlags, 663 ) -> u32 { 664 unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), &color_scheme) } 665 } 666 667 /// Returns whether the used color-scheme for `color-scheme` should be dark. 668 pub(crate) fn is_dark_color_scheme(&self, color_scheme: ColorSchemeFlags) -> bool { 669 unsafe { bindings::Gecko_IsDarkColorScheme(self.document(), &color_scheme) } 670 } 671 672 /// Returns the default background color. 673 /// 674 /// This is only for forced-colors/high-contrast, so looking at light colors 675 /// is ok. 676 pub fn default_background_color(&self) -> AbsoluteColor { 677 convert_nscolor_to_absolute_color( 678 self.system_nscolor(SystemColor::Canvas, ColorScheme::normal().bits), 679 ) 680 } 681 682 /// Returns the default foreground color. 683 /// 684 /// See above for looking at light colors only. 685 pub fn default_color(&self) -> AbsoluteColor { 686 convert_nscolor_to_absolute_color( 687 self.system_nscolor(SystemColor::Canvastext, ColorScheme::normal().bits), 688 ) 689 } 690 691 /// Returns the current effective text zoom. 692 #[inline] 693 fn text_zoom(&self) -> f32 { 694 let pc = match self.pres_context() { 695 Some(pc) => pc, 696 None => return 1., 697 }; 698 pc.mTextZoom 699 } 700 701 /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText). 702 #[inline] 703 pub fn zoom_text(&self, size: Length) -> Length { 704 size.scale_by(self.text_zoom()) 705 } 706 707 /// Un-apply text zoom. 708 #[inline] 709 pub fn unzoom_text(&self, size: Length) -> Length { 710 size.scale_by(1. / self.text_zoom()) 711 } 712 713 /// Returns safe area insets 714 pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> { 715 let pc = match self.pres_context() { 716 Some(pc) => pc, 717 None => return SideOffsets2D::zero(), 718 }; 719 let mut top = 0.0; 720 let mut right = 0.0; 721 let mut bottom = 0.0; 722 let mut left = 0.0; 723 unsafe { 724 bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left) 725 }; 726 SideOffsets2D::new(top, right, bottom, left) 727 } 728 729 /// Returns true if the given MIME type is supported 730 pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { 731 unsafe { 732 bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32) 733 } 734 } 735 736 /// Return whether the document is a chrome document. 737 /// 738 /// This check is consistent with how we enable chrome rules for chrome:// and resource:// 739 /// stylesheets (and thus chrome:// documents). 740 #[inline] 741 pub fn chrome_rules_enabled_for_document(&self) -> bool { 742 self.document().mChromeRulesEnabled() 743 } 744 }