1 : /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is nsMediaFeatures.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2008
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /* the features that media queries can test */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsMediaFeatures.h"
43 : #include "nsGkAtoms.h"
44 : #include "nsCSSKeywords.h"
45 : #include "nsStyleConsts.h"
46 : #include "nsPresContext.h"
47 : #include "nsCSSValue.h"
48 : #include "nsIDocShell.h"
49 : #include "nsLayoutUtils.h"
50 : #include "mozilla/LookAndFeel.h"
51 : #include "nsCSSRuleProcessor.h"
52 :
53 : using namespace mozilla;
54 :
55 : static const PRInt32 kOrientationKeywords[] = {
56 : eCSSKeyword_portrait, NS_STYLE_ORIENTATION_PORTRAIT,
57 : eCSSKeyword_landscape, NS_STYLE_ORIENTATION_LANDSCAPE,
58 : eCSSKeyword_UNKNOWN, -1
59 : };
60 :
61 : static const PRInt32 kScanKeywords[] = {
62 : eCSSKeyword_progressive, NS_STYLE_SCAN_PROGRESSIVE,
63 : eCSSKeyword_interlace, NS_STYLE_SCAN_INTERLACE,
64 : eCSSKeyword_UNKNOWN, -1
65 : };
66 :
67 : #ifdef XP_WIN
68 : struct WindowsThemeName {
69 : LookAndFeel::WindowsTheme id;
70 : const wchar_t* name;
71 : };
72 :
73 : // Windows theme identities used in the -moz-windows-theme media query.
74 : const WindowsThemeName themeStrings[] = {
75 : { LookAndFeel::eWindowsTheme_Aero, L"aero" },
76 : { LookAndFeel::eWindowsTheme_LunaBlue, L"luna-blue" },
77 : { LookAndFeel::eWindowsTheme_LunaOlive, L"luna-olive" },
78 : { LookAndFeel::eWindowsTheme_LunaSilver, L"luna-silver" },
79 : { LookAndFeel::eWindowsTheme_Royale, L"royale" },
80 : { LookAndFeel::eWindowsTheme_Zune, L"zune" },
81 : { LookAndFeel::eWindowsTheme_Generic, L"generic" }
82 : };
83 : #endif
84 :
85 : // A helper for four features below
86 : static nsSize
87 0 : GetSize(nsPresContext* aPresContext)
88 : {
89 0 : nsSize size;
90 0 : if (aPresContext->IsRootPaginatedDocument())
91 : // We want the page size, including unprintable areas and margins.
92 0 : size = aPresContext->GetPageSize();
93 : else
94 0 : size = aPresContext->GetVisibleArea().Size();
95 : return size;
96 : }
97 :
98 : static nsresult
99 0 : GetWidth(nsPresContext* aPresContext, const nsMediaFeature*,
100 : nsCSSValue& aResult)
101 : {
102 0 : nsSize size = GetSize(aPresContext);
103 0 : float pixelWidth = aPresContext->AppUnitsToFloatCSSPixels(size.width);
104 0 : aResult.SetFloatValue(pixelWidth, eCSSUnit_Pixel);
105 0 : return NS_OK;
106 : }
107 :
108 : static nsresult
109 0 : GetHeight(nsPresContext* aPresContext, const nsMediaFeature*,
110 : nsCSSValue& aResult)
111 : {
112 0 : nsSize size = GetSize(aPresContext);
113 0 : float pixelHeight = aPresContext->AppUnitsToFloatCSSPixels(size.height);
114 0 : aResult.SetFloatValue(pixelHeight, eCSSUnit_Pixel);
115 0 : return NS_OK;
116 : }
117 :
118 : inline static nsDeviceContext*
119 0 : GetDeviceContextFor(nsPresContext* aPresContext)
120 : {
121 : // It would be nice to call
122 : // nsLayoutUtils::GetDeviceContextForScreenInfo here, except for two
123 : // things: (1) it can flush, and flushing is bad here, and (2) it
124 : // doesn't really get us consistency in multi-monitor situations
125 : // *anyway*.
126 0 : return aPresContext->DeviceContext();
127 : }
128 :
129 : // A helper for three features below.
130 : static nsSize
131 0 : GetDeviceSize(nsPresContext* aPresContext)
132 : {
133 0 : nsSize size;
134 0 : if (aPresContext->IsRootPaginatedDocument())
135 : // We want the page size, including unprintable areas and margins.
136 : // XXX The spec actually says we want the "page sheet size", but
137 : // how is that different?
138 0 : size = aPresContext->GetPageSize();
139 : else
140 : GetDeviceContextFor(aPresContext)->
141 0 : GetDeviceSurfaceDimensions(size.width, size.height);
142 : return size;
143 : }
144 :
145 : static nsresult
146 0 : GetDeviceWidth(nsPresContext* aPresContext, const nsMediaFeature*,
147 : nsCSSValue& aResult)
148 : {
149 0 : nsSize size = GetDeviceSize(aPresContext);
150 0 : float pixelWidth = aPresContext->AppUnitsToFloatCSSPixels(size.width);
151 0 : aResult.SetFloatValue(pixelWidth, eCSSUnit_Pixel);
152 0 : return NS_OK;
153 : }
154 :
155 : static nsresult
156 0 : GetDeviceHeight(nsPresContext* aPresContext, const nsMediaFeature*,
157 : nsCSSValue& aResult)
158 : {
159 0 : nsSize size = GetDeviceSize(aPresContext);
160 0 : float pixelHeight = aPresContext->AppUnitsToFloatCSSPixels(size.height);
161 0 : aResult.SetFloatValue(pixelHeight, eCSSUnit_Pixel);
162 0 : return NS_OK;
163 : }
164 :
165 : static nsresult
166 0 : GetOrientation(nsPresContext* aPresContext, const nsMediaFeature*,
167 : nsCSSValue& aResult)
168 : {
169 0 : nsSize size = GetSize(aPresContext);
170 : PRInt32 orientation;
171 0 : if (size.width > size.height) {
172 0 : orientation = NS_STYLE_ORIENTATION_LANDSCAPE;
173 : } else {
174 : // Per spec, square viewports should be 'portrait'
175 0 : orientation = NS_STYLE_ORIENTATION_PORTRAIT;
176 : }
177 :
178 0 : aResult.SetIntValue(orientation, eCSSUnit_Enumerated);
179 0 : return NS_OK;
180 : }
181 :
182 : static nsresult
183 0 : GetDeviceOrientation(nsPresContext* aPresContext, const nsMediaFeature*,
184 : nsCSSValue& aResult)
185 : {
186 0 : nsSize size = GetDeviceSize(aPresContext);
187 : PRInt32 orientation;
188 0 : if (size.width > size.height) {
189 0 : orientation = NS_STYLE_ORIENTATION_LANDSCAPE;
190 : } else {
191 : // Per spec, square viewports should be 'portrait'
192 0 : orientation = NS_STYLE_ORIENTATION_PORTRAIT;
193 : }
194 :
195 0 : aResult.SetIntValue(orientation, eCSSUnit_Enumerated);
196 0 : return NS_OK;
197 : }
198 :
199 : static nsresult
200 0 : GetIsResourceDocument(nsPresContext* aPresContext, const nsMediaFeature*,
201 : nsCSSValue& aResult)
202 : {
203 0 : nsIDocument* doc = aPresContext->Document();
204 0 : aResult.SetIntValue(doc && doc->IsResourceDoc() ? 1 : 0, eCSSUnit_Integer);
205 0 : return NS_OK;
206 : }
207 :
208 : // Helper for two features below
209 : static nsresult
210 0 : MakeArray(const nsSize& aSize, nsCSSValue& aResult)
211 : {
212 0 : nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
213 :
214 0 : a->Item(0).SetIntValue(aSize.width, eCSSUnit_Integer);
215 0 : a->Item(1).SetIntValue(aSize.height, eCSSUnit_Integer);
216 :
217 0 : aResult.SetArrayValue(a, eCSSUnit_Array);
218 0 : return NS_OK;
219 : }
220 :
221 : static nsresult
222 0 : GetAspectRatio(nsPresContext* aPresContext, const nsMediaFeature*,
223 : nsCSSValue& aResult)
224 : {
225 0 : return MakeArray(GetSize(aPresContext), aResult);
226 : }
227 :
228 : static nsresult
229 0 : GetDeviceAspectRatio(nsPresContext* aPresContext, const nsMediaFeature*,
230 : nsCSSValue& aResult)
231 : {
232 0 : return MakeArray(GetDeviceSize(aPresContext), aResult);
233 : }
234 :
235 : static nsresult
236 0 : GetColor(nsPresContext* aPresContext, const nsMediaFeature*,
237 : nsCSSValue& aResult)
238 : {
239 : // FIXME: This implementation is bogus. nsDeviceContext
240 : // doesn't provide reliable information (should be fixed in bug
241 : // 424386).
242 : // FIXME: On a monochrome device, return 0!
243 0 : nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
244 : PRUint32 depth;
245 0 : dx->GetDepth(depth);
246 : // The spec says to use bits *per color component*, so divide by 3,
247 : // and round down, since the spec says to use the smallest when the
248 : // color components differ.
249 0 : depth /= 3;
250 0 : aResult.SetIntValue(PRInt32(depth), eCSSUnit_Integer);
251 0 : return NS_OK;
252 : }
253 :
254 : static nsresult
255 0 : GetColorIndex(nsPresContext* aPresContext, const nsMediaFeature*,
256 : nsCSSValue& aResult)
257 : {
258 : // We should return zero if the device does not use a color lookup
259 : // table. Stuart says that our handling of displays with 8-bit
260 : // color is bad enough that we never change the lookup table to
261 : // match what we're trying to display, so perhaps we should always
262 : // return zero. Given that there isn't any better information
263 : // exposed, we don't have much other choice.
264 0 : aResult.SetIntValue(0, eCSSUnit_Integer);
265 0 : return NS_OK;
266 : }
267 :
268 : static nsresult
269 0 : GetMonochrome(nsPresContext* aPresContext, const nsMediaFeature*,
270 : nsCSSValue& aResult)
271 : {
272 : // For color devices we should return 0.
273 : // FIXME: On a monochrome device, return the actual color depth, not
274 : // 0!
275 0 : aResult.SetIntValue(0, eCSSUnit_Integer);
276 0 : return NS_OK;
277 : }
278 :
279 : static nsresult
280 0 : GetResolution(nsPresContext* aPresContext, const nsMediaFeature*,
281 : nsCSSValue& aResult)
282 : {
283 : // Resolution values are in device pixels, not CSS pixels.
284 0 : nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
285 0 : float dpi = float(dx->AppUnitsPerPhysicalInch()) / float(dx->AppUnitsPerDevPixel());
286 0 : aResult.SetFloatValue(dpi, eCSSUnit_Inch);
287 0 : return NS_OK;
288 : }
289 :
290 : static nsresult
291 0 : GetScan(nsPresContext* aPresContext, const nsMediaFeature*,
292 : nsCSSValue& aResult)
293 : {
294 : // Since Gecko doesn't support the 'tv' media type, the 'scan'
295 : // feature is never present.
296 0 : aResult.Reset();
297 0 : return NS_OK;
298 : }
299 :
300 : static nsresult
301 0 : GetGrid(nsPresContext* aPresContext, const nsMediaFeature*,
302 : nsCSSValue& aResult)
303 : {
304 : // Gecko doesn't support grid devices (e.g., ttys), so the 'grid'
305 : // feature is always 0.
306 0 : aResult.SetIntValue(0, eCSSUnit_Integer);
307 0 : return NS_OK;
308 : }
309 :
310 : static nsresult
311 0 : GetDevicePixelRatio(nsPresContext* aPresContext, const nsMediaFeature*,
312 : nsCSSValue& aResult)
313 : {
314 0 : float ratio = aPresContext->CSSPixelsToDevPixels(1.0f);
315 0 : aResult.SetFloatValue(ratio, eCSSUnit_Number);
316 0 : return NS_OK;
317 : }
318 :
319 : static nsresult
320 0 : GetSystemMetric(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
321 : nsCSSValue& aResult)
322 : {
323 0 : NS_ABORT_IF_FALSE(aFeature->mValueType == nsMediaFeature::eBoolInteger,
324 : "unexpected type");
325 0 : nsIAtom *metricAtom = *aFeature->mData.mMetric;
326 0 : bool hasMetric = nsCSSRuleProcessor::HasSystemMetric(metricAtom);
327 0 : aResult.SetIntValue(hasMetric ? 1 : 0, eCSSUnit_Integer);
328 0 : return NS_OK;
329 : }
330 :
331 : static nsresult
332 0 : GetWindowsTheme(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
333 : nsCSSValue& aResult)
334 : {
335 0 : aResult.Reset();
336 : #ifdef XP_WIN
337 : PRUint8 windowsThemeId =
338 : nsCSSRuleProcessor::GetWindowsThemeIdentifier();
339 :
340 : // Classic mode should fail to match.
341 : if (windowsThemeId == LookAndFeel::eWindowsTheme_Classic)
342 : return NS_OK;
343 :
344 : // Look up the appropriate theme string
345 : for (size_t i = 0; i < ArrayLength(themeStrings); ++i) {
346 : if (windowsThemeId == themeStrings[i].id) {
347 : aResult.SetStringValue(nsDependentString(themeStrings[i].name),
348 : eCSSUnit_Ident);
349 : break;
350 : }
351 : }
352 : #endif
353 0 : return NS_OK;
354 : }
355 :
356 : /*
357 : * Adding new media features requires (1) adding the new feature to this
358 : * array, with appropriate entries (and potentially any new code needed
359 : * to support new types in these entries and (2) ensuring that either
360 : * nsPresContext::MediaFeatureValuesChanged or
361 : * nsPresContext::PostMediaFeatureValuesChangedEvent is called when the
362 : * value that would be returned by the entry's mGetter changes.
363 : */
364 :
365 : /* static */ const nsMediaFeature
366 : nsMediaFeatures::features[] = {
367 : {
368 : &nsGkAtoms::width,
369 : nsMediaFeature::eMinMaxAllowed,
370 : nsMediaFeature::eLength,
371 : { nsnull },
372 : GetWidth
373 : },
374 : {
375 : &nsGkAtoms::height,
376 : nsMediaFeature::eMinMaxAllowed,
377 : nsMediaFeature::eLength,
378 : { nsnull },
379 : GetHeight
380 : },
381 : {
382 : &nsGkAtoms::deviceWidth,
383 : nsMediaFeature::eMinMaxAllowed,
384 : nsMediaFeature::eLength,
385 : { nsnull },
386 : GetDeviceWidth
387 : },
388 : {
389 : &nsGkAtoms::deviceHeight,
390 : nsMediaFeature::eMinMaxAllowed,
391 : nsMediaFeature::eLength,
392 : { nsnull },
393 : GetDeviceHeight
394 : },
395 : {
396 : &nsGkAtoms::orientation,
397 : nsMediaFeature::eMinMaxNotAllowed,
398 : nsMediaFeature::eEnumerated,
399 : { kOrientationKeywords },
400 : GetOrientation
401 : },
402 : {
403 : &nsGkAtoms::aspectRatio,
404 : nsMediaFeature::eMinMaxAllowed,
405 : nsMediaFeature::eIntRatio,
406 : { nsnull },
407 : GetAspectRatio
408 : },
409 : {
410 : &nsGkAtoms::deviceAspectRatio,
411 : nsMediaFeature::eMinMaxAllowed,
412 : nsMediaFeature::eIntRatio,
413 : { nsnull },
414 : GetDeviceAspectRatio
415 : },
416 : {
417 : &nsGkAtoms::color,
418 : nsMediaFeature::eMinMaxAllowed,
419 : nsMediaFeature::eInteger,
420 : { nsnull },
421 : GetColor
422 : },
423 : {
424 : &nsGkAtoms::colorIndex,
425 : nsMediaFeature::eMinMaxAllowed,
426 : nsMediaFeature::eInteger,
427 : { nsnull },
428 : GetColorIndex
429 : },
430 : {
431 : &nsGkAtoms::monochrome,
432 : nsMediaFeature::eMinMaxAllowed,
433 : nsMediaFeature::eInteger,
434 : { nsnull },
435 : GetMonochrome
436 : },
437 : {
438 : &nsGkAtoms::resolution,
439 : nsMediaFeature::eMinMaxAllowed,
440 : nsMediaFeature::eResolution,
441 : { nsnull },
442 : GetResolution
443 : },
444 : {
445 : &nsGkAtoms::scan,
446 : nsMediaFeature::eMinMaxNotAllowed,
447 : nsMediaFeature::eEnumerated,
448 : { kScanKeywords },
449 : GetScan
450 : },
451 : {
452 : &nsGkAtoms::grid,
453 : nsMediaFeature::eMinMaxNotAllowed,
454 : nsMediaFeature::eBoolInteger,
455 : { nsnull },
456 : GetGrid
457 : },
458 :
459 : // Mozilla extensions
460 : {
461 : &nsGkAtoms::_moz_device_pixel_ratio,
462 : nsMediaFeature::eMinMaxAllowed,
463 : nsMediaFeature::eFloat,
464 : { nsnull },
465 : GetDevicePixelRatio
466 : },
467 : {
468 : &nsGkAtoms::_moz_device_orientation,
469 : nsMediaFeature::eMinMaxNotAllowed,
470 : nsMediaFeature::eEnumerated,
471 : { kOrientationKeywords },
472 : GetDeviceOrientation
473 : },
474 : {
475 : &nsGkAtoms::_moz_is_resource_document,
476 : nsMediaFeature::eMinMaxNotAllowed,
477 : nsMediaFeature::eBoolInteger,
478 : { nsnull },
479 : GetIsResourceDocument
480 : },
481 : {
482 : &nsGkAtoms::_moz_scrollbar_start_backward,
483 : nsMediaFeature::eMinMaxNotAllowed,
484 : nsMediaFeature::eBoolInteger,
485 : { &nsGkAtoms::scrollbar_start_backward },
486 : GetSystemMetric
487 : },
488 : {
489 : &nsGkAtoms::_moz_scrollbar_start_forward,
490 : nsMediaFeature::eMinMaxNotAllowed,
491 : nsMediaFeature::eBoolInteger,
492 : { &nsGkAtoms::scrollbar_start_forward },
493 : GetSystemMetric
494 : },
495 : {
496 : &nsGkAtoms::_moz_scrollbar_end_backward,
497 : nsMediaFeature::eMinMaxNotAllowed,
498 : nsMediaFeature::eBoolInteger,
499 : { &nsGkAtoms::scrollbar_end_backward },
500 : GetSystemMetric
501 : },
502 : {
503 : &nsGkAtoms::_moz_scrollbar_end_forward,
504 : nsMediaFeature::eMinMaxNotAllowed,
505 : nsMediaFeature::eBoolInteger,
506 : { &nsGkAtoms::scrollbar_end_forward },
507 : GetSystemMetric
508 : },
509 : {
510 : &nsGkAtoms::_moz_scrollbar_thumb_proportional,
511 : nsMediaFeature::eMinMaxNotAllowed,
512 : nsMediaFeature::eBoolInteger,
513 : { &nsGkAtoms::scrollbar_thumb_proportional },
514 : GetSystemMetric
515 : },
516 : {
517 : &nsGkAtoms::_moz_images_in_menus,
518 : nsMediaFeature::eMinMaxNotAllowed,
519 : nsMediaFeature::eBoolInteger,
520 : { &nsGkAtoms::images_in_menus },
521 : GetSystemMetric
522 : },
523 : {
524 : &nsGkAtoms::_moz_images_in_buttons,
525 : nsMediaFeature::eMinMaxNotAllowed,
526 : nsMediaFeature::eBoolInteger,
527 : { &nsGkAtoms::images_in_buttons },
528 : GetSystemMetric
529 : },
530 : {
531 : &nsGkAtoms::_moz_windows_default_theme,
532 : nsMediaFeature::eMinMaxNotAllowed,
533 : nsMediaFeature::eBoolInteger,
534 : { &nsGkAtoms::windows_default_theme },
535 : GetSystemMetric
536 : },
537 : {
538 : &nsGkAtoms::_moz_mac_graphite_theme,
539 : nsMediaFeature::eMinMaxNotAllowed,
540 : nsMediaFeature::eBoolInteger,
541 : { &nsGkAtoms::mac_graphite_theme },
542 : GetSystemMetric
543 : },
544 : {
545 : &nsGkAtoms::_moz_mac_lion_theme,
546 : nsMediaFeature::eMinMaxNotAllowed,
547 : nsMediaFeature::eBoolInteger,
548 : { &nsGkAtoms::mac_lion_theme },
549 : GetSystemMetric
550 : },
551 : {
552 : &nsGkAtoms::_moz_windows_compositor,
553 : nsMediaFeature::eMinMaxNotAllowed,
554 : nsMediaFeature::eBoolInteger,
555 : { &nsGkAtoms::windows_compositor },
556 : GetSystemMetric
557 : },
558 : {
559 : &nsGkAtoms::_moz_windows_classic,
560 : nsMediaFeature::eMinMaxNotAllowed,
561 : nsMediaFeature::eBoolInteger,
562 : { &nsGkAtoms::windows_classic },
563 : GetSystemMetric
564 : },
565 : {
566 : &nsGkAtoms::_moz_touch_enabled,
567 : nsMediaFeature::eMinMaxNotAllowed,
568 : nsMediaFeature::eBoolInteger,
569 : { &nsGkAtoms::touch_enabled },
570 : GetSystemMetric
571 : },
572 : {
573 : &nsGkAtoms::_moz_maemo_classic,
574 : nsMediaFeature::eMinMaxNotAllowed,
575 : nsMediaFeature::eBoolInteger,
576 : { &nsGkAtoms::maemo_classic },
577 : GetSystemMetric
578 : },
579 : {
580 : &nsGkAtoms::_moz_menubar_drag,
581 : nsMediaFeature::eMinMaxNotAllowed,
582 : nsMediaFeature::eBoolInteger,
583 : { &nsGkAtoms::menubar_drag },
584 : GetSystemMetric
585 : },
586 : {
587 : &nsGkAtoms::_moz_windows_theme,
588 : nsMediaFeature::eMinMaxNotAllowed,
589 : nsMediaFeature::eIdent,
590 : { nsnull },
591 : GetWindowsTheme
592 : },
593 : // Null-mName terminator:
594 : {
595 : nsnull,
596 : nsMediaFeature::eMinMaxAllowed,
597 : nsMediaFeature::eInteger,
598 : { nsnull },
599 : nsnull
600 : },
601 : };
|