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 "nsMediaFeatures.h"
10 #include "nsGkAtoms.h"
11 #include "nsCSSKeywords.h"
12 #include "nsStyleConsts.h"
13 #include "nsPresContext.h"
14 #include "nsCSSValue.h"
15 #ifdef XP_WIN
16 #include "mozilla/LookAndFeel.h"
17 #endif
18 #include "nsCSSRuleProcessor.h"
19 #include "nsDeviceContext.h"
20 #include "nsIBaseWindow.h"
21 #include "nsIDocument.h"
22 #include "nsContentUtils.h"
23 #include "mozilla/StyleSheet.h"
24 #include "mozilla/StyleSheetInlines.h"
25 
26 using namespace mozilla;
27 
28 static const nsCSSProps::KTableEntry kOrientationKeywords[] = {
29   { eCSSKeyword_portrait,                 NS_STYLE_ORIENTATION_PORTRAIT },
30   { eCSSKeyword_landscape,                NS_STYLE_ORIENTATION_LANDSCAPE },
31   { eCSSKeyword_UNKNOWN,                  -1 }
32 };
33 
34 static const nsCSSProps::KTableEntry kScanKeywords[] = {
35   { eCSSKeyword_progressive,              NS_STYLE_SCAN_PROGRESSIVE },
36   { eCSSKeyword_interlace,                NS_STYLE_SCAN_INTERLACE },
37   { eCSSKeyword_UNKNOWN,                  -1 }
38 };
39 
40 static const nsCSSProps::KTableEntry kDisplayModeKeywords[] = {
41   { eCSSKeyword_browser,                 NS_STYLE_DISPLAY_MODE_BROWSER },
42   { eCSSKeyword_minimal_ui,              NS_STYLE_DISPLAY_MODE_MINIMAL_UI },
43   { eCSSKeyword_standalone,              NS_STYLE_DISPLAY_MODE_STANDALONE },
44   { eCSSKeyword_fullscreen,              NS_STYLE_DISPLAY_MODE_FULLSCREEN },
45   { eCSSKeyword_UNKNOWN,                 -1 }
46 };
47 
48 #ifdef XP_WIN
49 struct WindowsThemeName {
50   LookAndFeel::WindowsTheme id;
51   const wchar_t* name;
52 };
53 
54 // Windows theme identities used in the -moz-windows-theme media query.
55 const WindowsThemeName themeStrings[] = {
56   { LookAndFeel::eWindowsTheme_Aero,       L"aero" },
57   { LookAndFeel::eWindowsTheme_AeroLite,   L"aero-lite" },
58   { LookAndFeel::eWindowsTheme_LunaBlue,   L"luna-blue" },
59   { LookAndFeel::eWindowsTheme_LunaOlive,  L"luna-olive" },
60   { LookAndFeel::eWindowsTheme_LunaSilver, L"luna-silver" },
61   { LookAndFeel::eWindowsTheme_Royale,     L"royale" },
62   { LookAndFeel::eWindowsTheme_Zune,       L"zune" },
63   { LookAndFeel::eWindowsTheme_Generic,    L"generic" }
64 };
65 
66 struct OperatingSystemVersionInfo {
67   LookAndFeel::OperatingSystemVersion id;
68   const wchar_t* name;
69 };
70 
71 // Os version identities used in the -moz-os-version media query.
72 const OperatingSystemVersionInfo osVersionStrings[] = {
73   { LookAndFeel::eOperatingSystemVersion_WindowsXP,     L"windows-xp" },
74   { LookAndFeel::eOperatingSystemVersion_WindowsVista,  L"windows-vista" },
75   { LookAndFeel::eOperatingSystemVersion_Windows7,      L"windows-win7" },
76   { LookAndFeel::eOperatingSystemVersion_Windows8,      L"windows-win8" },
77   { LookAndFeel::eOperatingSystemVersion_Windows10,     L"windows-win10" }
78 };
79 #endif
80 
81 // A helper for four features below
82 static nsSize
GetSize(nsPresContext * aPresContext)83 GetSize(nsPresContext* aPresContext)
84 {
85   nsSize size;
86   if (aPresContext->IsRootPaginatedDocument())
87     // We want the page size, including unprintable areas and margins.
88     size = aPresContext->GetPageSize();
89   else
90     size = aPresContext->GetVisibleArea().Size();
91   return size;
92 }
93 
94 static nsresult
GetWidth(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)95 GetWidth(nsPresContext* aPresContext, const nsMediaFeature*,
96          nsCSSValue& aResult)
97 {
98   nsSize size = GetSize(aPresContext);
99   float pixelWidth = aPresContext->AppUnitsToFloatCSSPixels(size.width);
100   aResult.SetFloatValue(pixelWidth, eCSSUnit_Pixel);
101   return NS_OK;
102 }
103 
104 static nsresult
GetHeight(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)105 GetHeight(nsPresContext* aPresContext, const nsMediaFeature*,
106           nsCSSValue& aResult)
107 {
108   nsSize size = GetSize(aPresContext);
109   float pixelHeight = aPresContext->AppUnitsToFloatCSSPixels(size.height);
110   aResult.SetFloatValue(pixelHeight, eCSSUnit_Pixel);
111   return NS_OK;
112 }
113 
114 inline static nsDeviceContext*
GetDeviceContextFor(nsPresContext * aPresContext)115 GetDeviceContextFor(nsPresContext* aPresContext)
116 {
117   // It would be nice to call
118   // nsLayoutUtils::GetDeviceContextForScreenInfo here, except for two
119   // things:  (1) it can flush, and flushing is bad here, and (2) it
120   // doesn't really get us consistency in multi-monitor situations
121   // *anyway*.
122   return aPresContext->DeviceContext();
123 }
124 
125 static bool
ShouldResistFingerprinting(nsPresContext * aPresContext)126 ShouldResistFingerprinting(nsPresContext* aPresContext)
127 {
128   return nsContentUtils::ShouldResistFingerprinting(aPresContext->GetDocShell());
129 }
130 
131 // A helper for three features below.
132 static nsSize
GetDeviceSize(nsPresContext * aPresContext)133 GetDeviceSize(nsPresContext* aPresContext)
134 {
135   nsSize size;
136 
137   if (ShouldResistFingerprinting(aPresContext) || aPresContext->IsDeviceSizePageSize()) {
138     size = GetSize(aPresContext);
139   } else if (aPresContext->IsRootPaginatedDocument()) {
140     // We want the page size, including unprintable areas and margins.
141     // XXX The spec actually says we want the "page sheet size", but
142     // how is that different?
143     size = aPresContext->GetPageSize();
144   } else {
145     GetDeviceContextFor(aPresContext)->
146       GetDeviceSurfaceDimensions(size.width, size.height);
147   }
148   return size;
149 }
150 
151 static nsresult
GetDeviceWidth(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)152 GetDeviceWidth(nsPresContext* aPresContext, const nsMediaFeature*,
153                nsCSSValue& aResult)
154 {
155   nsSize size = GetDeviceSize(aPresContext);
156   float pixelWidth = aPresContext->AppUnitsToFloatCSSPixels(size.width);
157   aResult.SetFloatValue(pixelWidth, eCSSUnit_Pixel);
158   return NS_OK;
159 }
160 
161 static nsresult
GetDeviceHeight(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)162 GetDeviceHeight(nsPresContext* aPresContext, const nsMediaFeature*,
163                 nsCSSValue& aResult)
164 {
165   nsSize size = GetDeviceSize(aPresContext);
166   float pixelHeight = aPresContext->AppUnitsToFloatCSSPixels(size.height);
167   aResult.SetFloatValue(pixelHeight, eCSSUnit_Pixel);
168   return NS_OK;
169 }
170 
171 static nsresult
GetOrientation(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)172 GetOrientation(nsPresContext* aPresContext, const nsMediaFeature*,
173                nsCSSValue& aResult)
174 {
175   nsSize size = GetSize(aPresContext);
176   int32_t orientation;
177   if (size.width > size.height) {
178     orientation = NS_STYLE_ORIENTATION_LANDSCAPE;
179   } else {
180     // Per spec, square viewports should be 'portrait'
181     orientation = NS_STYLE_ORIENTATION_PORTRAIT;
182   }
183 
184   aResult.SetIntValue(orientation, eCSSUnit_Enumerated);
185   return NS_OK;
186 }
187 
188 static nsresult
GetDeviceOrientation(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)189 GetDeviceOrientation(nsPresContext* aPresContext, const nsMediaFeature*,
190                      nsCSSValue& aResult)
191 {
192   nsSize size = GetDeviceSize(aPresContext);
193   int32_t orientation;
194   if (size.width > size.height) {
195     orientation = NS_STYLE_ORIENTATION_LANDSCAPE;
196   } else {
197     // Per spec, square viewports should be 'portrait'
198     orientation = NS_STYLE_ORIENTATION_PORTRAIT;
199   }
200 
201   aResult.SetIntValue(orientation, eCSSUnit_Enumerated);
202   return NS_OK;
203 }
204 
205 static nsresult
GetIsResourceDocument(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)206 GetIsResourceDocument(nsPresContext* aPresContext, const nsMediaFeature*,
207                       nsCSSValue& aResult)
208 {
209   nsIDocument* doc = aPresContext->Document();
210   aResult.SetIntValue(doc && doc->IsResourceDoc() ? 1 : 0, eCSSUnit_Integer);
211   return NS_OK;
212 }
213 
214 // Helper for two features below
215 static nsresult
MakeArray(const nsSize & aSize,nsCSSValue & aResult)216 MakeArray(const nsSize& aSize, nsCSSValue& aResult)
217 {
218   RefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
219 
220   a->Item(0).SetIntValue(aSize.width, eCSSUnit_Integer);
221   a->Item(1).SetIntValue(aSize.height, eCSSUnit_Integer);
222 
223   aResult.SetArrayValue(a, eCSSUnit_Array);
224   return NS_OK;
225 }
226 
227 static nsresult
GetAspectRatio(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)228 GetAspectRatio(nsPresContext* aPresContext, const nsMediaFeature*,
229                nsCSSValue& aResult)
230 {
231   return MakeArray(GetSize(aPresContext), aResult);
232 }
233 
234 static nsresult
GetDeviceAspectRatio(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)235 GetDeviceAspectRatio(nsPresContext* aPresContext, const nsMediaFeature*,
236                      nsCSSValue& aResult)
237 {
238   return MakeArray(GetDeviceSize(aPresContext), aResult);
239 }
240 
241 static nsresult
GetColor(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)242 GetColor(nsPresContext* aPresContext, const nsMediaFeature*,
243          nsCSSValue& aResult)
244 {
245   uint32_t depth = 24; // Use depth of 24 when resisting fingerprinting.
246 
247   if (!ShouldResistFingerprinting(aPresContext)) {
248     // FIXME:  This implementation is bogus.  nsDeviceContext
249     // doesn't provide reliable information (should be fixed in bug
250     // 424386).
251     // FIXME: On a monochrome device, return 0!
252     nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
253     dx->GetDepth(depth);
254   }
255 
256   // The spec says to use bits *per color component*, so divide by 3,
257   // and round down, since the spec says to use the smallest when the
258   // color components differ.
259   depth /= 3;
260   aResult.SetIntValue(int32_t(depth), eCSSUnit_Integer);
261   return NS_OK;
262 }
263 
264 static nsresult
GetColorIndex(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)265 GetColorIndex(nsPresContext* aPresContext, const nsMediaFeature*,
266               nsCSSValue& aResult)
267 {
268   // We should return zero if the device does not use a color lookup
269   // table.  Stuart says that our handling of displays with 8-bit
270   // color is bad enough that we never change the lookup table to
271   // match what we're trying to display, so perhaps we should always
272   // return zero.  Given that there isn't any better information
273   // exposed, we don't have much other choice.
274   aResult.SetIntValue(0, eCSSUnit_Integer);
275   return NS_OK;
276 }
277 
278 static nsresult
GetMonochrome(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)279 GetMonochrome(nsPresContext* aPresContext, const nsMediaFeature*,
280               nsCSSValue& aResult)
281 {
282   // For color devices we should return 0.
283   // FIXME: On a monochrome device, return the actual color depth, not
284   // 0!
285   aResult.SetIntValue(0, eCSSUnit_Integer);
286   return NS_OK;
287 }
288 
289 static nsresult
GetResolution(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)290 GetResolution(nsPresContext* aPresContext, const nsMediaFeature*,
291               nsCSSValue& aResult)
292 {
293   float dpi = 96; // Use 96 when resisting fingerprinting.
294 
295   if (!ShouldResistFingerprinting(aPresContext)) {
296     // Resolution measures device pixels per CSS (inch/cm/pixel).  We
297     // return it in device pixels per CSS inches.
298     dpi = float(nsPresContext::AppUnitsPerCSSInch()) /
299           float(aPresContext->AppUnitsPerDevPixel());
300   }
301 
302   aResult.SetFloatValue(dpi, eCSSUnit_Inch);
303   return NS_OK;
304 }
305 
306 static nsresult
GetScan(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)307 GetScan(nsPresContext* aPresContext, const nsMediaFeature*,
308         nsCSSValue& aResult)
309 {
310   // Since Gecko doesn't support the 'tv' media type, the 'scan'
311   // feature is never present.
312   aResult.Reset();
313   return NS_OK;
314 }
315 
316 static nsresult
GetDisplayMode(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)317 GetDisplayMode(nsPresContext* aPresContext, const nsMediaFeature*,
318                nsCSSValue& aResult)
319 {
320   nsCOMPtr<nsISupports> container;
321   if (aPresContext) {
322     // Calling GetRootPresContext() can be slow, so make sure to call it
323     // just once.
324     nsRootPresContext* root = aPresContext->GetRootPresContext();
325     if (root && root->Document()) {
326       container = root->Document()->GetContainer();
327     }
328   }
329   nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
330   if (!baseWindow) {
331     aResult.SetIntValue(NS_STYLE_DISPLAY_MODE_BROWSER, eCSSUnit_Enumerated);
332     return NS_OK;
333   }
334   nsCOMPtr<nsIWidget> mainWidget;
335   baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
336   int32_t displayMode;
337   nsSizeMode mode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
338   // Background tabs are always in 'browser' mode for now.
339   // If new modes are supported, please ensure not cause the regression in
340   // Bug 1259641.
341   switch (mode) {
342     case nsSizeMode_Fullscreen:
343       displayMode = NS_STYLE_DISPLAY_MODE_FULLSCREEN;
344       break;
345     default:
346       displayMode = NS_STYLE_DISPLAY_MODE_BROWSER;
347       break;
348   }
349 
350   aResult.SetIntValue(displayMode, eCSSUnit_Enumerated);
351   return NS_OK;
352 }
353 
354 static nsresult
GetGrid(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)355 GetGrid(nsPresContext* aPresContext, const nsMediaFeature*,
356         nsCSSValue& aResult)
357 {
358   // Gecko doesn't support grid devices (e.g., ttys), so the 'grid'
359   // feature is always 0.
360   aResult.SetIntValue(0, eCSSUnit_Integer);
361   return NS_OK;
362 }
363 
364 static nsresult
GetDevicePixelRatio(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)365 GetDevicePixelRatio(nsPresContext* aPresContext, const nsMediaFeature*,
366                     nsCSSValue& aResult)
367 {
368   if (!ShouldResistFingerprinting(aPresContext)) {
369     float ratio = aPresContext->CSSPixelsToDevPixels(1.0f);
370     aResult.SetFloatValue(ratio, eCSSUnit_Number);
371   } else {
372     aResult.SetFloatValue(1.0, eCSSUnit_Number);
373   }
374   return NS_OK;
375 }
376 
377 static nsresult
GetTransform3d(nsPresContext * aPresContext,const nsMediaFeature *,nsCSSValue & aResult)378 GetTransform3d(nsPresContext* aPresContext, const nsMediaFeature*,
379                nsCSSValue& aResult)
380 {
381   // Gecko supports 3d transforms, so this feature is always 1.
382   aResult.SetIntValue(1, eCSSUnit_Integer);
383   return NS_OK;
384 }
385 
386 static nsresult
GetSystemMetric(nsPresContext * aPresContext,const nsMediaFeature * aFeature,nsCSSValue & aResult)387 GetSystemMetric(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
388                 nsCSSValue& aResult)
389 {
390   aResult.Reset();
391   if (ShouldResistFingerprinting(aPresContext)) {
392     // If "privacy.resistFingerprinting" is enabled, then we simply don't
393     // return any system-backed media feature values. (No spoofed values returned.)
394     return NS_OK;
395   }
396 
397   MOZ_ASSERT(aFeature->mValueType == nsMediaFeature::eBoolInteger,
398              "unexpected type");
399   nsIAtom *metricAtom = *aFeature->mData.mMetric;
400   bool hasMetric = nsCSSRuleProcessor::HasSystemMetric(metricAtom);
401   aResult.SetIntValue(hasMetric ? 1 : 0, eCSSUnit_Integer);
402   return NS_OK;
403 }
404 
405 static nsresult
GetWindowsTheme(nsPresContext * aPresContext,const nsMediaFeature * aFeature,nsCSSValue & aResult)406 GetWindowsTheme(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
407                 nsCSSValue& aResult)
408 {
409   aResult.Reset();
410   if (ShouldResistFingerprinting(aPresContext)) {
411     return NS_OK;
412   }
413 
414 #ifdef XP_WIN
415   uint8_t windowsThemeId =
416     nsCSSRuleProcessor::GetWindowsThemeIdentifier();
417 
418   // Classic mode should fail to match.
419   if (windowsThemeId == LookAndFeel::eWindowsTheme_Classic)
420     return NS_OK;
421 
422   // Look up the appropriate theme string
423   for (size_t i = 0; i < ArrayLength(themeStrings); ++i) {
424     if (windowsThemeId == themeStrings[i].id) {
425       aResult.SetStringValue(nsDependentString(themeStrings[i].name),
426                              eCSSUnit_Ident);
427       break;
428     }
429   }
430 #endif
431   return NS_OK;
432 }
433 
434 static nsresult
GetOperatingSystemVersion(nsPresContext * aPresContext,const nsMediaFeature * aFeature,nsCSSValue & aResult)435 GetOperatingSystemVersion(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
436                          nsCSSValue& aResult)
437 {
438   aResult.Reset();
439   if (ShouldResistFingerprinting(aPresContext)) {
440     return NS_OK;
441   }
442 
443 #ifdef XP_WIN
444   int32_t metricResult;
445   if (NS_SUCCEEDED(
446         LookAndFeel::GetInt(LookAndFeel::eIntID_OperatingSystemVersionIdentifier,
447                             &metricResult))) {
448     for (size_t i = 0; i < ArrayLength(osVersionStrings); ++i) {
449       if (metricResult == osVersionStrings[i].id) {
450         aResult.SetStringValue(nsDependentString(osVersionStrings[i].name),
451                                eCSSUnit_Ident);
452         break;
453       }
454     }
455   }
456 #endif
457   return NS_OK;
458 }
459 
460 static nsresult
GetIsGlyph(nsPresContext * aPresContext,const nsMediaFeature * aFeature,nsCSSValue & aResult)461 GetIsGlyph(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
462           nsCSSValue& aResult)
463 {
464   aResult.SetIntValue(aPresContext->IsGlyph() ? 1 : 0, eCSSUnit_Integer);
465   return NS_OK;
466 }
467 
468 /*
469  * Adding new media features requires (1) adding the new feature to this
470  * array, with appropriate entries (and potentially any new code needed
471  * to support new types in these entries and (2) ensuring that either
472  * nsPresContext::MediaFeatureValuesChanged or
473  * nsPresContext::PostMediaFeatureValuesChangedEvent is called when the
474  * value that would be returned by the entry's mGetter changes.
475  */
476 
477 /* static */ const nsMediaFeature
478 nsMediaFeatures::features[] = {
479   {
480     &nsGkAtoms::width,
481     nsMediaFeature::eMinMaxAllowed,
482     nsMediaFeature::eLength,
483     nsMediaFeature::eNoRequirements,
484     { nullptr },
485     GetWidth
486   },
487   {
488     &nsGkAtoms::height,
489     nsMediaFeature::eMinMaxAllowed,
490     nsMediaFeature::eLength,
491     nsMediaFeature::eNoRequirements,
492     { nullptr },
493     GetHeight
494   },
495   {
496     &nsGkAtoms::deviceWidth,
497     nsMediaFeature::eMinMaxAllowed,
498     nsMediaFeature::eLength,
499     nsMediaFeature::eNoRequirements,
500     { nullptr },
501     GetDeviceWidth
502   },
503   {
504     &nsGkAtoms::deviceHeight,
505     nsMediaFeature::eMinMaxAllowed,
506     nsMediaFeature::eLength,
507     nsMediaFeature::eNoRequirements,
508     { nullptr },
509     GetDeviceHeight
510   },
511   {
512     &nsGkAtoms::orientation,
513     nsMediaFeature::eMinMaxNotAllowed,
514     nsMediaFeature::eEnumerated,
515     nsMediaFeature::eNoRequirements,
516     { kOrientationKeywords },
517     GetOrientation
518   },
519   {
520     &nsGkAtoms::aspectRatio,
521     nsMediaFeature::eMinMaxAllowed,
522     nsMediaFeature::eIntRatio,
523     nsMediaFeature::eNoRequirements,
524     { nullptr },
525     GetAspectRatio
526   },
527   {
528     &nsGkAtoms::deviceAspectRatio,
529     nsMediaFeature::eMinMaxAllowed,
530     nsMediaFeature::eIntRatio,
531     nsMediaFeature::eNoRequirements,
532     { nullptr },
533     GetDeviceAspectRatio
534   },
535   {
536     &nsGkAtoms::color,
537     nsMediaFeature::eMinMaxAllowed,
538     nsMediaFeature::eInteger,
539     nsMediaFeature::eNoRequirements,
540     { nullptr },
541     GetColor
542   },
543   {
544     &nsGkAtoms::colorIndex,
545     nsMediaFeature::eMinMaxAllowed,
546     nsMediaFeature::eInteger,
547     nsMediaFeature::eNoRequirements,
548     { nullptr },
549     GetColorIndex
550   },
551   {
552     &nsGkAtoms::monochrome,
553     nsMediaFeature::eMinMaxAllowed,
554     nsMediaFeature::eInteger,
555     nsMediaFeature::eNoRequirements,
556     { nullptr },
557     GetMonochrome
558   },
559   {
560     &nsGkAtoms::resolution,
561     nsMediaFeature::eMinMaxAllowed,
562     nsMediaFeature::eResolution,
563     nsMediaFeature::eNoRequirements,
564     { nullptr },
565     GetResolution
566   },
567   {
568     &nsGkAtoms::scan,
569     nsMediaFeature::eMinMaxNotAllowed,
570     nsMediaFeature::eEnumerated,
571     nsMediaFeature::eNoRequirements,
572     { kScanKeywords },
573     GetScan
574   },
575   {
576     &nsGkAtoms::grid,
577     nsMediaFeature::eMinMaxNotAllowed,
578     nsMediaFeature::eBoolInteger,
579     nsMediaFeature::eNoRequirements,
580     { nullptr },
581     GetGrid
582   },
583   {
584     &nsGkAtoms::displayMode,
585     nsMediaFeature::eMinMaxNotAllowed,
586     nsMediaFeature::eEnumerated,
587     nsMediaFeature::eNoRequirements,
588     { kDisplayModeKeywords },
589     GetDisplayMode
590   },
591 
592   // Webkit extensions that we support for de-facto web compatibility
593   // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):
594   {
595     &nsGkAtoms::devicePixelRatio,
596     nsMediaFeature::eMinMaxAllowed,
597     nsMediaFeature::eFloat,
598     nsMediaFeature::eHasWebkitPrefix |
599       nsMediaFeature::eWebkitDevicePixelRatioPrefEnabled,
600     { nullptr },
601     GetDevicePixelRatio
602   },
603   // -webkit-transform-3d:
604   {
605     &nsGkAtoms::transform_3d,
606     nsMediaFeature::eMinMaxNotAllowed,
607     nsMediaFeature::eBoolInteger,
608     nsMediaFeature::eHasWebkitPrefix,
609     { nullptr },
610     GetTransform3d
611   },
612 
613   // Mozilla extensions
614   {
615     &nsGkAtoms::_moz_device_pixel_ratio,
616     nsMediaFeature::eMinMaxAllowed,
617     nsMediaFeature::eFloat,
618     nsMediaFeature::eNoRequirements,
619     { nullptr },
620     GetDevicePixelRatio
621   },
622   {
623     &nsGkAtoms::_moz_device_orientation,
624     nsMediaFeature::eMinMaxNotAllowed,
625     nsMediaFeature::eEnumerated,
626     nsMediaFeature::eNoRequirements,
627     { kOrientationKeywords },
628     GetDeviceOrientation
629   },
630   {
631     &nsGkAtoms::_moz_is_resource_document,
632     nsMediaFeature::eMinMaxNotAllowed,
633     nsMediaFeature::eBoolInteger,
634     nsMediaFeature::eNoRequirements,
635     { nullptr },
636     GetIsResourceDocument
637   },
638   {
639     &nsGkAtoms::_moz_color_picker_available,
640     nsMediaFeature::eMinMaxNotAllowed,
641     nsMediaFeature::eBoolInteger,
642     nsMediaFeature::eNoRequirements,
643     { &nsGkAtoms::color_picker_available },
644     GetSystemMetric
645   },
646   {
647     &nsGkAtoms::_moz_scrollbar_start_backward,
648     nsMediaFeature::eMinMaxNotAllowed,
649     nsMediaFeature::eBoolInteger,
650     nsMediaFeature::eNoRequirements,
651     { &nsGkAtoms::scrollbar_start_backward },
652     GetSystemMetric
653   },
654   {
655     &nsGkAtoms::_moz_scrollbar_start_forward,
656     nsMediaFeature::eMinMaxNotAllowed,
657     nsMediaFeature::eBoolInteger,
658     nsMediaFeature::eNoRequirements,
659     { &nsGkAtoms::scrollbar_start_forward },
660     GetSystemMetric
661   },
662   {
663     &nsGkAtoms::_moz_scrollbar_end_backward,
664     nsMediaFeature::eMinMaxNotAllowed,
665     nsMediaFeature::eBoolInteger,
666     nsMediaFeature::eNoRequirements,
667     { &nsGkAtoms::scrollbar_end_backward },
668     GetSystemMetric
669   },
670   {
671     &nsGkAtoms::_moz_scrollbar_end_forward,
672     nsMediaFeature::eMinMaxNotAllowed,
673     nsMediaFeature::eBoolInteger,
674     nsMediaFeature::eNoRequirements,
675     { &nsGkAtoms::scrollbar_end_forward },
676     GetSystemMetric
677   },
678   {
679     &nsGkAtoms::_moz_scrollbar_thumb_proportional,
680     nsMediaFeature::eMinMaxNotAllowed,
681     nsMediaFeature::eBoolInteger,
682     nsMediaFeature::eNoRequirements,
683     { &nsGkAtoms::scrollbar_thumb_proportional },
684     GetSystemMetric
685   },
686   {
687     &nsGkAtoms::_moz_overlay_scrollbars,
688     nsMediaFeature::eMinMaxNotAllowed,
689     nsMediaFeature::eBoolInteger,
690     nsMediaFeature::eNoRequirements,
691     { &nsGkAtoms::overlay_scrollbars },
692     GetSystemMetric
693   },
694   {
695     &nsGkAtoms::_moz_windows_default_theme,
696     nsMediaFeature::eMinMaxNotAllowed,
697     nsMediaFeature::eBoolInteger,
698     nsMediaFeature::eNoRequirements,
699     { &nsGkAtoms::windows_default_theme },
700     GetSystemMetric
701   },
702   {
703     &nsGkAtoms::_moz_mac_graphite_theme,
704     nsMediaFeature::eMinMaxNotAllowed,
705     nsMediaFeature::eBoolInteger,
706     nsMediaFeature::eNoRequirements,
707     { &nsGkAtoms::mac_graphite_theme },
708     GetSystemMetric
709   },
710   {
711     &nsGkAtoms::_moz_mac_yosemite_theme,
712     nsMediaFeature::eMinMaxNotAllowed,
713     nsMediaFeature::eBoolInteger,
714     nsMediaFeature::eNoRequirements,
715     { &nsGkAtoms::mac_yosemite_theme },
716     GetSystemMetric
717   },
718   {
719     &nsGkAtoms::_moz_windows_compositor,
720     nsMediaFeature::eMinMaxNotAllowed,
721     nsMediaFeature::eBoolInteger,
722     nsMediaFeature::eNoRequirements,
723     { &nsGkAtoms::windows_compositor },
724     GetSystemMetric
725   },
726   {
727     &nsGkAtoms::_moz_windows_classic,
728     nsMediaFeature::eMinMaxNotAllowed,
729     nsMediaFeature::eBoolInteger,
730     nsMediaFeature::eNoRequirements,
731     { &nsGkAtoms::windows_classic },
732     GetSystemMetric
733   },
734   {
735     &nsGkAtoms::_moz_windows_glass,
736     nsMediaFeature::eMinMaxNotAllowed,
737     nsMediaFeature::eBoolInteger,
738     nsMediaFeature::eNoRequirements,
739     { &nsGkAtoms::windows_glass },
740     GetSystemMetric
741   },
742   {
743     &nsGkAtoms::_moz_touch_enabled,
744     nsMediaFeature::eMinMaxNotAllowed,
745     nsMediaFeature::eBoolInteger,
746     nsMediaFeature::eNoRequirements,
747     { &nsGkAtoms::touch_enabled },
748     GetSystemMetric
749   },
750   {
751     &nsGkAtoms::_moz_menubar_drag,
752     nsMediaFeature::eMinMaxNotAllowed,
753     nsMediaFeature::eBoolInteger,
754     nsMediaFeature::eNoRequirements,
755     { &nsGkAtoms::menubar_drag },
756     GetSystemMetric
757   },
758   {
759     &nsGkAtoms::_moz_windows_theme,
760     nsMediaFeature::eMinMaxNotAllowed,
761     nsMediaFeature::eIdent,
762     nsMediaFeature::eNoRequirements,
763     { nullptr },
764     GetWindowsTheme
765   },
766   {
767     &nsGkAtoms::_moz_os_version,
768     nsMediaFeature::eMinMaxNotAllowed,
769     nsMediaFeature::eIdent,
770     nsMediaFeature::eNoRequirements,
771     { nullptr },
772     GetOperatingSystemVersion
773   },
774 
775   {
776     &nsGkAtoms::_moz_swipe_animation_enabled,
777     nsMediaFeature::eMinMaxNotAllowed,
778     nsMediaFeature::eBoolInteger,
779     nsMediaFeature::eNoRequirements,
780     { &nsGkAtoms::swipe_animation_enabled },
781     GetSystemMetric
782   },
783 
784   {
785     &nsGkAtoms::_moz_physical_home_button,
786     nsMediaFeature::eMinMaxNotAllowed,
787     nsMediaFeature::eBoolInteger,
788     nsMediaFeature::eNoRequirements,
789     { &nsGkAtoms::physical_home_button },
790     GetSystemMetric
791   },
792 
793   // Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
794   // Internal because it is really only useful in the user agent anyway
795   //  and therefore not worth standardizing.
796   {
797     &nsGkAtoms::_moz_is_glyph,
798     nsMediaFeature::eMinMaxNotAllowed,
799     nsMediaFeature::eBoolInteger,
800     nsMediaFeature::eNoRequirements,
801     { nullptr },
802     GetIsGlyph
803   },
804   // Null-mName terminator:
805   {
806     nullptr,
807     nsMediaFeature::eMinMaxAllowed,
808     nsMediaFeature::eInteger,
809     nsMediaFeature::eNoRequirements,
810     { nullptr },
811     nullptr
812   },
813 };
814