Bug 474356 - Add -moz-device-pixel-ratio media query. r=dbaron a=approval2.0
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1745,8 +1745,9 @@ GK_ATOM(_moz_images_in_menus, "-moz-imag
GK_ATOM(_moz_images_in_buttons, "-moz-images-in-buttons")
GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme")
GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme")
GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor")
GK_ATOM(_moz_windows_classic, "-moz-windows-classic")
GK_ATOM(_moz_touch_enabled, "-moz-touch-enabled")
GK_ATOM(_moz_maemo_classic, "-moz-maemo-classic")
GK_ATOM(_moz_menubar_drag, "-moz-menubar-drag")
+GK_ATOM(_moz_device_pixel_ratio, "-moz-device-pixel-ratio")
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -1786,16 +1786,19 @@ CSSParserImpl::ParseMediaQueryExpression
case nsMediaFeature::eBoolInteger:
rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nsnull);
// Enforce extra restrictions for eBoolInteger
if (rv &&
feature->mValueType == nsMediaFeature::eBoolInteger &&
expr->mValue.GetIntValue() > 1)
rv = PR_FALSE;
break;
+ case nsMediaFeature::eFloat:
+ rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nsnull);
+ break;
case nsMediaFeature::eIntRatio:
{
// Two integers separated by '/', with optional whitespace on
// either side of the '/'.
nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
if (!a) {
mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
SkipUntil(')');
--- a/layout/style/nsCSSStyleSheet.cpp
+++ b/layout/style/nsCSSStyleSheet.cpp
@@ -244,16 +244,25 @@ nsMediaExpression::Matches(nsPresContext
actual.GetIntValue() == 0 || actual.GetIntValue() == 1,
"bad actual bool integer value");
NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
required.GetIntValue() == 0 || required.GetIntValue() == 1,
"bad required bool integer value");
cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
}
break;
+ case nsMediaFeature::eFloat:
+ {
+ NS_ASSERTION(actual.GetUnit() == eCSSUnit_Number,
+ "bad actual value");
+ NS_ASSERTION(required.GetUnit() == eCSSUnit_Number,
+ "bad required value");
+ cmp = DoCompare(actual.GetFloatValue(), required.GetFloatValue());
+ }
+ break;
case nsMediaFeature::eIntRatio:
{
NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array &&
actual.GetArrayValue()->Count() == 2 &&
actual.GetArrayValue()->Item(0).GetUnit() ==
eCSSUnit_Integer &&
actual.GetArrayValue()->Item(1).GetUnit() ==
eCSSUnit_Integer,
@@ -417,16 +426,25 @@ nsMediaQuery::AppendToString(nsAString&
case nsMediaFeature::eInteger:
case nsMediaFeature::eBoolInteger:
NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer,
"bad unit");
// Use 'z-index' as a property that takes integer values
// written without anything extra.
expr.mValue.AppendToString(eCSSProperty_z_index, aString);
break;
+ case nsMediaFeature::eFloat:
+ {
+ NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Number,
+ "bad unit");
+ // Use 'line-height' as a property that takes float values
+ // written in the normal way.
+ expr.mValue.AppendToString(eCSSProperty_line_height, aString);
+ }
+ break;
case nsMediaFeature::eIntRatio:
{
NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array,
"bad unit");
nsCSSValue::Array *array = expr.mValue.GetArrayValue();
NS_ASSERTION(array->Count() == 2, "unexpected length");
NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
"bad unit");
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -180,17 +180,16 @@ GetAspectRatio(nsPresContext* aPresConte
static nsresult
GetDeviceAspectRatio(nsPresContext* aPresContext, const nsMediaFeature*,
nsCSSValue& aResult)
{
return MakeArray(GetDeviceSize(aPresContext), aResult);
}
-
static nsresult
GetColor(nsPresContext* aPresContext, const nsMediaFeature*,
nsCSSValue& aResult)
{
// FIXME: This implementation is bogus. nsThebesDeviceContext
// doesn't provide reliable information (should be fixed in bug
// 424386).
// FIXME: On a monochrome device, return 0!
@@ -257,16 +256,25 @@ GetGrid(nsPresContext* aPresContext, con
{
// Gecko doesn't support grid devices (e.g., ttys), so the 'grid'
// feature is always 0.
aResult.SetIntValue(0, eCSSUnit_Integer);
return NS_OK;
}
static nsresult
+GetDevicePixelRatio(nsPresContext* aPresContext, const nsMediaFeature*,
+ nsCSSValue& aResult)
+{
+ float ratio = aPresContext->CSSPixelsToDevPixels(1.0f);
+ aResult.SetFloatValue(ratio, eCSSUnit_Number);
+ return NS_OK;
+}
+
+static nsresult
GetSystemMetric(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
nsCSSValue& aResult)
{
NS_ABORT_IF_FALSE(aFeature->mValueType == nsMediaFeature::eBoolInteger,
"unexpected type");
nsIAtom *metricAtom = *aFeature->mData.mMetric;
PRBool hasMetric = nsCSSRuleProcessor::HasSystemMetric(metricAtom);
aResult.SetIntValue(hasMetric ? 1 : 0, eCSSUnit_Integer);
@@ -373,16 +381,23 @@ nsMediaFeatures::features[] = {
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eBoolInteger,
{ nsnull },
GetGrid
},
// Mozilla extensions
{
+ &nsGkAtoms::_moz_device_pixel_ratio,
+ nsMediaFeature::eMinMaxAllowed,
+ nsMediaFeature::eFloat,
+ { nsnull },
+ GetDevicePixelRatio
+ },
+ {
&nsGkAtoms::_moz_scrollbar_start_backward,
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eBoolInteger,
{ &nsGkAtoms::scrollbar_start_backward },
GetSystemMetric
},
{
&nsGkAtoms::_moz_scrollbar_start_forward,
--- a/layout/style/nsMediaFeatures.h
+++ b/layout/style/nsMediaFeatures.h
@@ -58,16 +58,17 @@ struct nsMediaFeature {
enum RangeType { eMinMaxAllowed, eMinMaxNotAllowed };
RangeType mRangeType;
enum ValueType {
// All value types allow eCSSUnit_Null to indicate that no value
// was given (in addition to the types listed below).
eLength, // values are such that nsCSSValue::IsLengthUnit() is true
eInteger, // values are eCSSUnit_Integer
+ eFloat, // values are eCSSUnit_Number
eBoolInteger,// values are eCSSUnit_Integer (0, -0, or 1 only)
eIntRatio, // values are eCSSUnit_Array of two eCSSUnit_Integer
eResolution, // values are in eCSSUnit_Inch (for dpi) or
// eCSSUnit_Centimeter (for dpcm)
eEnumerated // values are eCSSUnit_Enumerated (uses keyword table)
// Note that a number of pieces of code (both for parsing and
// for matching of valueless expressions) assume that all numeric
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -148,16 +148,17 @@ GARBAGE += css_properties.js
test_ident_escaping.html \
test_inherit_computation.html \
test_inherit_storage.html \
test_initial_computation.html \
test_initial_storage.html \
test_media_queries.html \
test_media_queries_dynamic.html \
test_media_queries_dynamic_xbl.html \
+ test_moz_device_pixel_ratio.html \
test_namespace_rule.html \
test_of_type_selectors.xhtml \
test_parse_rule.html \
test_parse_url.html \
test_pixel_lengths.html \
test_pointer-events.html \
test_property_database.html \
test_priority_preservation.html \
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -21,16 +21,23 @@ https://bugzilla.mozilla.org/show_bug.cg
/** Test for Bug 156716 **/
// Note that many other tests are in test_acid3_test46.html .
SimpleTest.waitForExplicitFinish();
var iframe;
+function getZoomRatio() {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+ return utils.screenPixelsPerCSSPixel;
+}
+
function run() {
iframe = document.getElementById("subdoc");
var subdoc = iframe.contentDocument;
var subwin = iframe.contentWindow;
var style = subdoc.getElementById("style");
var iframe_style = iframe.style;
var body_cs = subdoc.defaultView.getComputedStyle(subdoc.body, "");
@@ -324,16 +331,36 @@ function run() {
should_apply("all and (max-device-aspect-ratio: " + real_dar + ")");
should_apply("(max-device-aspect-ratio: " + high_dar_1 + ")");
should_apply("(max-device-aspect-ratio: " + high_dar_2 + ")");
should_not_apply("all and (max-device-aspect-ratio: " + low_dar_1 + ")");
should_apply("not all and (max-device-aspect-ratio: " + low_dar_2 + ")");
expression_should_not_be_parseable("max-device-aspect-ratio");
+ var real_dpr = 1.0 * getZoomRatio();
+ var high_dpr = 1.1 * getZoomRatio();
+ var low_dpr = 0.9 * getZoomRatio();
+ should_apply("all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
+ should_apply("all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
+ should_not_apply("not all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
+ should_not_apply("not all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
+ should_apply("all and (min--moz-device-pixel-ratio: " + low_dpr + ")");
+ should_apply("all and (max--moz-device-pixel-ratio: " + high_dpr + ")");
+ should_not_apply("all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
+ should_not_apply("all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
+ should_apply("not all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
+ should_apply("not all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
+ should_apply("(-moz-device-pixel-ratio: " + real_dpr + ")");
+ should_not_apply("(-moz-device-pixel-ratio: " + high_dpr + ")");
+ should_not_apply("(-moz-device-pixel-ratio: " + low_dpr + ")");
+ should_apply("(-moz-device-pixel-ratio)");
+ expression_should_not_be_parseable("min--moz-device-pixel-ratio");
+ expression_should_not_be_parseable("max--moz-device-pixel-ratio");
+
features = [ "max-aspect-ratio", "device-aspect-ratio" ];
for (i in features) {
feature = features[i];
expression_should_be_parseable(feature + ": 1/1");
expression_should_be_parseable(feature + ": 1 /1");
expression_should_be_parseable(feature + ": 1 / \t\n1");
expression_should_be_parseable(feature + ": 1/\r1");
expression_should_not_be_parseable(feature + ": 1");
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_moz_device_pixel_ratio.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=474356
+-->
+<head>
+ <title>Test for Bug 474356</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>.zoom-test { visibility: hidden; }</style>
+ <style><!-- placeholder for dynamic additions --></style>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=474356">Mozilla Bug 474356</a>
+<div id="content" style="display: none">
+
+</div>
+<script type="text/javascript">
+</script>
+<pre id="test">
+<div id="zoom1" class="zoom-test"></div>
+<div id="zoom2" class="zoom-test"></div>
+<div id="zoom3" class="zoom-test"></div>
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 474356 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ viewer = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShell)
+ .contentViewer
+ .QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);
+
+
+ function zoom(factor) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var previous = viewer.fullZoom;
+ viewer.fullZoom = factor;
+ return previous;
+ }
+
+ function isVisible(divName) {
+ return window.getComputedStyle(document.getElementById(divName), null).visibility == "visible";
+ }
+
+ function getZoomRatio() {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+ return utils.screenPixelsPerCSSPixel;
+ }
+
+ var screenPixelsPerCSSPixel = getZoomRatio();
+ var baseRatio = 1.0 * screenPixelsPerCSSPixel;
+ var doubleRatio = 2.0 * screenPixelsPerCSSPixel;
+ var halfRatio = 0.5 * screenPixelsPerCSSPixel;
+ var styleElem = document.getElementsByTagName("style")[1];
+ styleElem.textContent =
+ ["@media all and (-moz-device-pixel-ratio: " + baseRatio + ") {",
+ "#zoom1 { visibility: visible; }",
+ "}",
+ "@media all and (-moz-device-pixel-ratio: " + doubleRatio + ") {",
+ "#zoom2 { visibility: visible; }",
+ "}",
+ "@media all and (-moz-device-pixel-ratio: " + halfRatio + ") {",
+ "#zoom3 { visibility: visible; }",
+ "}"
+ ].join("\n");
+
+ ok(isVisible("zoom1"), "Base ratio rule should apply at base zoom level");
+ ok(!isVisible("zoom2") && !isVisible("zoom3"), "no other rules should apply");
+ var origZoom = zoom(2 * screenPixelsPerCSSPixel);
+ ok(isVisible("zoom2"), "Double ratio rule should apply at double zoom level");
+ ok(!isVisible("zoom1") && !isVisible("zoom3"), "no other rules should apply");
+ zoom(0.5 * screenPixelsPerCSSPixel);
+ ok(isVisible("zoom3"), "Half ratio rule should apply at half zoom level");
+ ok(!isVisible("zoom1") && !isVisible("zoom2"), "no other rules should apply");
+ zoom(origZoom);
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>