|
|
|
|
|
|
1 |
// # Bug 418986, part 2. |
| 2 |
|
| 3 |
/* jshint esnext:true */ |
| 4 |
/* jshint loopfunc:true */ |
| 5 |
/* global window, screen, ok, SpecialPowers, matchMedia */ |
| 6 |
|
| 7 |
SimpleTest.waitForExplicitFinish(); |
| 8 |
|
| 9 |
// Expected values. Format: [name, pref_off_value, pref_on_value] |
| 10 |
// If pref_*_value is an array with two values, then we will match |
| 11 |
// any value in between those two values. If a value is null, then |
| 12 |
// we skip the media query. |
| 13 |
let expected_values = [ |
| 14 |
["color", null, 8], |
| 15 |
["color-index", null, 0], |
| 16 |
["aspect-ratio", null, window.innerWidth + "/" + window.innerHeight], |
| 17 |
["device-aspect-ratio", screen.width + "/" + screen.height, |
| 18 |
window.innerWidth + "/" + window.innerHeight], |
| 19 |
["device-height", screen.height + "px", window.innerHeight + "px"], |
| 20 |
["device-width", screen.width + "px", window.innerWidth + "px"], |
| 21 |
["grid", null, 0], |
| 22 |
["height", window.innerHeight + "px", window.innerHeight + "px"], |
| 23 |
["monochrome", null, 0], |
| 24 |
// Square is defined as portrait: |
| 25 |
["orientation", null, |
| 26 |
window.innerWidth > window.innerHeight ? |
| 27 |
"landscape" : "portrait"], |
| 28 |
["resolution", null, "96dpi"], |
| 29 |
["resolution", [0.999 * window.devicePixelRatio + "dppx", |
| 30 |
1.001 * window.devicePixelRatio + "dppx"], "1dppx"], |
| 31 |
["width", window.innerWidth + "px", window.innerWidth + "px"], |
| 32 |
["-moz-device-pixel-ratio", window.devicePixelRatio, 1], |
| 33 |
["-moz-device-orientation", screen.width > screen.height ? |
| 34 |
"landscape" : "portrait", |
| 35 |
window.innerWidth > window.innerHeight ? |
| 36 |
"landscape" : "portrait"] |
| 37 |
]; |
| 38 |
|
| 39 |
// These media queries return value 0 or 1 when the pref is off. |
| 40 |
// When the pref is on, they should not match. |
| 41 |
let suppressed_toggles = [ |
| 42 |
"-moz-images-in-menus", |
| 43 |
"-moz-mac-graphite-theme", |
| 44 |
// Not available on most OSs. |
| 45 |
// "-moz-maemo-classic", |
| 46 |
"-moz-scrollbar-end-backward", |
| 47 |
"-moz-scrollbar-end-forward", |
| 48 |
"-moz-scrollbar-start-backward", |
| 49 |
"-moz-scrollbar-start-forward", |
| 50 |
"-moz-scrollbar-thumb-proportional", |
| 51 |
"-moz-touch-enabled", |
| 52 |
"-moz-windows-compositor", |
| 53 |
"-moz-windows-default-theme", |
| 54 |
"-moz-windows-glass", |
| 55 |
]; |
| 56 |
|
| 57 |
// Possible values for '-moz-os-version' |
| 58 |
let windows_versions = [ |
| 59 |
"windows-xp", |
| 60 |
"windows-vista", |
| 61 |
"windows-win7", |
| 62 |
"windows-win8"]; |
| 63 |
|
| 64 |
// Possible values for '-moz-windows-theme' |
| 65 |
let windows_themes = [ |
| 66 |
"aero", |
| 67 |
"luna-blue", |
| 68 |
"luna-olive", |
| 69 |
"luna-silver", |
| 70 |
"royale", |
| 71 |
"generic", |
| 72 |
"zune" |
| 73 |
]; |
| 74 |
|
| 75 |
// Read the current OS. |
| 76 |
let OS = SpecialPowers.Services.appinfo.OS; |
| 77 |
|
| 78 |
// If we are using Windows, add an extra toggle only |
| 79 |
// available on that OS. |
| 80 |
if (OS === "WINNT") { |
| 81 |
suppressed_toggles.push("-moz-windows-classic"); |
| 82 |
} |
| 83 |
|
| 84 |
// __keyValMatches(key, val)__. |
| 85 |
// Runs a media query and returns true if key matches to val. |
| 86 |
let keyValMatches = (key, val) => matchMedia("(" + key + ":" + val +")").matches; |
| 87 |
|
| 88 |
// __testMatch(key, val)__. |
| 89 |
// Attempts to run a media query match for the given key and value. |
| 90 |
// If value is an array of two elements [min max], then matches any |
| 91 |
// value in-between. |
| 92 |
let testMatch = function (key, val) { |
| 93 |
if (val === null) { |
| 94 |
return; |
| 95 |
} else if (Array.isArray(val)) { |
| 96 |
ok(keyValMatches("min-" + key, val[0]) && keyValMatches("max-" + key, val[1]), |
| 97 |
"Expected " + key + " between " + val[0] + " and " + val[1]); |
| 98 |
} else { |
| 99 |
ok(keyValMatches(key, val), "Expected " + key + ":" + val); |
| 100 |
} |
| 101 |
}; |
| 102 |
|
| 103 |
// __testToggles(resisting)__. |
| 104 |
// Test whether we are able to match the "toggle" media queries. |
| 105 |
let testToggles = function (resisting) { |
| 106 |
suppressed_toggles.forEach( |
| 107 |
function (key) { |
| 108 |
var exists = keyValMatches(key, 0) || keyValMatches(key, 1); |
| 109 |
if (resisting) { |
| 110 |
ok(!exists, key + " should not exist."); |
| 111 |
} else { |
| 112 |
ok(exists, key + " should exist."); |
| 113 |
} |
| 114 |
}); |
| 115 |
}; |
| 116 |
|
| 117 |
// __testWindowsSpecific__. |
| 118 |
// Runs a media query on the queryName with the given possible matching values. |
| 119 |
let testWindowsSpecific = function (resisting, queryName, possibleValues) { |
| 120 |
let found = false; |
| 121 |
possibleValues.forEach(function (val) { |
| 122 |
found = found || keyValMatches(queryName, val); |
| 123 |
}); |
| 124 |
if (resisting) { |
| 125 |
ok(!found, queryName + " should have no match"); |
| 126 |
} else { |
| 127 |
ok(found, queryName + " should match"); |
| 128 |
} |
| 129 |
}; |
| 130 |
|
| 131 |
// __generateHtmlLines(resisting)__. |
| 132 |
// Create a series of div elements that look like: |
| 133 |
// `<div class='spoof' id='resolution'>resolution</div>`, |
| 134 |
// where each line corresponds to a different media query. |
| 135 |
let generateHtmlLines = function (resisting) { |
| 136 |
let lines = ""; |
| 137 |
expected_values.forEach( |
| 138 |
function ([key, offVal, onVal]) { |
| 139 |
let val = resisting ? onVal : offVal; |
| 140 |
if (val) { |
| 141 |
lines += "<div class='spoof' id='" + key + "'>" + key + "</div>\n"; |
| 142 |
} |
| 143 |
}); |
| 144 |
suppressed_toggles.forEach( |
| 145 |
function (key) { |
| 146 |
lines += "<div class='suppress' id='" + key + "'>" + key + "</div>\n"; |
| 147 |
}); |
| 148 |
if (OS === "WINNT") { |
| 149 |
lines += "<div class='windows' id='-moz-os-version'>-moz-os-version</div>"; |
| 150 |
lines += "<div class='windows' id='-moz-windows-theme'>-moz-windows-theme</div>"; |
| 151 |
} |
| 152 |
return lines; |
| 153 |
}; |
| 154 |
|
| 155 |
// __cssLine__. |
| 156 |
// Creates a line of css that looks something like |
| 157 |
// `@media (resolution: 1ppx) { .spoof#resolution { background-color: green; } }`. |
| 158 |
let cssLine = function (query, clazz, id, color) { |
| 159 |
return "@media " + query + " { ." + clazz + "#" + id + |
| 160 |
" { background-color: " + color + "; } }\n"; |
| 161 |
}; |
| 162 |
|
| 163 |
// __mediaQueryCSSLine(key, val, color)__. |
| 164 |
// Creates a line containing a CSS media query and a CSS expression. |
| 165 |
let mediaQueryCSSLine = function (key, val, color) { |
| 166 |
if (val === null) { |
| 167 |
return ""; |
| 168 |
} |
| 169 |
let query; |
| 170 |
if (Array.isArray(val)) { |
| 171 |
query = "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")"; |
| 172 |
} else { |
| 173 |
query = "(" + key + ": " + val + ")"; |
| 174 |
} |
| 175 |
return cssLine(query, "spoof", key, color); |
| 176 |
}; |
| 177 |
|
| 178 |
// __suppressedMediaQueryCSSLine(key, color)__. |
| 179 |
// Creates a CSS line that matches the existence of a |
| 180 |
// media query that is supposed to be suppressed. |
| 181 |
let suppressedMediaQueryCSSLine = function (key, color, suppressed) { |
| 182 |
let query = "(" + key + ": 0), (" + key + ": 1)"; |
| 183 |
return cssLine(query, "suppress", key, color); |
| 184 |
}; |
| 185 |
|
| 186 |
// __generateCSSLines(resisting)__. |
| 187 |
// Creates a series of lines of CSS, each of which corresponds to |
| 188 |
// a different media query. If the query produces a match to the |
| 189 |
// expected value, then the element will be colored green. |
| 190 |
let generateCSSLines = function (resisting) { |
| 191 |
let lines = ".spoof { background-color: red;}\n"; |
| 192 |
expected_values.forEach( |
| 193 |
function ([key, offVal, onVal]) { |
| 194 |
lines += mediaQueryCSSLine(key, resisting ? onVal : offVal, "green"); |
| 195 |
}); |
| 196 |
lines += ".suppress { background-color: " + (resisting ? "green" : "red") + ";}\n"; |
| 197 |
suppressed_toggles.forEach( |
| 198 |
function (key) { |
| 199 |
lines += suppressedMediaQueryCSSLine(key, resisting ? "red" : "green"); |
| 200 |
}); |
| 201 |
if (OS === "WINNT") { |
| 202 |
lines += ".windows { background-color: " + (resisting ? "green" : "red") + ";}\n"; |
| 203 |
lines += windows_versions.map(val => "(-moz-os-version: " + val + ")").join(", ") + |
| 204 |
" { #-moz-os-version { background-color: " + (resisting ? "red" : "green") + ";} }\n"; |
| 205 |
lines += windows_themes.map(val => "(-moz-windows-theme: " + val + ")").join(",") + |
| 206 |
" { #-moz-windows-theme { background-color: " + (resisting ? "red" : "green") + ";} }\n"; |
| 207 |
} |
| 208 |
return lines; |
| 209 |
}; |
| 210 |
|
| 211 |
// __green__. |
| 212 |
// Returns the computed color style corresponding to green. |
| 213 |
let green = (function () { |
| 214 |
let temp = document.createElement("span"); |
| 215 |
temp.style.backgroundColor = "green"; |
| 216 |
return getComputedStyle(temp).backgroundColor; |
| 217 |
})(); |
| 218 |
|
| 219 |
// __testCSS(resisting)__. |
| 220 |
// Creates a series of divs and CSS using media queries to set their |
| 221 |
// background color. If all media queries match as expected, then |
| 222 |
// all divs should have a green background color. |
| 223 |
let testCSS = function (resisting) { |
| 224 |
document.getElementById("display").innerHTML = generateHtmlLines(resisting); |
| 225 |
document.getElementById("test-css").innerHTML = generateCSSLines(resisting); |
| 226 |
let cssTestDivs = document.querySelectorAll(".spoof,.suppress"); |
| 227 |
for (let div of cssTestDivs) { |
| 228 |
let color = window.getComputedStyle(div).backgroundColor; |
| 229 |
ok(color === green, "CSS for '" + div.id + "'"); |
| 230 |
} |
| 231 |
}; |
| 232 |
|
| 233 |
// __testOSXFontSmoothing(resisting)__. |
| 234 |
// When fingerprinting resistance is enabled, the `getComputedStyle` |
| 235 |
// should always return `undefined` for `MozOSXFontSmoothing`. |
| 236 |
let testOSXFontSmoothing = function (resisting) { |
| 237 |
let div = document.createElement("div"); |
| 238 |
div.style.MozOsxFontSmoothing = "unset"; |
| 239 |
let readBack = window.getComputedStyle(div).MozOsxFontSmoothing; |
| 240 |
let smoothingPref = SpecialPowers.getBoolPref("layout.css.osx-font-smoothing.enabled", false); |
| 241 |
is(readBack, resisting ? "" : (smoothingPref ? "auto" : ""), |
| 242 |
"-moz-osx-font-smoothing"); |
| 243 |
}; |
| 244 |
|
| 245 |
// An iterator yielding pref values for two consecutive tests. |
| 246 |
let prefVals = (for (prefVal of [false, true]) prefVal); |
| 247 |
|
| 248 |
// __test(isContent)__. |
| 249 |
// Run all tests. |
| 250 |
let test = function(isContent) { |
| 251 |
let {value: prefValue, done} = prefVals.next(); |
| 252 |
if (done) { |
| 253 |
SimpleTest.finish(); |
| 254 |
return; |
| 255 |
} |
| 256 |
SpecialPowers.pushPrefEnv({set: [["privacy.resistFingerprinting", prefValue]]}, |
| 257 |
function () { |
| 258 |
let resisting = prefValue && isContent; |
| 259 |
expected_values.forEach( |
| 260 |
function ([key, offVal, onVal]) { |
| 261 |
testMatch(key, resisting ? onVal : offVal); |
| 262 |
}); |
| 263 |
testToggles(resisting); |
| 264 |
if (OS === "WINNT") { |
| 265 |
testWindowsSpecific(resisting, "-moz-os-version", windows_versions); |
| 266 |
testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); |
| 267 |
} |
| 268 |
testCSS(resisting); |
| 269 |
if (OS === "Darwin") { |
| 270 |
testOSXFontSmoothing(resisting); |
| 271 |
} |
| 272 |
test(isContent); |
| 273 |
}); |
| 274 |
}; |