Bug 1839526 [wpt PR 40658] - Update wpt metadata, a=testonly
[gecko.git] / layout / style / nsMediaFeatures.cpp
blob581b6da29ba6bc321c549802b8bd5e3364724460
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 */
9 #include "nsGkAtoms.h"
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"
32 #ifdef XP_WIN
33 # include "mozilla/WindowsVersion.h"
34 #endif
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
49 if (!pc) {
50 return {};
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
70 // device size.
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
80 // really.
81 if (!pc) {
82 return {};
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();
92 nsSize size;
93 pc->DeviceContext()->GetDeviceSurfaceDimensions(size.width, size.height);
94 return size;
97 bool Gecko_MediaFeatures_WindowsNonNativeMenus(const Document* aDocument) {
98 return LookAndFeel::WindowsNonNativeMenusEnabled() ||
99 aDocument->ShouldAvoidNativeTheme();
102 bool Gecko_MediaFeatures_IsResourceDocument(const Document* aDocument) {
103 return aDocument->IsResourceDoc();
106 bool Gecko_MediaFeatures_ShouldAvoidNativeTheme(const Document* aDocument) {
107 return aDocument->ShouldAvoidNativeTheme();
110 bool Gecko_MediaFeatures_UseOverlayScrollbars(const Document* aDocument) {
111 nsPresContext* pc = aDocument->GetPresContext();
112 return pc && pc->UseOverlayScrollbars();
115 static nsDeviceContext* GetDeviceContextFor(const Document* aDocument) {
116 nsPresContext* pc = aDocument->GetPresContext();
117 if (!pc) {
118 return nullptr;
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();
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;
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;
141 nsPresContext* pc = aDocument->GetPresContext();
142 if (!pc) {
143 return 0;
145 nsIPrintSettings* ps = pc->GetPrintSettings();
146 if (!ps) {
147 return 0;
149 bool color = true;
150 ps->GetPrintInColor(&color);
151 return color ? 0 : kDefaultMonochromeBpp;
154 dom::ScreenColorGamut Gecko_MediaFeatures_ColorGamut(
155 const Document* aDocument) {
156 auto colorGamut = dom::ScreenColorGamut::Srgb;
157 if (!aDocument->ShouldResistFingerprinting(RFPTarget::CSSColorInfo)) {
158 if (auto* dx = GetDeviceContextFor(aDocument)) {
159 colorGamut = dx->GetColorGamut();
162 return colorGamut;
165 int32_t Gecko_MediaFeatures_GetColorDepth(const Document* aDocument) {
166 if (Gecko_MediaFeatures_GetMonochromeBitsPerPixel(aDocument) != 0) {
167 // If we're a monochrome device, then the color depth is zero.
168 return 0;
171 // Use depth of 24 when resisting fingerprinting, or when we're not being
172 // rendered.
173 int32_t depth = 24;
175 if (!aDocument->ShouldResistFingerprinting(RFPTarget::CSSColorInfo)) {
176 if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) {
177 depth = dx->GetDepth();
181 // The spec says to use bits *per color component*, so divide by 3,
182 // and round down, since the spec says to use the smallest when the
183 // color components differ.
184 return depth / 3;
187 float Gecko_MediaFeatures_GetResolution(const Document* aDocument) {
188 // We're returning resolution in terms of device pixels per css pixel, since
189 // that is the preferred unit for media queries of resolution. This avoids
190 // introducing precision error from conversion to and from less-used
191 // physical units like inches.
192 nsPresContext* pc = aDocument->GetPresContext();
193 if (!pc) {
194 return 1.;
197 if (pc->GetOverrideDPPX() > 0.) {
198 return pc->GetOverrideDPPX();
201 if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSResolution)) {
202 return pc->DeviceContext()->GetFullZoom();
204 // Get the actual device pixel ratio, which also takes zoom into account.
205 return float(AppUnitsPerCSSPixel()) /
206 pc->DeviceContext()->AppUnitsPerDevPixel();
209 static const Document* TopDocument(const Document* aDocument) {
210 const Document* current = aDocument;
211 while (const Document* parent = current->GetInProcessParentDocument()) {
212 current = parent;
214 return current;
217 StyleDisplayMode Gecko_MediaFeatures_GetDisplayMode(const Document* aDocument) {
218 const Document* rootDocument = TopDocument(aDocument);
220 nsCOMPtr<nsISupports> container = rootDocument->GetContainer();
221 if (nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container)) {
222 nsCOMPtr<nsIWidget> mainWidget;
223 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
224 if (mainWidget && mainWidget->SizeMode() == nsSizeMode_Fullscreen) {
225 return StyleDisplayMode::Fullscreen;
229 static_assert(static_cast<int32_t>(DisplayMode::Browser) ==
230 static_cast<int32_t>(StyleDisplayMode::Browser) &&
231 static_cast<int32_t>(DisplayMode::Minimal_ui) ==
232 static_cast<int32_t>(StyleDisplayMode::MinimalUi) &&
233 static_cast<int32_t>(DisplayMode::Standalone) ==
234 static_cast<int32_t>(StyleDisplayMode::Standalone) &&
235 static_cast<int32_t>(DisplayMode::Fullscreen) ==
236 static_cast<int32_t>(StyleDisplayMode::Fullscreen),
237 "DisplayMode must mach nsStyleConsts.h");
239 dom::BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
240 if (!browsingContext) {
241 return StyleDisplayMode::Browser;
243 return static_cast<StyleDisplayMode>(browsingContext->DisplayMode());
246 bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
247 switch (aPlatform) {
248 #if defined(XP_WIN)
249 case StylePlatform::Windows:
250 return true;
251 case StylePlatform::WindowsWin10:
252 case StylePlatform::WindowsWin7:
253 case StylePlatform::WindowsWin8: {
254 if (IsWin10OrLater()) {
255 return aPlatform == StylePlatform::WindowsWin10;
257 if (IsWin8OrLater()) {
258 return aPlatform == StylePlatform::WindowsWin8;
260 return aPlatform == StylePlatform::WindowsWin7;
262 #elif defined(ANDROID)
263 case StylePlatform::Android:
264 return true;
265 #elif defined(MOZ_WIDGET_GTK)
266 case StylePlatform::Linux:
267 return true;
268 #elif defined(XP_MACOSX)
269 case StylePlatform::Macos:
270 return true;
271 #else
272 # error "Unknown platform?"
273 #endif
274 default:
275 return false;
279 bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
280 if (aDocument->ShouldResistFingerprinting(
281 RFPTarget::CSSPrefersReducedMotion)) {
282 return false;
284 return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
287 bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) {
288 if (aDocument->ShouldResistFingerprinting(
289 RFPTarget::CSSPrefersReducedTransparency)) {
290 return false;
292 return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency,
293 0) == 1;
296 StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
297 const Document* aDocument, bool aUseContent) {
298 auto scheme = aUseContent ? LookAndFeel::PreferredColorSchemeForContent()
299 : aDocument->PreferredColorScheme();
300 return scheme == ColorScheme::Dark ? StylePrefersColorScheme::Dark
301 : StylePrefersColorScheme::Light;
304 // Neither Linux, Windows, nor Mac have a way to indicate that low contrast is
305 // preferred so we use the presence of an accessibility theme or forced colors
306 // as a signal.
307 StylePrefersContrast Gecko_MediaFeatures_PrefersContrast(
308 const Document* aDocument) {
309 if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSPrefersContrast)) {
310 return StylePrefersContrast::NoPreference;
312 const auto& prefs = PreferenceSheet::PrefsFor(*aDocument);
313 if (!prefs.mUseAccessibilityTheme && prefs.mUseDocumentColors) {
314 return StylePrefersContrast::NoPreference;
316 const auto& colors = prefs.ColorsFor(ColorScheme::Light);
317 float ratio = RelativeLuminanceUtils::ContrastRatio(colors.mDefaultBackground,
318 colors.mDefault);
319 // https://www.w3.org/TR/WCAG21/#contrast-minimum
320 if (ratio < 4.5f) {
321 return StylePrefersContrast::Less;
323 // https://www.w3.org/TR/WCAG21/#contrast-enhanced
324 if (ratio >= 7.0f) {
325 return StylePrefersContrast::More;
327 return StylePrefersContrast::Custom;
330 bool Gecko_MediaFeatures_InvertedColors(const Document* aDocument) {
331 if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSInvertedColors)) {
332 return false;
334 return LookAndFeel::GetInt(LookAndFeel::IntID::InvertedColors, 0) == 1;
337 StyleScripting Gecko_MediaFeatures_Scripting(const Document* aDocument) {
338 const auto* doc = aDocument;
339 if (aDocument->IsStaticDocument()) {
340 doc = aDocument->GetOriginalDocument();
343 return doc->IsScriptEnabled() ? StyleScripting::Enabled
344 : StyleScripting::None;
347 StyleDynamicRange Gecko_MediaFeatures_DynamicRange(const Document* aDocument) {
348 // Bug 1759772: Once HDR color is available, update each platform
349 // LookAndFeel implementation to return StyleDynamicRange::High when
350 // appropriate.
351 return StyleDynamicRange::Standard;
354 StyleDynamicRange Gecko_MediaFeatures_VideoDynamicRange(
355 const Document* aDocument) {
356 if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSVideoDynamicRange)) {
357 return StyleDynamicRange::Standard;
359 // video-dynamic-range: high has 3 requirements:
360 // 1) high peak brightness
361 // 2) high contrast ratio
362 // 3) color depth > 24
363 // We check the color depth requirement before asking the LookAndFeel
364 // if it is HDR capable.
365 if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) {
366 if (dx->GetDepth() > 24 &&
367 LookAndFeel::GetInt(LookAndFeel::IntID::VideoDynamicRange)) {
368 return StyleDynamicRange::High;
372 return StyleDynamicRange::Standard;
375 static PointerCapabilities GetPointerCapabilities(const Document* aDocument,
376 LookAndFeel::IntID aID) {
377 MOZ_ASSERT(aID == LookAndFeel::IntID::PrimaryPointerCapabilities ||
378 aID == LookAndFeel::IntID::AllPointerCapabilities);
379 MOZ_ASSERT(aDocument);
381 if (dom::BrowsingContext* bc = aDocument->GetBrowsingContext()) {
382 // The touch-events-override happens only for the Responsive Design Mode so
383 // that we don't need to care about ResistFingerprinting.
384 if (bc->TouchEventsOverride() == dom::TouchEventsOverride::Enabled) {
385 return PointerCapabilities::Coarse;
389 // The default value for Desktop is mouse-type pointer, and for Android
390 // a coarse pointer.
391 const PointerCapabilities kDefaultCapabilities =
392 #ifdef ANDROID
393 PointerCapabilities::Coarse;
394 #else
395 PointerCapabilities::Fine | PointerCapabilities::Hover;
396 #endif
397 if (aDocument->ShouldResistFingerprinting(
398 RFPTarget::CSSPointerCapabilities)) {
399 return kDefaultCapabilities;
402 int32_t intValue;
403 nsresult rv = LookAndFeel::GetInt(aID, &intValue);
404 if (NS_FAILED(rv)) {
405 return kDefaultCapabilities;
408 return static_cast<PointerCapabilities>(intValue);
411 PointerCapabilities Gecko_MediaFeatures_PrimaryPointerCapabilities(
412 const Document* aDocument) {
413 return GetPointerCapabilities(aDocument,
414 LookAndFeel::IntID::PrimaryPointerCapabilities);
417 PointerCapabilities Gecko_MediaFeatures_AllPointerCapabilities(
418 const Document* aDocument) {
419 return GetPointerCapabilities(aDocument,
420 LookAndFeel::IntID::AllPointerCapabilities);