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/. */
7 /* the features that media queries can test */
10 #include "nsStyleConsts.h"
11 #include "nsPresContext.h"
12 #include "nsCSSProps.h"
13 #include "nsCSSValue.h"
14 #include "mozilla/LookAndFeel.h"
15 #include "nsDeviceContext.h"
16 #include "nsIBaseWindow.h"
17 #include "nsIDocShell.h"
18 #include "nsIPrintSettings.h"
19 #include "mozilla/dom/Document.h"
20 #include "mozilla/dom/DocumentInlines.h"
21 #include "mozilla/dom/BrowsingContextBinding.h"
22 #include "mozilla/dom/ScreenBinding.h"
23 #include "nsIWidget.h"
24 #include "nsContentUtils.h"
25 #include "mozilla/RelativeLuminanceUtils.h"
26 #include "mozilla/StaticPrefs_browser.h"
27 #include "mozilla/StyleSheet.h"
28 #include "mozilla/StyleSheetInlines.h"
29 #include "mozilla/GeckoBindings.h"
30 #include "PreferenceSheet.h"
31 #include "nsGlobalWindowOuter.h"
33 # include "mozilla/WindowsVersion.h"
36 using namespace mozilla
;
37 using mozilla::dom::DisplayMode
;
38 using mozilla::dom::Document
;
40 // A helper for four features below
41 static nsSize
GetSize(const Document
& aDocument
) {
42 nsPresContext
* pc
= aDocument
.GetPresContext();
44 // Per spec, return a 0x0 viewport if we're not being rendered. See:
46 // * https://github.com/w3c/csswg-drafts/issues/571
47 // * https://github.com/whatwg/html/issues/1813
53 if (pc
->IsRootPaginatedDocument()) {
54 // We want the page size, including unprintable areas and margins.
56 // FIXME(emilio, bug 1414600): Not quite!
57 return pc
->GetPageSize();
60 return pc
->GetVisibleArea().Size();
63 // A helper for three features below.
64 static nsSize
GetDeviceSize(const Document
& aDocument
) {
65 if (aDocument
.ShouldResistFingerprinting(RFPTarget::CSSDeviceSize
)) {
66 return GetSize(aDocument
);
69 // Media queries in documents in an RDM pane should use the simulated
71 Maybe
<CSSIntSize
> deviceSize
=
72 nsGlobalWindowOuter::GetRDMDeviceSize(aDocument
);
73 if (deviceSize
.isSome()) {
74 return CSSPixel::ToAppUnits(deviceSize
.value());
77 nsPresContext
* pc
= aDocument
.GetPresContext();
78 // NOTE(emilio): We should probably figure out how to return an appropriate
79 // device size here, though in a multi-screen world that makes no sense
85 if (pc
->IsRootPaginatedDocument()) {
86 // We want the page size, including unprintable areas and margins.
87 // XXX The spec actually says we want the "page sheet size", but
88 // how is that different?
89 return pc
->GetPageSize();
93 pc
->DeviceContext()->GetDeviceSurfaceDimensions(size
.width
, size
.height
);
97 bool Gecko_MediaFeatures_IsResourceDocument(const Document
* aDocument
) {
98 return aDocument
->IsResourceDoc();
101 bool Gecko_MediaFeatures_UseOverlayScrollbars(const Document
* aDocument
) {
102 nsPresContext
* pc
= aDocument
->GetPresContext();
103 return pc
&& pc
->UseOverlayScrollbars();
106 static nsDeviceContext
* GetDeviceContextFor(const Document
* aDocument
) {
107 nsPresContext
* pc
= aDocument
->GetPresContext();
112 // It would be nice to call nsLayoutUtils::GetDeviceContextForScreenInfo here,
113 // except for two things: (1) it can flush, and flushing is bad here, and (2)
114 // it doesn't really get us consistency in multi-monitor situations *anyway*.
115 return pc
->DeviceContext();
118 void Gecko_MediaFeatures_GetDeviceSize(const Document
* aDocument
,
119 nscoord
* aWidth
, nscoord
* aHeight
) {
120 nsSize size
= GetDeviceSize(*aDocument
);
121 *aWidth
= size
.width
;
122 *aHeight
= size
.height
;
125 int32_t Gecko_MediaFeatures_GetMonochromeBitsPerPixel(
126 const Document
* aDocument
) {
127 // The default bits per pixel for a monochrome device. We could propagate this
128 // further to nsIPrintSettings, but Gecko doesn't actually know this value
129 // from the hardware, so it seems silly to do so.
130 static constexpr int32_t kDefaultMonochromeBpp
= 8;
132 nsPresContext
* pc
= aDocument
->GetPresContext();
136 nsIPrintSettings
* ps
= pc
->GetPrintSettings();
141 ps
->GetPrintInColor(&color
);
142 return color
? 0 : kDefaultMonochromeBpp
;
145 dom::ScreenColorGamut
Gecko_MediaFeatures_ColorGamut(
146 const Document
* aDocument
) {
147 auto colorGamut
= dom::ScreenColorGamut::Srgb
;
148 if (!aDocument
->ShouldResistFingerprinting(RFPTarget::CSSColorInfo
)) {
149 if (auto* dx
= GetDeviceContextFor(aDocument
)) {
150 colorGamut
= dx
->GetColorGamut();
156 int32_t Gecko_MediaFeatures_GetColorDepth(const Document
* aDocument
) {
157 if (Gecko_MediaFeatures_GetMonochromeBitsPerPixel(aDocument
) != 0) {
158 // If we're a monochrome device, then the color depth is zero.
162 // Use depth of 24 when resisting fingerprinting, or when we're not being
166 if (!aDocument
->ShouldResistFingerprinting(RFPTarget::CSSColorInfo
)) {
167 if (nsDeviceContext
* dx
= GetDeviceContextFor(aDocument
)) {
168 depth
= dx
->GetDepth();
172 // The spec says to use bits *per color component*, so divide by 3,
173 // and round down, since the spec says to use the smallest when the
174 // color components differ.
178 float Gecko_MediaFeatures_GetResolution(const Document
* aDocument
) {
179 // We're returning resolution in terms of device pixels per css pixel, since
180 // that is the preferred unit for media queries of resolution. This avoids
181 // introducing precision error from conversion to and from less-used
182 // physical units like inches.
183 nsPresContext
* pc
= aDocument
->GetPresContext();
188 if (pc
->GetOverrideDPPX() > 0.) {
189 return pc
->GetOverrideDPPX();
192 if (aDocument
->ShouldResistFingerprinting(RFPTarget::CSSResolution
)) {
193 return pc
->DeviceContext()->GetFullZoom();
195 // Get the actual device pixel ratio, which also takes zoom into account.
196 return float(AppUnitsPerCSSPixel()) /
197 pc
->DeviceContext()->AppUnitsPerDevPixel();
200 static const Document
* TopDocument(const Document
* aDocument
) {
201 const Document
* current
= aDocument
;
202 while (const Document
* parent
= current
->GetInProcessParentDocument()) {
208 StyleDisplayMode
Gecko_MediaFeatures_GetDisplayMode(const Document
* aDocument
) {
209 const Document
* rootDocument
= TopDocument(aDocument
);
211 nsCOMPtr
<nsISupports
> container
= rootDocument
->GetContainer();
212 if (nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(container
)) {
213 nsCOMPtr
<nsIWidget
> mainWidget
;
214 baseWindow
->GetMainWidget(getter_AddRefs(mainWidget
));
215 if (mainWidget
&& mainWidget
->SizeMode() == nsSizeMode_Fullscreen
) {
216 return StyleDisplayMode::Fullscreen
;
220 static_assert(static_cast<int32_t>(DisplayMode::Browser
) ==
221 static_cast<int32_t>(StyleDisplayMode::Browser
) &&
222 static_cast<int32_t>(DisplayMode::Minimal_ui
) ==
223 static_cast<int32_t>(StyleDisplayMode::MinimalUi
) &&
224 static_cast<int32_t>(DisplayMode::Standalone
) ==
225 static_cast<int32_t>(StyleDisplayMode::Standalone
) &&
226 static_cast<int32_t>(DisplayMode::Fullscreen
) ==
227 static_cast<int32_t>(StyleDisplayMode::Fullscreen
),
228 "DisplayMode must mach nsStyleConsts.h");
230 dom::BrowsingContext
* browsingContext
= aDocument
->GetBrowsingContext();
231 if (!browsingContext
) {
232 return StyleDisplayMode::Browser
;
234 return static_cast<StyleDisplayMode
>(browsingContext
->DisplayMode());
237 bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform
) {
240 case StylePlatform::Windows
:
242 #elif defined(ANDROID)
243 case StylePlatform::Android
:
245 #elif defined(MOZ_WIDGET_GTK)
246 case StylePlatform::Linux
:
248 #elif defined(XP_MACOSX)
249 case StylePlatform::Macos
:
251 #elif defined(XP_IOS)
252 case StylePlatform::Ios
:
255 # error "Unknown platform?"
262 bool Gecko_MediaFeatures_PrefersReducedMotion(const Document
* aDocument
) {
263 if (aDocument
->ShouldResistFingerprinting(
264 RFPTarget::CSSPrefersReducedMotion
)) {
267 return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion
, 0) == 1;
270 bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document
* aDocument
) {
271 if (aDocument
->ShouldResistFingerprinting(
272 RFPTarget::CSSPrefersReducedTransparency
)) {
275 return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency
,
279 StylePrefersColorScheme
Gecko_MediaFeatures_PrefersColorScheme(
280 const Document
* aDocument
, bool aUseContent
) {
281 auto scheme
= aUseContent
? PreferenceSheet::ContentPrefs().mColorScheme
282 : aDocument
->PreferredColorScheme();
283 return scheme
== ColorScheme::Dark
? StylePrefersColorScheme::Dark
284 : StylePrefersColorScheme::Light
;
287 // Neither Linux, Windows, nor Mac have a way to indicate that low contrast is
288 // preferred so we use the presence of an accessibility theme or forced colors
290 StylePrefersContrast
Gecko_MediaFeatures_PrefersContrast(
291 const Document
* aDocument
) {
292 if (aDocument
->ShouldResistFingerprinting(RFPTarget::CSSPrefersContrast
)) {
293 return StylePrefersContrast::NoPreference
;
295 const auto& prefs
= PreferenceSheet::PrefsFor(*aDocument
);
296 if (!prefs
.mUseAccessibilityTheme
&& prefs
.mUseDocumentColors
) {
297 return StylePrefersContrast::NoPreference
;
299 const auto& colors
= prefs
.ColorsFor(ColorScheme::Light
);
300 float ratio
= RelativeLuminanceUtils::ContrastRatio(colors
.mDefaultBackground
,
302 // https://www.w3.org/TR/WCAG21/#contrast-minimum
304 return StylePrefersContrast::Less
;
306 // https://www.w3.org/TR/WCAG21/#contrast-enhanced
308 return StylePrefersContrast::More
;
310 return StylePrefersContrast::Custom
;
313 bool Gecko_MediaFeatures_InvertedColors(const Document
* aDocument
) {
314 if (aDocument
->ShouldResistFingerprinting(RFPTarget::CSSInvertedColors
)) {
317 return LookAndFeel::GetInt(LookAndFeel::IntID::InvertedColors
, 0) == 1;
320 StyleScripting
Gecko_MediaFeatures_Scripting(const Document
* aDocument
) {
321 const auto* doc
= aDocument
;
322 if (aDocument
->IsStaticDocument()) {
323 doc
= aDocument
->GetOriginalDocument();
326 return doc
->IsScriptEnabled() ? StyleScripting::Enabled
327 : StyleScripting::None
;
330 StyleDynamicRange
Gecko_MediaFeatures_DynamicRange(const Document
* aDocument
) {
331 // Bug 1759772: Once HDR color is available, update each platform
332 // LookAndFeel implementation to return StyleDynamicRange::High when
334 return StyleDynamicRange::Standard
;
337 StyleDynamicRange
Gecko_MediaFeatures_VideoDynamicRange(
338 const Document
* aDocument
) {
339 if (aDocument
->ShouldResistFingerprinting(RFPTarget::CSSVideoDynamicRange
)) {
340 return StyleDynamicRange::Standard
;
342 // video-dynamic-range: high has 3 requirements:
343 // 1) high peak brightness
344 // 2) high contrast ratio
345 // 3) color depth > 24
346 // We check the color depth requirement before asking the LookAndFeel
347 // if it is HDR capable.
348 if (nsDeviceContext
* dx
= GetDeviceContextFor(aDocument
)) {
349 if (dx
->GetDepth() > 24 &&
350 LookAndFeel::GetInt(LookAndFeel::IntID::VideoDynamicRange
)) {
351 return StyleDynamicRange::High
;
355 return StyleDynamicRange::Standard
;
358 static PointerCapabilities
GetPointerCapabilities(const Document
* aDocument
,
359 LookAndFeel::IntID aID
) {
360 MOZ_ASSERT(aID
== LookAndFeel::IntID::PrimaryPointerCapabilities
||
361 aID
== LookAndFeel::IntID::AllPointerCapabilities
);
362 MOZ_ASSERT(aDocument
);
364 if (dom::BrowsingContext
* bc
= aDocument
->GetBrowsingContext()) {
365 // The touch-events-override happens only for the Responsive Design Mode so
366 // that we don't need to care about ResistFingerprinting.
367 if (bc
->TouchEventsOverride() == dom::TouchEventsOverride::Enabled
) {
368 return PointerCapabilities::Coarse
;
372 // The default value for Desktop is mouse-type pointer, and for Android
374 const PointerCapabilities kDefaultCapabilities
=
376 PointerCapabilities::Coarse
;
378 PointerCapabilities::Fine
| PointerCapabilities::Hover
;
380 if (aDocument
->ShouldResistFingerprinting(
381 RFPTarget::CSSPointerCapabilities
)) {
382 return kDefaultCapabilities
;
386 nsresult rv
= LookAndFeel::GetInt(aID
, &intValue
);
388 return kDefaultCapabilities
;
391 return static_cast<PointerCapabilities
>(intValue
);
394 PointerCapabilities
Gecko_MediaFeatures_PrimaryPointerCapabilities(
395 const Document
* aDocument
) {
396 return GetPointerCapabilities(aDocument
,
397 LookAndFeel::IntID::PrimaryPointerCapabilities
);
400 PointerCapabilities
Gecko_MediaFeatures_AllPointerCapabilities(
401 const Document
* aDocument
) {
402 return GetPointerCapabilities(aDocument
,
403 LookAndFeel::IntID::AllPointerCapabilities
);
406 StyleGtkThemeFamily
Gecko_MediaFeatures_GtkThemeFamily() {
407 static_assert(int32_t(StyleGtkThemeFamily::Unknown
) == 0);
408 return StyleGtkThemeFamily(
409 LookAndFeel::GetInt(LookAndFeel::IntID::GTKThemeFamily
));