summaryrefslogtreecommitdiffstats
path: root/layout/style/nsMediaFeatures.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsMediaFeatures.cpp')
-rw-r--r--layout/style/nsMediaFeatures.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
new file mode 100644
index 0000000000..cc0366c8cd
--- /dev/null
+++ b/layout/style/nsMediaFeatures.cpp
@@ -0,0 +1,504 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* the features that media queries can test */
+
+#include "nsMediaFeatures.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsPresContext.h"
+#include "nsCSSProps.h"
+#include "nsCSSValue.h"
+#include "mozilla/LookAndFeel.h"
+#include "nsDeviceContext.h"
+#include "nsIBaseWindow.h"
+#include "nsIDocShell.h"
+#include "nsIPrintSettings.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "mozilla/dom/BrowsingContextBinding.h"
+#include "nsIWidget.h"
+#include "nsContentUtils.h"
+#include "mozilla/RelativeLuminanceUtils.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StyleSheet.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/GeckoBindings.h"
+#include "PreferenceSheet.h"
+#include "nsGlobalWindowOuter.h"
+
+using namespace mozilla;
+using mozilla::dom::Document;
+
+static nsTArray<const nsStaticAtom*>* sSystemMetrics = nullptr;
+
+// A helper for four features below
+static nsSize GetSize(const Document* aDocument) {
+ nsPresContext* pc = aDocument->GetPresContext();
+
+ // Per spec, return a 0x0 viewport if we're not being rendered. See:
+ //
+ // * https://github.com/w3c/csswg-drafts/issues/571
+ // * https://github.com/whatwg/html/issues/1813
+ //
+ if (!pc) {
+ return {};
+ }
+
+ if (pc->IsRootPaginatedDocument()) {
+ // We want the page size, including unprintable areas and margins.
+ //
+ // FIXME(emilio, bug 1414600): Not quite!
+ return pc->GetPageSize();
+ }
+
+ return pc->GetVisibleArea().Size();
+}
+
+// A helper for three features below.
+static nsSize GetDeviceSize(const Document* aDocument) {
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return GetSize(aDocument);
+ }
+
+ // Media queries in documents in an RDM pane should use the simulated
+ // device size.
+ Maybe<CSSIntSize> deviceSize =
+ nsGlobalWindowOuter::GetRDMDeviceSize(*aDocument);
+ if (deviceSize.isSome()) {
+ return CSSPixel::ToAppUnits(deviceSize.value());
+ }
+
+ nsPresContext* pc = aDocument->GetPresContext();
+ // NOTE(emilio): We should probably figure out how to return an appropriate
+ // device size here, though in a multi-screen world that makes no sense
+ // really.
+ if (!pc) {
+ return {};
+ }
+
+ if (pc->IsRootPaginatedDocument()) {
+ // We want the page size, including unprintable areas and margins.
+ // XXX The spec actually says we want the "page sheet size", but
+ // how is that different?
+ return pc->GetPageSize();
+ }
+
+ nsSize size;
+ pc->DeviceContext()->GetDeviceSurfaceDimensions(size.width, size.height);
+ return size;
+}
+
+bool Gecko_MediaFeatures_IsResourceDocument(const Document* aDocument) {
+ return aDocument->IsResourceDoc();
+}
+
+bool Gecko_MediaFeatures_ShouldAvoidNativeTheme(const Document* aDocument) {
+ return aDocument->ShouldAvoidNativeTheme();
+}
+
+static nsDeviceContext* GetDeviceContextFor(const Document* aDocument) {
+ nsPresContext* pc = aDocument->GetPresContext();
+ if (!pc) {
+ return nullptr;
+ }
+
+ // It would be nice to call nsLayoutUtils::GetDeviceContextForScreenInfo here,
+ // except for two things: (1) it can flush, and flushing is bad here, and (2)
+ // it doesn't really get us consistency in multi-monitor situations *anyway*.
+ return pc->DeviceContext();
+}
+
+void Gecko_MediaFeatures_GetDeviceSize(const Document* aDocument,
+ nscoord* aWidth, nscoord* aHeight) {
+ nsSize size = GetDeviceSize(aDocument);
+ *aWidth = size.width;
+ *aHeight = size.height;
+}
+
+uint32_t Gecko_MediaFeatures_GetMonochromeBitsPerPixel(
+ const Document* aDocument) {
+ // The default bits per pixel for a monochrome device. We could propagate this
+ // further to nsIPrintSettings, but Gecko doesn't actually know this value
+ // from the hardware, so it seems silly to do so.
+ static constexpr uint32_t kDefaultMonochromeBpp = 8;
+
+ nsPresContext* pc = aDocument->GetPresContext();
+ if (!pc) {
+ return 0;
+ }
+ nsIPrintSettings* ps = pc->GetPrintSettings();
+ if (!ps) {
+ return 0;
+ }
+ bool color = true;
+ ps->GetPrintInColor(&color);
+ return color ? 0 : kDefaultMonochromeBpp;
+}
+
+uint32_t Gecko_MediaFeatures_GetColorDepth(const Document* aDocument) {
+ if (Gecko_MediaFeatures_GetMonochromeBitsPerPixel(aDocument) != 0) {
+ // If we're a monochrome device, then the color depth is zero.
+ return 0;
+ }
+
+ // Use depth of 24 when resisting fingerprinting, or when we're not being
+ // rendered.
+ uint32_t depth = 24;
+
+ if (!nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) {
+ dx->GetDepth(depth);
+ }
+ }
+
+ // The spec says to use bits *per color component*, so divide by 3,
+ // and round down, since the spec says to use the smallest when the
+ // color components differ.
+ return depth / 3;
+}
+
+float Gecko_MediaFeatures_GetResolution(const Document* aDocument) {
+ // We're returning resolution in terms of device pixels per css pixel, since
+ // that is the preferred unit for media queries of resolution. This avoids
+ // introducing precision error from conversion to and from less-used
+ // physical units like inches.
+ nsPresContext* pc = aDocument->GetPresContext();
+ if (!pc) {
+ return 1.;
+ }
+
+ if (pc->GetOverrideDPPX() > 0.) {
+ return pc->GetOverrideDPPX();
+ }
+
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return pc->DeviceContext()->GetFullZoom();
+ }
+ // Get the actual device pixel ratio, which also takes zoom into account.
+ return float(AppUnitsPerCSSPixel()) /
+ pc->DeviceContext()->AppUnitsPerDevPixel();
+}
+
+static const Document* TopDocument(const Document* aDocument) {
+ const Document* current = aDocument;
+ while (const Document* parent = current->GetInProcessParentDocument()) {
+ current = parent;
+ }
+ return current;
+}
+
+StyleDisplayMode Gecko_MediaFeatures_GetDisplayMode(const Document* aDocument) {
+ const Document* rootDocument = TopDocument(aDocument);
+
+ nsCOMPtr<nsISupports> container = rootDocument->GetContainer();
+ if (nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container)) {
+ nsCOMPtr<nsIWidget> mainWidget;
+ baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+ if (mainWidget && mainWidget->SizeMode() == nsSizeMode_Fullscreen) {
+ return StyleDisplayMode::Fullscreen;
+ }
+ }
+
+ static_assert(static_cast<int32_t>(DisplayMode::Browser) ==
+ static_cast<int32_t>(StyleDisplayMode::Browser) &&
+ static_cast<int32_t>(DisplayMode::Minimal_ui) ==
+ static_cast<int32_t>(StyleDisplayMode::MinimalUi) &&
+ static_cast<int32_t>(DisplayMode::Standalone) ==
+ static_cast<int32_t>(StyleDisplayMode::Standalone) &&
+ static_cast<int32_t>(DisplayMode::Fullscreen) ==
+ static_cast<int32_t>(StyleDisplayMode::Fullscreen),
+ "DisplayMode must mach nsStyleConsts.h");
+
+ BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
+ if (!browsingContext) {
+ return StyleDisplayMode::Browser;
+ }
+ return static_cast<StyleDisplayMode>(browsingContext->DisplayMode());
+}
+
+bool Gecko_MediaFeatures_HasSystemMetric(const Document* aDocument,
+ nsAtom* aMetric,
+ bool aIsAccessibleFromContent) {
+ if (aIsAccessibleFromContent &&
+ nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return false;
+ }
+
+ nsMediaFeatures::InitSystemMetrics();
+ return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
+}
+
+nsAtom* Gecko_MediaFeatures_GetOperatingSystemVersion(
+ const Document* aDocument) {
+ using OperatingSystemVersion = LookAndFeel::OperatingSystemVersion;
+
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return nullptr;
+ }
+
+ int32_t metricResult;
+ if (NS_FAILED(LookAndFeel::GetInt(
+ LookAndFeel::IntID::OperatingSystemVersionIdentifier,
+ &metricResult))) {
+ return nullptr;
+ }
+
+ switch (OperatingSystemVersion(metricResult)) {
+ case OperatingSystemVersion::Windows7:
+ return nsGkAtoms::windows_win7;
+ case OperatingSystemVersion::Windows8:
+ return nsGkAtoms::windows_win8;
+ case OperatingSystemVersion::Windows10:
+ return nsGkAtoms::windows_win10;
+ default:
+ return nullptr;
+ }
+}
+
+bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return false;
+ }
+ return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
+}
+
+StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
+ const Document* aDocument) {
+ return aDocument->PrefersColorScheme();
+}
+
+StyleContrastPref Gecko_MediaFeatures_PrefersContrast(
+ const Document* aDocument, const bool aForcedColors) {
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return StyleContrastPref::NoPreference;
+ }
+ // Neither Linux, Windows, nor Mac have a way to indicate that low
+ // contrast is prefered so the presence of an accessibility theme
+ // implies that high contrast is prefered.
+ //
+ // Note that MacOS does not expose whether or not high contrast is
+ // enabled so for MacOS users this will always evaluate to
+ // false. For more information and discussion see:
+ // https://github.com/w3c/csswg-drafts/issues/3856#issuecomment-642313572
+ // https://github.com/w3c/csswg-drafts/issues/2943
+ if (!!LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme, 0)) {
+ return StyleContrastPref::More;
+ }
+ return StyleContrastPref::NoPreference;
+}
+
+static PointerCapabilities GetPointerCapabilities(const Document* aDocument,
+ LookAndFeel::IntID aID) {
+ MOZ_ASSERT(aID == LookAndFeel::IntID::PrimaryPointerCapabilities ||
+ aID == LookAndFeel::IntID::AllPointerCapabilities);
+ MOZ_ASSERT(aDocument);
+
+ if (BrowsingContext* bc = aDocument->GetBrowsingContext()) {
+ // The touch-events-override happens only for the Responsive Design Mode so
+ // that we don't need to care about ResistFingerprinting.
+ if (bc->TouchEventsOverride() ==
+ mozilla::dom::TouchEventsOverride::Enabled) {
+ return PointerCapabilities::Coarse;
+ }
+ }
+
+ // The default value for Desktop is mouse-type pointer, and for Android
+ // a coarse pointer.
+ const PointerCapabilities kDefaultCapabilities =
+#ifdef ANDROID
+ PointerCapabilities::Coarse;
+#else
+ PointerCapabilities::Fine | PointerCapabilities::Hover;
+#endif
+
+ if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+ return kDefaultCapabilities;
+ }
+
+ int32_t intValue;
+ nsresult rv = LookAndFeel::GetInt(aID, &intValue);
+ if (NS_FAILED(rv)) {
+ return kDefaultCapabilities;
+ }
+
+ return static_cast<PointerCapabilities>(intValue);
+}
+
+PointerCapabilities Gecko_MediaFeatures_PrimaryPointerCapabilities(
+ const Document* aDocument) {
+ return GetPointerCapabilities(aDocument,
+ LookAndFeel::IntID::PrimaryPointerCapabilities);
+}
+
+PointerCapabilities Gecko_MediaFeatures_AllPointerCapabilities(
+ const Document* aDocument) {
+ return GetPointerCapabilities(aDocument,
+ LookAndFeel::IntID::AllPointerCapabilities);
+}
+
+/* static */
+void nsMediaFeatures::InitSystemMetrics() {
+ if (sSystemMetrics) return;
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ sSystemMetrics = new nsTArray<const nsStaticAtom*>;
+
+ /***************************************************************************
+ * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES BELOW *
+ ***************************************************************************/
+
+ int32_t metricResult =
+ LookAndFeel::GetInt(LookAndFeel::IntID::ScrollArrowStyle);
+ if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_start_backward);
+ }
+ if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_start_forward);
+ }
+ if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_end_backward);
+ }
+ if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_end_forward);
+ }
+
+ metricResult = LookAndFeel::GetInt(LookAndFeel::IntID::ScrollSliderStyle);
+ if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_thumb_proportional);
+ }
+
+ metricResult = LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
+ if (metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_overlay_scrollbars);
+ }
+
+ metricResult = LookAndFeel::GetInt(LookAndFeel::IntID::MenuBarDrag);
+ if (metricResult) {
+ sSystemMetrics->AppendElement((nsStaticAtom*)nsGkAtoms::_moz_menubar_drag);
+ }
+
+ nsresult rv = LookAndFeel::GetInt(LookAndFeel::IntID::WindowsDefaultTheme,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_windows_default_theme);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::MacGraphiteTheme, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_mac_graphite_theme);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::MacBigSurTheme, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_mac_big_sur_theme);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::WindowsAccentColorInTitlebar,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_windows_accent_color_in_titlebar);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::DWMCompositor, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_windows_compositor);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::WindowsGlass, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement((nsStaticAtom*)nsGkAtoms::_moz_windows_glass);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::WindowsClassic, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_windows_classic);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::SwipeAnimationEnabled,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_swipe_animation_enabled);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDAvailable, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_available);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDHideTitlebarByDefault,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_hide_titlebar_by_default);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDTransparentBackground,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_transparent_background);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDMinimizeButton,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_minimize_button);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDMaximizeButton,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_maximize_button);
+ }
+
+ rv =
+ LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDCloseButton, &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_close_button);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::GTKCSDReversedPlacement,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_reversed_placement);
+ }
+
+ rv = LookAndFeel::GetInt(LookAndFeel::IntID::SystemUsesDarkTheme,
+ &metricResult);
+ if (NS_SUCCEEDED(rv) && metricResult) {
+ sSystemMetrics->AppendElement(
+ (nsStaticAtom*)nsGkAtoms::_moz_system_dark_theme);
+ }
+}
+
+/* static */
+void nsMediaFeatures::FreeSystemMetrics() {
+ delete sSystemMetrics;
+ sSystemMetrics = nullptr;
+}
+
+/* static */
+void nsMediaFeatures::Shutdown() { FreeSystemMetrics(); }