tor-browser

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

nsMediaFeatures.cpp (15132B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* the features that media queries can test */
      8 
      9 #include "PreferenceSheet.h"
     10 #include "mozilla/GeckoBindings.h"
     11 #include "mozilla/LookAndFeel.h"
     12 #include "mozilla/RelativeLuminanceUtils.h"
     13 #include "mozilla/StaticPrefs_browser.h"
     14 #include "mozilla/StaticPrefs_gfx.h"
     15 #include "mozilla/StyleSheet.h"
     16 #include "mozilla/StyleSheetInlines.h"
     17 #include "mozilla/dom/BrowsingContextBinding.h"
     18 #include "mozilla/dom/Document.h"
     19 #include "mozilla/dom/DocumentInlines.h"
     20 #include "mozilla/dom/ScreenBinding.h"
     21 #include "nsCSSProps.h"
     22 #include "nsCSSValue.h"
     23 #include "nsContentUtils.h"
     24 #include "nsDeviceContext.h"
     25 #include "nsGkAtoms.h"
     26 #include "nsGlobalWindowOuter.h"
     27 #include "nsIBaseWindow.h"
     28 #include "nsIDocShell.h"
     29 #include "nsIPrintSettings.h"
     30 #include "nsIWidget.h"
     31 #include "nsPresContext.h"
     32 #include "nsStyleConsts.h"
     33 
     34 using namespace mozilla;
     35 using mozilla::dom::DisplayMode;
     36 using mozilla::dom::Document;
     37 
     38 // A helper for four features below
     39 static nsSize GetSize(const Document& aDocument) {
     40  nsPresContext* pc = aDocument.GetPresContext();
     41 
     42  // Per spec, return a 0x0 viewport if we're not being rendered. See:
     43  //
     44  //  * https://github.com/w3c/csswg-drafts/issues/571
     45  //  * https://github.com/whatwg/html/issues/1813
     46  //
     47  if (!pc) {
     48    return {};
     49  }
     50 
     51  if (pc->IsRootPaginatedDocument()) {
     52    // We want the page size, including unprintable areas and margins.
     53    //
     54    // FIXME(emilio, bug 1414600): Not quite!
     55    return pc->GetPageSize();
     56  }
     57 
     58  return pc->GetVisibleArea().Size();
     59 }
     60 
     61 // A helper for three features below.
     62 static nsSize GetDeviceSize(const Document& aDocument) {
     63  if (aDocument.ShouldResistFingerprinting(RFPTarget::CSSDeviceSize)) {
     64    return GetSize(aDocument);
     65  }
     66 
     67  // Media queries in documents in an RDM pane should use the simulated
     68  // device size.
     69  Maybe<CSSIntSize> deviceSize =
     70      nsGlobalWindowOuter::GetRDMDeviceSize(aDocument);
     71  if (deviceSize.isSome()) {
     72    return CSSPixel::ToAppUnits(deviceSize.value());
     73  }
     74 
     75  // Media queries in documents should use an override set with WebDriver BiDi
     76  // if it exists.
     77  if (dom::BrowsingContext* bc = aDocument.GetBrowsingContext()) {
     78    Maybe<CSSIntSize> screenSize = bc->GetScreenAreaOverride();
     79    if (screenSize.isSome()) {
     80      return CSSPixel::ToAppUnits(screenSize.value());
     81    }
     82  }
     83 
     84  nsPresContext* pc = aDocument.GetPresContext();
     85  // NOTE(emilio): We should probably figure out how to return an appropriate
     86  // device size here, though in a multi-screen world that makes no sense
     87  // really.
     88  if (!pc) {
     89    return {};
     90  }
     91 
     92  if (pc->IsRootPaginatedDocument()) {
     93    // We want the page size, including unprintable areas and margins.
     94    // XXX The spec actually says we want the "page sheet size", but
     95    // how is that different?
     96    return pc->GetPageSize();
     97  }
     98 
     99  return pc->DeviceContext()->GetDeviceSurfaceDimensions();
    100 }
    101 
    102 bool Gecko_MediaFeatures_IsResourceDocument(const Document* aDocument) {
    103  return aDocument->IsResourceDoc();
    104 }
    105 
    106 bool Gecko_MediaFeatures_InAndroidPipMode(const Document* aDocument) {
    107  return aDocument->InAndroidPipMode();
    108 }
    109 
    110 bool Gecko_MediaFeatures_UseOverlayScrollbars(const Document* aDocument) {
    111  nsPresContext* pc = aDocument->GetPresContext();
    112  return pc && pc->UseOverlayScrollbars();
    113 }
    114 
    115 static nsDeviceContext* GetDeviceContextFor(const Document* aDocument) {
    116  nsPresContext* pc = aDocument->GetPresContext();
    117  if (!pc) {
    118    return nullptr;
    119  }
    120 
    121  // It would be nice to call nsLayoutUtils::GetDeviceContextForScreenInfo here,
    122  // except for two things:  (1) it can flush, and flushing is bad here, and (2)
    123  // it doesn't really get us consistency in multi-monitor situations *anyway*.
    124  return pc->DeviceContext();
    125 }
    126 
    127 void Gecko_MediaFeatures_GetDeviceSize(const Document* aDocument,
    128                                       nscoord* aWidth, nscoord* aHeight) {
    129  nsSize size = GetDeviceSize(*aDocument);
    130  *aWidth = size.width;
    131  *aHeight = size.height;
    132 }
    133 
    134 int32_t Gecko_MediaFeatures_GetMonochromeBitsPerPixel(
    135    const Document* aDocument) {
    136  // The default bits per pixel for a monochrome device. We could propagate this
    137  // further to nsIPrintSettings, but Gecko doesn't actually know this value
    138  // from the hardware, so it seems silly to do so.
    139  static constexpr int32_t kDefaultMonochromeBpp = 8;
    140 
    141  nsPresContext* pc = aDocument->GetPresContext();
    142  if (!pc) {
    143    return 0;
    144  }
    145  nsIPrintSettings* ps = pc->GetPrintSettings();
    146  if (!ps) {
    147    return 0;
    148  }
    149  bool color = true;
    150  ps->GetPrintInColor(&color);
    151  return color ? 0 : kDefaultMonochromeBpp;
    152 }
    153 
    154 StyleColorGamut Gecko_MediaFeatures_ColorGamut(const Document* aDocument) {
    155  auto* dx = GetDeviceContextFor(aDocument);
    156  if (!dx || aDocument->ShouldResistFingerprinting(RFPTarget::CSSColorInfo)) {
    157    return StyleColorGamut::Srgb;
    158  }
    159  switch (dx->GetColorGamut()) {
    160    case dom::ScreenColorGamut::Srgb:
    161      return StyleColorGamut::Srgb;
    162    case dom::ScreenColorGamut::Rec2020:
    163      return StyleColorGamut::Rec2020;
    164    case dom::ScreenColorGamut::P3:
    165      return StyleColorGamut::P3;
    166  }
    167  return StyleColorGamut::Srgb;
    168 }
    169 
    170 int32_t Gecko_MediaFeatures_GetColorDepth(const Document* aDocument) {
    171  if (Gecko_MediaFeatures_GetMonochromeBitsPerPixel(aDocument) != 0) {
    172    // If we're a monochrome device, then the color depth is zero.
    173    return 0;
    174  }
    175 
    176  // Use depth of 24 when resisting fingerprinting, or when we're not being
    177  // rendered.
    178  int32_t depth = 24;
    179 
    180  if (!aDocument->ShouldResistFingerprinting(RFPTarget::CSSColorInfo)) {
    181    if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) {
    182      depth = dx->GetDepth();
    183    }
    184  }
    185 
    186  // The spec says to use bits *per color component*, so divide by 3,
    187  // and round down, since the spec says to use the smallest when the
    188  // color components differ.
    189  return depth / 3;
    190 }
    191 
    192 float Gecko_MediaFeatures_GetResolution(const Document* aDocument) {
    193  // We're returning resolution in terms of device pixels per css pixel, since
    194  // that is the preferred unit for media queries of resolution. This avoids
    195  // introducing precision error from conversion to and from less-used
    196  // physical units like inches.
    197  nsPresContext* pc = aDocument->GetPresContext();
    198  if (!pc) {
    199    return 1.;
    200  }
    201 
    202  if (pc->GetOverrideDPPX() > 0.) {
    203    return pc->GetOverrideDPPX();
    204  }
    205 
    206  if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSResolution)) {
    207    return float(nsRFPService::GetDevicePixelRatioAtZoom(pc->GetFullZoom()));
    208  }
    209  // Get the actual device pixel ratio, which also takes zoom into account.
    210  return float(AppUnitsPerCSSPixel()) /
    211         pc->DeviceContext()->AppUnitsPerDevPixel();
    212 }
    213 
    214 static const Document* TopDocument(const Document* aDocument) {
    215  const Document* current = aDocument;
    216  while (const Document* parent = current->GetInProcessParentDocument()) {
    217    current = parent;
    218  }
    219  return current;
    220 }
    221 
    222 StyleDisplayMode Gecko_MediaFeatures_GetDisplayMode(const Document* aDocument) {
    223  const Document* rootDocument = TopDocument(aDocument);
    224 
    225  nsCOMPtr<nsISupports> container = rootDocument->GetContainer();
    226  if (nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container)) {
    227    nsCOMPtr<nsIWidget> mainWidget = baseWindow->GetMainWidget();
    228    if (mainWidget && mainWidget->SizeMode() == nsSizeMode_Fullscreen) {
    229      return StyleDisplayMode::Fullscreen;
    230    }
    231  }
    232 
    233  static_assert(
    234      static_cast<int32_t>(DisplayMode::Browser) ==
    235              static_cast<int32_t>(StyleDisplayMode::Browser) &&
    236          static_cast<int32_t>(DisplayMode::Minimal_ui) ==
    237              static_cast<int32_t>(StyleDisplayMode::MinimalUi) &&
    238          static_cast<int32_t>(DisplayMode::Standalone) ==
    239              static_cast<int32_t>(StyleDisplayMode::Standalone) &&
    240          static_cast<int32_t>(DisplayMode::Fullscreen) ==
    241              static_cast<int32_t>(StyleDisplayMode::Fullscreen) &&
    242          static_cast<int32_t>(DisplayMode::Picture_in_picture) ==
    243              static_cast<int32_t>(StyleDisplayMode::PictureInPicture),
    244      "DisplayMode must mach nsStyleConsts.h");
    245 
    246  dom::BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
    247  if (!browsingContext) {
    248    return StyleDisplayMode::Browser;
    249  }
    250  return static_cast<StyleDisplayMode>(browsingContext->DisplayMode());
    251 }
    252 
    253 bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
    254  switch (aPlatform) {
    255 #if defined(XP_WIN)
    256    case StylePlatform::Windows:
    257      return true;
    258 #elif defined(ANDROID)
    259    case StylePlatform::Android:
    260      return true;
    261 #elif defined(MOZ_WIDGET_GTK)
    262    case StylePlatform::Linux:
    263      return true;
    264 #elif defined(XP_MACOSX)
    265    case StylePlatform::Macos:
    266      return true;
    267 #elif defined(XP_IOS)
    268    case StylePlatform::Ios:
    269      return true;
    270 #else
    271 #  error "Unknown platform?"
    272 #endif
    273    default:
    274      return false;
    275  }
    276 }
    277 
    278 bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
    279  if (aDocument->ShouldResistFingerprinting(
    280          RFPTarget::CSSPrefersReducedMotion)) {
    281    return false;
    282  }
    283  return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
    284 }
    285 
    286 bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) {
    287  if (aDocument->ShouldResistFingerprinting(
    288          RFPTarget::CSSPrefersReducedTransparency)) {
    289    return false;
    290  }
    291  return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency,
    292                             0) == 1;
    293 }
    294 
    295 StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
    296    const Document* aDocument, bool aUseContent) {
    297  auto scheme = aUseContent ? PreferenceSheet::ContentPrefs().mColorScheme
    298                            : aDocument->PreferredColorScheme();
    299  return scheme == ColorScheme::Dark ? StylePrefersColorScheme::Dark
    300                                     : StylePrefersColorScheme::Light;
    301 }
    302 
    303 bool Gecko_MediaFeatures_MacRTL(const Document* aDocument) {
    304  auto* widget = nsContentUtils::WidgetForDocument(aDocument);
    305  return widget && widget->IsMacTitlebarDirectionRTL();
    306 }
    307 
    308 // Neither Linux, Windows, nor Mac have a way to indicate that low contrast is
    309 // preferred so we use the presence of an accessibility theme or forced colors
    310 // as a signal.
    311 StylePrefersContrast Gecko_MediaFeatures_PrefersContrast(
    312    const Document* aDocument) {
    313  if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSPrefersContrast)) {
    314    return StylePrefersContrast::NoPreference;
    315  }
    316  const auto& prefs = PreferenceSheet::PrefsFor(*aDocument);
    317  if (!prefs.mUseAccessibilityTheme && prefs.mUseDocumentColors) {
    318    return StylePrefersContrast::NoPreference;
    319  }
    320  const auto& colors = prefs.ColorsFor(ColorScheme::Light);
    321  float ratio = RelativeLuminanceUtils::ContrastRatio(colors.mDefaultBackground,
    322                                                      colors.mDefault);
    323  // https://www.w3.org/TR/WCAG21/#contrast-minimum
    324  if (ratio < 4.5f) {
    325    return StylePrefersContrast::Less;
    326  }
    327  // https://www.w3.org/TR/WCAG21/#contrast-enhanced
    328  if (ratio >= 7.0f) {
    329    return StylePrefersContrast::More;
    330  }
    331  return StylePrefersContrast::Custom;
    332 }
    333 
    334 bool Gecko_MediaFeatures_InvertedColors(const Document* aDocument) {
    335  if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSInvertedColors)) {
    336    return false;
    337  }
    338  return LookAndFeel::GetInt(LookAndFeel::IntID::InvertedColors, 0) == 1;
    339 }
    340 
    341 StyleScripting Gecko_MediaFeatures_Scripting(const Document* aDocument) {
    342  const auto* doc = aDocument;
    343  if (aDocument->IsStaticDocument()) {
    344    doc = aDocument->GetOriginalDocument();
    345  }
    346 
    347  return doc->IsScriptEnabled() ? StyleScripting::Enabled
    348                                : StyleScripting::None;
    349 }
    350 
    351 StyleDynamicRange Gecko_MediaFeatures_DynamicRange(const Document* aDocument) {
    352  // Bug 1759772: Once HDR color is available, update each platform
    353  // LookAndFeel implementation to return StyleDynamicRange::High when
    354  // appropriate.
    355  return StyleDynamicRange::Standard;
    356 }
    357 
    358 StyleDynamicRange Gecko_MediaFeatures_VideoDynamicRange(
    359    const Document* aDocument) {
    360  if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSVideoDynamicRange) ||
    361      !StaticPrefs::layout_css_video_dynamic_range_allows_high()) {
    362    return StyleDynamicRange::Standard;
    363  }
    364 #ifdef MOZ_WAYLAND
    365  // Wayland compositors allow to process HDR content even without HDR monitor
    366  // attached.
    367  if (StaticPrefs::gfx_wayland_hdr_force_enabled_AtStartup()) {
    368    return StyleDynamicRange::High;
    369  }
    370  if (!StaticPrefs::gfx_wayland_hdr_AtStartup()) {
    371    return StyleDynamicRange::Standard;
    372  }
    373 #endif
    374  // video-dynamic-range: high has 3 requirements:
    375  // 1) high peak brightness
    376  // 2) high contrast ratio
    377  // 3) color depth > 24
    378 
    379  // As a proxy for those requirements, return 'High' if the screen associated
    380  // with the device context claims to be HDR capable.
    381  if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) {
    382    if (dx->GetScreenIsHDR()) {
    383      return StyleDynamicRange::High;
    384    }
    385  }
    386 
    387  return StyleDynamicRange::Standard;
    388 }
    389 
    390 static PointerCapabilities GetPointerCapabilities(const Document* aDocument,
    391                                                  LookAndFeel::IntID aID) {
    392  MOZ_ASSERT(aID == LookAndFeel::IntID::PrimaryPointerCapabilities ||
    393             aID == LookAndFeel::IntID::AllPointerCapabilities);
    394  MOZ_ASSERT(aDocument);
    395 
    396  if (dom::BrowsingContext* bc = aDocument->GetBrowsingContext()) {
    397    // The touch-events-override happens only for the Responsive Design Mode so
    398    // that we don't need to care about ResistFingerprinting.
    399    if (bc->TouchEventsOverride() == dom::TouchEventsOverride::Enabled) {
    400      return PointerCapabilities::Coarse;
    401    }
    402  }
    403 
    404  // The default value for Desktop is mouse-type pointer, and for Android
    405  // a coarse pointer.
    406  const PointerCapabilities kDefaultCapabilities =
    407 #ifdef ANDROID
    408      PointerCapabilities::Coarse;
    409 #else
    410      PointerCapabilities::Fine | PointerCapabilities::Hover;
    411 #endif
    412  if (aDocument->ShouldResistFingerprinting(
    413          RFPTarget::CSSPointerCapabilities)) {
    414    return kDefaultCapabilities;
    415  }
    416 
    417  int32_t intValue;
    418  nsresult rv = LookAndFeel::GetInt(aID, &intValue);
    419  if (NS_FAILED(rv)) {
    420    return kDefaultCapabilities;
    421  }
    422 
    423  return static_cast<PointerCapabilities>(intValue);
    424 }
    425 
    426 PointerCapabilities Gecko_MediaFeatures_PrimaryPointerCapabilities(
    427    const Document* aDocument) {
    428  return GetPointerCapabilities(aDocument,
    429                                LookAndFeel::IntID::PrimaryPointerCapabilities);
    430 }
    431 
    432 PointerCapabilities Gecko_MediaFeatures_AllPointerCapabilities(
    433    const Document* aDocument) {
    434  return GetPointerCapabilities(aDocument,
    435                                LookAndFeel::IntID::AllPointerCapabilities);
    436 }
    437 
    438 StyleGtkThemeFamily Gecko_MediaFeatures_GtkThemeFamily() {
    439  static_assert(int32_t(StyleGtkThemeFamily::Unknown) == 0);
    440  return StyleGtkThemeFamily(
    441      LookAndFeel::GetInt(LookAndFeel::IntID::GTKThemeFamily));
    442 }