tor-browser

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

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 }