summaryrefslogtreecommitdiffstats
path: root/layout/style/test/chrome
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /layout/style/test/chrome
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/style/test/chrome')
-rw-r--r--layout/style/test/chrome/bug418986-2.js329
-rw-r--r--layout/style/test/chrome/bug535806-css.css1
-rw-r--r--layout/style/test/chrome/bug535806-html.html8
-rw-r--r--layout/style/test/chrome/bug535806-xul.xhtml8
-rw-r--r--layout/style/test/chrome/chrome-only-media-queries.js43
-rw-r--r--layout/style/test/chrome/chrome.ini30
-rw-r--r--layout/style/test/chrome/display_mode.html122
-rw-r--r--layout/style/test/chrome/display_mode_reflow.html84
-rw-r--r--layout/style/test/chrome/display_mode_reflow_iframe.html23
-rw-r--r--layout/style/test/chrome/hover_empty.html4
-rw-r--r--layout/style/test/chrome/hover_helper.html270
-rw-r--r--layout/style/test/chrome/import_useless1.css3
-rw-r--r--layout/style/test/chrome/import_useless2.css3
-rw-r--r--layout/style/test/chrome/match.pngbin0 -> 1210 bytes
-rw-r--r--layout/style/test/chrome/mismatch.pngbin0 -> 1573 bytes
-rw-r--r--layout/style/test/chrome/moz_document_helper.html2
-rw-r--r--layout/style/test/chrome/test_bug1157097.html27
-rw-r--r--layout/style/test/chrome/test_bug1346623.html59
-rw-r--r--layout/style/test/chrome/test_bug1371453.html33
-rw-r--r--layout/style/test/chrome/test_bug418986-2.xhtml29
-rw-r--r--layout/style/test/chrome/test_bug535806.xhtml43
-rw-r--r--layout/style/test/chrome/test_chrome_only_media_queries.html84
-rw-r--r--layout/style/test/chrome/test_constructable_stylesheets_chrome_only_rules.html11
-rw-r--r--layout/style/test/chrome/test_display_mode.html39
-rw-r--r--layout/style/test/chrome/test_display_mode_reflow.html41
-rw-r--r--layout/style/test/chrome/test_hover.html29
-rw-r--r--layout/style/test/chrome/test_moz_document_rules.html97
-rw-r--r--layout/style/test/chrome/test_stylesheet_clone_import_rule.html86
28 files changed, 1508 insertions, 0 deletions
diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js
new file mode 100644
index 0000000000..c0b8264d6e
--- /dev/null
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -0,0 +1,329 @@
+// # Bug 418986, part 2.
+
+const is_chrome_window = window.location.protocol === "chrome:";
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+// Expected values. Format: [name, pref_off_value, pref_on_value]
+// If pref_*_value is an array with two values, then we will match
+// any value in between those two values. If a value is null, then
+// we skip the media query.
+var expected_values = [
+ ["color", null, 8],
+ ["color-index", null, 0],
+ ["aspect-ratio", null, window.innerWidth + "/" + window.innerHeight],
+ [
+ "device-aspect-ratio",
+ screen.width + "/" + screen.height,
+ window.innerWidth + "/" + window.innerHeight,
+ ],
+ ["device-height", screen.height + "px", window.innerHeight + "px"],
+ ["device-width", screen.width + "px", window.innerWidth + "px"],
+ ["grid", null, 0],
+ ["height", window.innerHeight + "px", window.innerHeight + "px"],
+ ["monochrome", null, 0],
+ // Square is defined as portrait:
+ [
+ "orientation",
+ null,
+ window.innerWidth > window.innerHeight ? "landscape" : "portrait",
+ ],
+ ["resolution", null, "96dpi"],
+ [
+ "resolution",
+ [
+ 0.999 * window.devicePixelRatio + "dppx",
+ 1.001 * window.devicePixelRatio + "dppx",
+ ],
+ "1dppx",
+ ],
+ ["width", window.innerWidth + "px", window.innerWidth + "px"],
+ ["-moz-device-pixel-ratio", window.devicePixelRatio, 1],
+ [
+ "-moz-device-orientation",
+ screen.width > screen.height ? "landscape" : "portrait",
+ window.innerWidth > window.innerHeight ? "landscape" : "portrait",
+ ],
+];
+
+// These media queries return value 0 or 1 when the pref is off.
+// When the pref is on, they should not match.
+var suppressed_toggles = [
+ "-moz-mac-graphite-theme",
+ // Not available on most OSs.
+ // "-moz-maemo-classic",
+ "-moz-scrollbar-end-backward",
+ "-moz-scrollbar-end-forward",
+ "-moz-scrollbar-start-backward",
+ "-moz-scrollbar-start-forward",
+ "-moz-windows-compositor",
+ "-moz-windows-default-theme",
+ "-moz-windows-glass",
+ "-moz-gtk-csd-available",
+ "-moz-gtk-csd-minimize-button",
+ "-moz-gtk-csd-maximize-button",
+ "-moz-gtk-csd-close-button",
+ "-moz-gtk-csd-reversed-placement",
+];
+
+var toggles_enabled_in_content = [];
+
+// Read the current OS.
+var OS = SpecialPowers.Services.appinfo.OS;
+
+// If we are using Windows, add an extra toggle only
+// available on that OS.
+if (OS === "WINNT") {
+ suppressed_toggles.push("-moz-windows-classic");
+}
+
+// __keyValMatches(key, val)__.
+// Runs a media query and returns true if key matches to val.
+var keyValMatches = (key, val) =>
+ matchMedia("(" + key + ":" + val + ")").matches;
+
+// __testMatch(key, val)__.
+// Attempts to run a media query match for the given key and value.
+// If value is an array of two elements [min max], then matches any
+// value in-between.
+var testMatch = function(key, val) {
+ if (val === null) {
+ return;
+ } else if (Array.isArray(val)) {
+ ok(
+ keyValMatches("min-" + key, val[0]) &&
+ keyValMatches("max-" + key, val[1]),
+ "Expected " + key + " between " + val[0] + " and " + val[1]
+ );
+ } else {
+ ok(keyValMatches(key, val), "Expected " + key + ":" + val);
+ }
+};
+
+// __testToggles(resisting)__.
+// Test whether we are able to match the "toggle" media queries.
+var testToggles = function(resisting) {
+ suppressed_toggles.forEach(function(key) {
+ var exists = keyValMatches(key, 0) || keyValMatches(key, 1);
+ if (!toggles_enabled_in_content.includes(key) && !is_chrome_window) {
+ ok(!exists, key + " should not exist.");
+ } else {
+ ok(exists, key + " should exist.");
+ if (resisting) {
+ ok(
+ keyValMatches(key, 0) && !keyValMatches(key, 1),
+ "Should always match as false"
+ );
+ }
+ }
+ });
+};
+
+// __generateHtmlLines(resisting)__.
+// Create a series of div elements that look like:
+// `<div class='spoof' id='resolution'>resolution</div>`,
+// where each line corresponds to a different media query.
+var generateHtmlLines = function(resisting) {
+ let fragment = document.createDocumentFragment();
+ expected_values.forEach(function([key, offVal, onVal]) {
+ let val = resisting ? onVal : offVal;
+ if (val) {
+ let div = document.createElementNS(HTML_NS, "div");
+ div.setAttribute("class", "spoof");
+ div.setAttribute("id", key);
+ div.textContent = key;
+ fragment.appendChild(div);
+ }
+ });
+ suppressed_toggles.forEach(function(key) {
+ let div = document.createElementNS(HTML_NS, "div");
+ div.setAttribute("class", "suppress");
+ div.setAttribute("id", key);
+ div.textContent = key;
+ fragment.appendChild(div);
+ });
+ return fragment;
+};
+
+// __cssLine__.
+// Creates a line of css that looks something like
+// `@media (resolution: 1ppx) { .spoof#resolution { background-color: green; } }`.
+var cssLine = function(query, clazz, id, color) {
+ return (
+ "@media " +
+ query +
+ " { ." +
+ clazz +
+ "#" +
+ id +
+ " { background-color: " +
+ color +
+ "; } }\n"
+ );
+};
+
+// __constructQuery(key, val)__.
+// Creates a CSS media query from key and val. If key is an array of
+// two elements, constructs a range query (using min- and max-).
+var constructQuery = function(key, val) {
+ return Array.isArray(val)
+ ? "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")"
+ : "(" + key + ": " + val + ")";
+};
+
+// __mediaQueryCSSLine(key, val, color)__.
+// Creates a line containing a CSS media query and a CSS expression.
+var mediaQueryCSSLine = function(key, val, color) {
+ if (val === null) {
+ return "";
+ }
+ return cssLine(constructQuery(key, val), "spoof", key, color);
+};
+
+// __suppressedMediaQueryCSSLine(key, color)__.
+// Creates a CSS line that matches the existence of a
+// media query that is supposed to be suppressed.
+var suppressedMediaQueryCSSLine = function(key, color, suppressed) {
+ let query = "(" + key + ": 0), (" + key + ": 1)";
+ return cssLine(query, "suppress", key, color);
+};
+
+// __generateCSSLines(resisting)__.
+// Creates a series of lines of CSS, each of which corresponds to
+// a different media query. If the query produces a match to the
+// expected value, then the element will be colored green.
+var generateCSSLines = function(resisting) {
+ let lines = ".spoof { background-color: red;}\n";
+ expected_values.forEach(function([key, offVal, onVal]) {
+ lines += mediaQueryCSSLine(key, resisting ? onVal : offVal, "green");
+ });
+ lines +=
+ ".suppress { background-color: " + (resisting ? "green" : "red") + ";}\n";
+ suppressed_toggles.forEach(function(key) {
+ if (
+ !toggles_enabled_in_content.includes(key) &&
+ !resisting &&
+ !is_chrome_window
+ ) {
+ lines += "#" + key + " { background-color: green; }\n";
+ } else {
+ lines += suppressedMediaQueryCSSLine(key, "green");
+ }
+ });
+ return lines;
+};
+
+// __green__.
+// Returns the computed color style corresponding to green.
+var green = "rgb(0, 128, 0)";
+
+// __testCSS(resisting)__.
+// Creates a series of divs and CSS using media queries to set their
+// background color. If all media queries match as expected, then
+// all divs should have a green background color.
+var testCSS = function(resisting) {
+ document.getElementById("display").appendChild(generateHtmlLines(resisting));
+ document.getElementById("test-css").textContent = generateCSSLines(resisting);
+ let cssTestDivs = document.querySelectorAll(".spoof,.suppress");
+ for (let div of cssTestDivs) {
+ let color = window.getComputedStyle(div).backgroundColor;
+ ok(color === green, "CSS for '" + div.id + "'");
+ }
+};
+
+// __testOSXFontSmoothing(resisting)__.
+// When fingerprinting resistance is enabled, the `getComputedStyle`
+// should always return `undefined` for `MozOSXFontSmoothing`.
+var testOSXFontSmoothing = function(resisting) {
+ let div = document.createElementNS(HTML_NS, "div");
+ div.style.MozOsxFontSmoothing = "unset";
+ document.documentElement.appendChild(div);
+ let readBack = window.getComputedStyle(div).MozOsxFontSmoothing;
+ div.remove();
+ let smoothingPref = SpecialPowers.getBoolPref(
+ "layout.css.osx-font-smoothing.enabled",
+ false
+ );
+ is(
+ readBack,
+ resisting ? "" : smoothingPref ? "auto" : "",
+ "-moz-osx-font-smoothing"
+ );
+};
+
+// __sleep(timeoutMs)__.
+// Returns a promise that resolves after the given timeout.
+var sleep = function(timeoutMs) {
+ return new Promise(function(resolve, reject) {
+ window.setTimeout(resolve);
+ });
+};
+
+// __testMediaQueriesInPictureElements(resisting)__.
+// Test to see if media queries are properly spoofed in picture elements
+// when we are resisting fingerprinting.
+var testMediaQueriesInPictureElements = async function(resisting) {
+ const MATCH = "/tests/layout/style/test/chrome/match.png";
+ let container = document.getElementById("pictures");
+ let testImages = [];
+ for (let [key, offVal, onVal] of expected_values) {
+ let expected = resisting ? onVal : offVal;
+ if (expected) {
+ let picture = document.createElementNS(HTML_NS, "picture");
+ let query = constructQuery(key, expected);
+ ok(matchMedia(query).matches, `${query} should match`);
+
+ let source = document.createElementNS(HTML_NS, "source");
+ source.setAttribute("srcset", MATCH);
+ source.setAttribute("media", query);
+
+ let image = document.createElementNS(HTML_NS, "img");
+ image.setAttribute("title", key + ":" + expected);
+ image.setAttribute("class", "testImage");
+ image.setAttribute("src", "/tests/layout/style/test/chrome/mismatch.png");
+ image.setAttribute("alt", key);
+
+ testImages.push(image);
+
+ picture.appendChild(source);
+ picture.appendChild(image);
+ container.appendChild(picture);
+ }
+ }
+ const matchURI = new URL(MATCH, document.baseURI).href;
+ await sleep(0);
+ for (let testImage of testImages) {
+ is(
+ testImage.currentSrc,
+ matchURI,
+ "Media query '" + testImage.title + "' in picture should match."
+ );
+ }
+};
+
+// __pushPref(key, value)__.
+// Set a pref value asynchronously, returning a promise that resolves
+// when it succeeds.
+var pushPref = function(key, value) {
+ return new Promise(function(resolve, reject) {
+ SpecialPowers.pushPrefEnv({ set: [[key, value]] }, resolve);
+ });
+};
+
+// __test(isContent)__.
+// Run all tests.
+var test = async function(isContent) {
+ for (prefValue of [false, true]) {
+ await pushPref("privacy.resistFingerprinting", prefValue);
+ let resisting = prefValue && isContent;
+ expected_values.forEach(function([key, offVal, onVal]) {
+ testMatch(key, resisting ? onVal : offVal);
+ });
+ testToggles(resisting);
+ testCSS(resisting);
+ if (OS === "Darwin") {
+ testOSXFontSmoothing(resisting);
+ }
+ await testMediaQueriesInPictureElements(resisting);
+ }
+};
diff --git a/layout/style/test/chrome/bug535806-css.css b/layout/style/test/chrome/bug535806-css.css
new file mode 100644
index 0000000000..bda339f776
--- /dev/null
+++ b/layout/style/test/chrome/bug535806-css.css
@@ -0,0 +1 @@
+fooBar[fooBar] { color: green; }
diff --git a/layout/style/test/chrome/bug535806-html.html b/layout/style/test/chrome/bug535806-html.html
new file mode 100644
index 0000000000..e4395da3f3
--- /dev/null
+++ b/layout/style/test/chrome/bug535806-html.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="bug535806-css.css">
+ </head>
+ <body onload="window.parent.wrappedJSObject.htmlLoaded()">
+ </body>
+</html>
diff --git a/layout/style/test/chrome/bug535806-xul.xhtml b/layout/style/test/chrome/bug535806-xul.xhtml
new file mode 100644
index 0000000000..3d9a82b91e
--- /dev/null
+++ b/layout/style/test/chrome/bug535806-xul.xhtml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="data:text/css,fooBar{color:red;}"?>
+<?xml-stylesheet type="text/css" href="bug535806-css.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="window.parent.wrappedJSObject.xulLoaded()">
+ <fooBar fooBar="" id="s"/>
+</window>
diff --git a/layout/style/test/chrome/chrome-only-media-queries.js b/layout/style/test/chrome/chrome-only-media-queries.js
new file mode 100644
index 0000000000..35ae84e796
--- /dev/null
+++ b/layout/style/test/chrome/chrome-only-media-queries.js
@@ -0,0 +1,43 @@
+const CHROME_ONLY_TOGGLES = [
+ "-moz-is-glyph",
+ "-moz-print-preview",
+ "-moz-non-native-content-theme",
+ "-moz-scrollbar-start-backward",
+ "-moz-scrollbar-start-forward",
+ "-moz-scrollbar-end-backward",
+ "-moz-scrollbar-end-forward",
+ "-moz-overlay-scrollbars",
+ "-moz-windows-classic",
+ "-moz-windows-compositor",
+ "-moz-windows-default-theme",
+ "-moz-mac-graphite-theme",
+ "-moz-mac-big-sur-theme",
+ "-moz-menubar-drag",
+ "-moz-windows-accent-color-in-titlebar",
+ "-moz-windows-compositor",
+ "-moz-windows-classic",
+ "-moz-windows-glass",
+ "-moz-windows-non-native-menus",
+ "-moz-swipe-animation-enabled",
+ "-moz-gtk-csd-available",
+ "-moz-gtk-csd-minimize-button",
+ "-moz-gtk-csd-maximize-button",
+ "-moz-gtk-csd-close-button",
+ "-moz-gtk-csd-reversed-placement",
+ "-moz-box-flexbox-emulation",
+ "-moz-panel-animations",
+];
+
+// Non-parseable queries can be tested directly in
+// `test_chrome_only_media_queries.html`.
+const CHROME_ONLY_QUERIES = [
+ "(-moz-platform: linux)",
+ "(-moz-platform: windows)",
+ "(-moz-platform: macos)",
+ "(-moz-platform: android)",
+ "(-moz-platform: windows-win7)",
+ "(-moz-platform: windows-win8)",
+ "(-moz-platform: windows-win10)",
+ "(-moz-content-prefers-color-scheme: dark)",
+ "(-moz-content-prefers-color-scheme: light)",
+];
diff --git a/layout/style/test/chrome/chrome.ini b/layout/style/test/chrome/chrome.ini
new file mode 100644
index 0000000000..990faeac5d
--- /dev/null
+++ b/layout/style/test/chrome/chrome.ini
@@ -0,0 +1,30 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ bug418986-2.js
+ bug535806-css.css
+ bug535806-html.html
+ bug535806-xul.xhtml
+ hover_helper.html
+ match.png
+ mismatch.png
+
+[test_bug1346623.html]
+[test_bug1371453.html]
+[test_bug418986-2.xhtml]
+[test_bug1157097.html]
+[test_bug535806.xhtml]
+[test_chrome_only_media_queries.html]
+support-files = chrome-only-media-queries.js
+[test_display_mode.html]
+support-files = display_mode.html
+tags = fullscreen
+[test_display_mode_reflow.html]
+support-files = display_mode_reflow.html
+tags = fullscreen
+[test_hover.html]
+skip-if = true # bug 1346353
+[test_moz_document_rules.html]
+[test_stylesheet_clone_import_rule.html]
+support-files = import_useless1.css import_useless2.css
+[test_constructable_stylesheets_chrome_only_rules.html]
diff --git a/layout/style/test/chrome/display_mode.html b/layout/style/test/chrome/display_mode.html
new file mode 100644
index 0000000000..a4a0afb57e
--- /dev/null
+++ b/layout/style/test/chrome/display_mode.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1104916
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Display Mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ var imports = [ "SimpleTest", "is", "isnot", "ok" ];
+ for (var n of imports) {
+ window[n] = window.opener.wrappedJSObject[n];
+ }
+
+ /** Test for Display Mode **/
+
+ function waitOneEvent(element, name) {
+ return new Promise(function(resolve, reject) {
+ element.addEventListener(name, function() {
+ resolve();
+ }, {once: true});
+ });
+ }
+
+ function promiseNextTick() {
+ return new Promise(resolve => setTimeout(resolve, 0));
+ }
+
+ async function test_task() {
+ var iframe = document.getElementById("subdoc");
+ var subdoc = iframe.contentDocument;
+ var style = subdoc.getElementById("style");
+ var bodyComputedStyled = subdoc.defaultView.getComputedStyle(subdoc.body);
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+
+ function queryApplies(q) {
+ style.setAttribute("media", q);
+ return bodyComputedStyled.getPropertyValue("text-decoration-line") ==
+ "underline";
+ }
+
+ function shouldApply(q) {
+ ok(queryApplies(q), q + " should apply");
+ }
+
+ function shouldNotApply(q) {
+ ok(!queryApplies(q), q + " should not apply");
+ }
+
+ function setDisplayMode(mode) {
+ window.browsingContext.top.displayMode = mode;
+ }
+
+ shouldApply("all and (display-mode: browser)");
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: standalone)");
+ shouldNotApply("all and (display-mode: minimal-ui)");
+
+ // Test entering the OS's fullscreen mode.
+ var fullScreenEntered = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("KEY_F11");
+ await fullScreenEntered;
+ // Wait for the next tick to apply media feature changes. See bug 1430380.
+ await promiseNextTick();
+ shouldApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: browser)");
+ var fullScreenExited = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("KEY_F11");
+ await fullScreenExited;
+ // Wait for the next tick to apply media feature changes. See bug 1430380.
+ await promiseNextTick();
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldApply("all and (display-mode: browser)");
+
+ // Test entering fullscreen through document requestFullScreen.
+ fullScreenEntered = waitOneEvent(document, "mozfullscreenchange");
+ document.body.mozRequestFullScreen();
+ await fullScreenEntered
+ ok(document.mozFullScreenElement, "window entered fullscreen");
+ shouldApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: browser)");
+ fullScreenExited = waitOneEvent(document, "mozfullscreenchange");
+ document.mozCancelFullScreen();
+ await fullScreenExited;
+ ok(!document.mozFullScreenElement, "window exited fullscreen");
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldApply("all and (display-mode: browser)");
+
+ // Test entering display mode mode through docshell
+ setDisplayMode("standalone");
+ shouldApply("all and (display-mode: standalone)");
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: browser)");
+ shouldNotApply("all and (display-mode: minimal-ui)");
+
+ // Test that changes in the display mode are reflected
+ setDisplayMode("minimal-ui");
+ shouldApply("all and (display-mode: minimal-ui)");
+ shouldNotApply("all and (display-mode: standalone)");
+
+ // Set the display mode back.
+ setDisplayMode("browser");
+
+ window.close();
+ window.SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="test_task()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104916">Mozilla Bug 1104916</a>
+<iframe id="subdoc" src="http://mochi.test:8888/tests/layout/style/test/chrome/media_queries_iframe.html" allowfullscreen></iframe>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/display_mode_reflow.html b/layout/style/test/chrome/display_mode_reflow.html
new file mode 100644
index 0000000000..7b2a118cd6
--- /dev/null
+++ b/layout/style/test/chrome/display_mode_reflow.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1256084
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Display Mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ var imports = [ "SimpleTest", "is", "isnot", "ok" ];
+ for (var n of imports) {
+ window[n] = window.opener.wrappedJSObject[n];
+ }
+
+ /** Test for Display Mode **/
+
+ function waitOneEvent(element, name) {
+ return new Promise(function(resolve, reject) {
+ element.addEventListener(name, function() {
+ resolve();
+ }, {once: true});
+ });
+ }
+
+ function promiseNextTick() {
+ return new Promise(resolve => setTimeout(resolve, 0));
+ }
+
+ async function test_task() {
+ var iframe = document.getElementById("subdoc");
+ var subdoc = iframe.contentDocument;
+ var style = subdoc.getElementById("style");
+ var bodyComputedStyled = subdoc.defaultView.getComputedStyle(subdoc.body);
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+
+ var secondDiv = subdoc.getElementById("b");
+ var offsetTop = secondDiv.offsetTop;
+
+ // Test entering the OS's fullscreen mode.
+ var fullScreenEntered = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("KEY_F11");
+ await fullScreenEntered;
+
+ // Wait for the next tick to apply media feature changes. See bug 1430380.
+ await promiseNextTick();
+ ok(offsetTop !== secondDiv.offsetTop, "offset top changes");
+ var fullScreenExited = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("KEY_F11");
+ await fullScreenExited;
+
+ // Wait for the next tick to apply media feature changes. See bug 1430380.
+ await promiseNextTick();
+ ok(offsetTop === secondDiv.offsetTop, "offset top returns to original value");
+
+ offsetTop = secondDiv.offsetTop;
+ // Test entering fullscreen through document requestFullScreen.
+ fullScreenEntered = waitOneEvent(document, "mozfullscreenchange");
+ document.body.mozRequestFullScreen();
+ await fullScreenEntered
+ ok(offsetTop !== secondDiv.offsetTop, "offset top changes");
+ fullScreenExited = waitOneEvent(document, "mozfullscreenchange");
+ document.mozCancelFullScreen();
+ await fullScreenExited;
+ ok(offsetTop === secondDiv.offsetTop, "offset top returns to original value");
+
+ window.close();
+ window.SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="test_task()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1256084">Mozilla Bug 1256084</a>
+<iframe id="subdoc" src="http://mochi.test:8888/tests/layout/style/test/chrome/display_mode_reflow_iframe.html"></iframe>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/display_mode_reflow_iframe.html b/layout/style/test/chrome/display_mode_reflow_iframe.html
new file mode 100644
index 0000000000..c05880ce7f
--- /dev/null
+++ b/layout/style/test/chrome/display_mode_reflow_iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+ <title>Display Mode Reflow inner frame</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta http-equiv="Content-Style-Type" content="text/css">
+ <style type="text/css" id="style" media="all">
+ div {
+ border: 2px solid black;
+ width: 50px;
+ height: 50px;
+ }
+ @media (display-mode: fullscreen) {
+ #a { height: 100px; }
+ }
+ </style>
+</head>
+<body>
+ <div id="a"></div>
+ <div id="b"></div>
+</body>
+</html>
diff --git a/layout/style/test/chrome/hover_empty.html b/layout/style/test/chrome/hover_empty.html
new file mode 100644
index 0000000000..7879e1ce9f
--- /dev/null
+++ b/layout/style/test/chrome/hover_empty.html
@@ -0,0 +1,4 @@
+<html>
+<body>
+</body>
+</html>
diff --git a/layout/style/test/chrome/hover_helper.html b/layout/style/test/chrome/hover_helper.html
new file mode 100644
index 0000000000..b1ae14e8cc
--- /dev/null
+++ b/layout/style/test/chrome/hover_helper.html
@@ -0,0 +1,270 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for :hover</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <style type="text/css">
+
+ div#one { height: 10px; width: 10px; }
+ div#one:hover { background: #00f; }
+ div#one > div { height: 5px; width: 20px; }
+ div#one > div:hover { background: #f00; }
+
+ div#twoparent { overflow: hidden; height: 20px; }
+ div#two { width: 10px; height: 10px; }
+ div#two:hover { margin-left: 5px; background: #0f0; }
+ div#two + iframe { width: 50px; height: 10px; }
+ div#two:hover + iframe { width: 100px; }
+
+ </style>
+</head>
+<!-- need a set timeout because we need things to start after painting suppression ends -->
+<body onload="setTimeout(step1, 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<div id="display" style="position: absolute; top: 0; left: 0; width: 300px; height: 300px">
+
+ <div id="one"><div></div></div>
+
+ <div id="twoparent">
+ <div id="two"></div>
+ <iframe id="twoi" src="hover_empty.html"></iframe>
+ <div style="width: 5000px; height: 10px;"></div>
+ </div>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var imports = [ "SimpleTest", "is", "isnot", "ok" ];
+for (var name of imports) {
+ window[name] = window.opener.wrappedJSObject[name];
+}
+
+var div = document.getElementById("display");
+var divtwo = document.getElementById("two");
+var iframe = document.getElementById("twoi");
+var divtwoparent = document.getElementById("twoparent");
+
+iframe.contentDocument.open();
+iframe.contentDocument.write("<style type='text/css'>html, body { margin: 0; padding: 0; }<\/style><body>");
+iframe.contentDocument.close();
+
+var moveEvent = { type: "mousemove", clickCount: "0" };
+
+function setResize(str) {
+ var handler = function() {
+ iframe.contentWindow.removeEventListener("resize", arguments.callee);
+ setTimeout(str, 100);
+ };
+ iframe.contentWindow.addEventListener("resize", handler);
+}
+
+function step1() {
+ /** test basic hover **/
+ var divone = document.getElementById("one");
+ synthesizeMouse(divone, 5, 7, moveEvent, window);
+ is(getComputedStyle(divone, "").backgroundColor, "rgb(0, 0, 255)",
+ ":hover applies");
+ is(getComputedStyle(divone.firstChild, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ synthesizeMouse(divone, 5, 2, moveEvent, window);
+ is(getComputedStyle(divone, "").backgroundColor, "rgb(0, 0, 255)",
+ ":hover applies hierarchically");
+ is(getComputedStyle(divone.firstChild, "").backgroundColor, "rgb(255, 0, 0)",
+ ":hover applies");
+ synthesizeMouse(divone, 15, 7, moveEvent, window);
+ is(getComputedStyle(divone, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ is(getComputedStyle(divone.firstChild, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ synthesizeMouse(divone, 15, 2, moveEvent, window);
+ is(getComputedStyle(divone, "").backgroundColor, "rgb(0, 0, 255)",
+ ":hover applies hierarchically");
+ is(getComputedStyle(divone.firstChild, "").backgroundColor, "rgb(255, 0, 0)",
+ ":hover applies");
+
+ /** Test for Bug 302561 **/
+ setResize("step2();");
+ is(iframe.contentDocument.body.offsetWidth, 50,
+ ":hover does not apply (iframe body width)");
+ synthesizeMouse(divtwoparent, 7, 5, moveEvent, window);
+ is(iframe.contentDocument.body.offsetWidth, 100,
+ ":hover applies (iframe body width)");
+}
+
+var step2called = false;
+function step2() {
+ is(step2called, false, "step2 called only once");
+ step2called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgb(0, 255, 0)",
+ ":hover applies");
+ is(iframe.contentDocument.body.offsetWidth, 100,
+ ":hover applies (iframe body width)");
+ setResize("step3()");
+ synthesizeMouse(divtwoparent, 2, 5, moveEvent, window);
+ is(iframe.contentDocument.body.offsetWidth, 50,
+ ":hover does not apply (iframe body width)");
+}
+
+var step3called = false;
+function step3() {
+ is(step3called, false, "step3 called only once");
+ step3called = true;
+ if (getComputedStyle(iframe, "").width == "100px") {
+ // The two resize events may be coalesced into a single one.
+ step4();
+ return;
+ }
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ setResize("step4()");
+ /* expect to get a second resize from the oscillation */
+}
+
+var step4called = false;
+function step4() {
+ is(step4called, false, "step4 called only once (more than two cycles of oscillation)");
+ if (step4called)
+ return;
+ step4called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgb(0, 255, 0)",
+ ":hover applies");
+ setTimeout(step5, 500); // time to detect oscillations if they exist
+}
+
+var step5called = false;
+function step5() {
+ is(step5called, false, "step5 called only once");
+ step5called = true;
+ setResize("step6()");
+ synthesizeMouse(divtwoparent, 25, 5, moveEvent, window);
+}
+
+var step6called = false;
+function step6() {
+ is(step6called, false, "step6 called only once");
+ step6called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ synthesizeMouse(divtwoparent, 2, 5, moveEvent, window);
+ setTimeout(step7, 500); // time to detect oscillations if they exist
+}
+
+var step7called = false;
+function step7() {
+ is(step7called, false, "step7 called only once (more than two cycles of oscillation)");
+ if (step7called)
+ return;
+ step7called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ setTimeout(step8, 500); // time to detect oscillations if they exist
+}
+
+/* test the same case with scrolltop */
+
+var step8called = false;
+function step8() {
+ is(step8called, false, "step8 called only once");
+ step8called = true;
+ iframe.contentDocument.body.removeAttribute("onresize");
+ /* move the mouse out of the way */
+ synthesizeMouse(divtwoparent, 200, 5, moveEvent, window);
+ divtwoparent.scrollLeft = 5;
+ setResize("step9()");
+ synthesizeMouse(divtwoparent, 2, 5, moveEvent, window);
+ /* mouse now over 7, 5 */
+}
+
+var step9called = false;
+function step9() {
+ is(step9called, false, "step9 called only once");
+ step9called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgb(0, 255, 0)",
+ ":hover applies");
+ setResize("step10()");
+ divtwoparent.scrollLeft = 0; /* mouse now over 2,5 */
+}
+
+var step10called = false;
+function step10() {
+ is(step10called, false, "step10 called only once");
+ step10called = true;
+ if (getComputedStyle(iframe, "").width == "100px") {
+ // The two resize events may be coalesced into a single one.
+ step11();
+ return;
+ }
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ setResize("step11()");
+ /* expect to get a second resize from the oscillation */
+}
+
+var step11called = false;
+function step11() {
+ is(step11called, false, "step11 called only once (more than two cycles of oscillation)");
+ if (step11called)
+ return;
+ step11called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgb(0, 255, 0)",
+ ":hover applies");
+ setTimeout(step12, 500); // time to detect oscillations if they exist
+}
+
+var step12called = false;
+function step12() {
+ is(step12called, false, "step12 called only once");
+ step12called = true;
+ setResize("step13()");
+ divtwoparent.scrollLeft = 25; /* mouse now over 27,5 */
+}
+
+var step13called = false;
+function step13() {
+ is(step13called, false, "step13 called only once");
+ step13called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ setResize("step14()");
+ divtwoparent.scrollLeft = 0; /* mouse now over 2,5 */
+}
+
+var step14called = false;
+function step14() {
+ is(step14called, false, "step14 called only once");
+ step14called = true;
+ if (getComputedStyle(iframe, "").width == "50px") {
+ // The two resize events may be coalesced into a single one.
+ step15();
+ return;
+ }
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgb(0, 255, 0)",
+ ":hover applies");
+ setResize("step15()");
+ /* expect to get a second resize from the oscillation */
+}
+
+var step15called = false;
+function step15() {
+ is(step15called, false, "step15 called only once (more than two cycles of oscillation)");
+ if (step15called)
+ return;
+ step15called = true;
+ is(getComputedStyle(divtwo, "").backgroundColor, "rgba(0, 0, 0, 0)",
+ ":hover does not apply");
+ setTimeout(finish, 500); // time to detect oscillations if they exist
+}
+
+function finish() {
+ document.getElementById("display").style.display = "none";
+
+ var tester = window.SimpleTest;
+ window.close();
+ tester.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/import_useless1.css b/layout/style/test/chrome/import_useless1.css
new file mode 100644
index 0000000000..37e1a3d1d9
--- /dev/null
+++ b/layout/style/test/chrome/import_useless1.css
@@ -0,0 +1,3 @@
+.unlikely_to_match_anything {
+ color: black;
+}
diff --git a/layout/style/test/chrome/import_useless2.css b/layout/style/test/chrome/import_useless2.css
new file mode 100644
index 0000000000..37e1a3d1d9
--- /dev/null
+++ b/layout/style/test/chrome/import_useless2.css
@@ -0,0 +1,3 @@
+.unlikely_to_match_anything {
+ color: black;
+}
diff --git a/layout/style/test/chrome/match.png b/layout/style/test/chrome/match.png
new file mode 100644
index 0000000000..d3f299bf58
--- /dev/null
+++ b/layout/style/test/chrome/match.png
Binary files differ
diff --git a/layout/style/test/chrome/mismatch.png b/layout/style/test/chrome/mismatch.png
new file mode 100644
index 0000000000..8f9da3f00f
--- /dev/null
+++ b/layout/style/test/chrome/mismatch.png
Binary files differ
diff --git a/layout/style/test/chrome/moz_document_helper.html b/layout/style/test/chrome/moz_document_helper.html
new file mode 100644
index 0000000000..8b331b19e0
--- /dev/null
+++ b/layout/style/test/chrome/moz_document_helper.html
@@ -0,0 +1,2 @@
+<!DOCTYPE HTML>
+<div id="display" style="position: relative"></div>
diff --git a/layout/style/test/chrome/test_bug1157097.html b/layout/style/test/chrome/test_bug1157097.html
new file mode 100644
index 0000000000..febf4952fb
--- /dev/null
+++ b/layout/style/test/chrome/test_bug1157097.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Test for bug 1157097</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+<style>
+.blue { color: blue; }
+.red { color: red; }
+.inline-block { display: inline-block; }
+</style>
+<body onload=run()>
+<p><span id=s1 class=blue><b></b></span><span id=s2 class=red><b></b></span></p>
+<script>
+function run() {
+ window.windowUtils.postRestyleSelfEvent(document.querySelector("p"));
+ document.querySelectorAll("span")[0].className = "";
+ document.querySelectorAll("b")[0].className = "inline-block";
+ document.querySelectorAll("span")[1].className = "blue";
+ window.windowUtils.postRestyleSelfEvent(document.querySelectorAll("b")[1]);
+
+ document.body.offsetTop;
+
+ ok(true, "finished (hopefully we didn't assert)");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
diff --git a/layout/style/test/chrome/test_bug1346623.html b/layout/style/test/chrome/test_bug1346623.html
new file mode 100644
index 0000000000..30e21bcc36
--- /dev/null
+++ b/layout/style/test/chrome/test_bug1346623.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1346623</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body onload="startTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1346623">Mozilla Bug 1346623</a>
+<div id="display">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var winUtils = window.windowUtils;
+
+function startTest() {
+ // load some styles at the agent level
+ var css = `
+ #ac-parent { color: green; }
+ #ac-child.abc { }
+ `;
+ var sheetURL = "data:text/css," + encodeURIComponent(css);
+ winUtils.loadSheetUsingURIString(sheetURL, winUtils.AGENT_SHEET);
+
+ // add canvas anonymous content
+ var bq = document.createElement("blockquote");
+ bq.id = "ac-parent";
+ bq.textContent = "This blockquote text should be green.";
+ var div = document.createElement("div");
+ div.id = "ac-child";
+ div.textContent = " This div text should be green.";
+ bq.appendChild(div);
+ var ac = document.insertAnonymousContent(bq);
+ document.body.offsetWidth;
+
+ is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)",
+ "color before reframing");
+
+ // reframe the root
+ document.documentElement.style.display = "flex";
+ document.body.offsetWidth;
+
+ // restyle the div
+ ac.setAttributeForElement("ac-child", "class", "abc");
+ document.body.offsetWidth;
+
+ is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)",
+ "color after reframing");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_bug1371453.html b/layout/style/test/chrome/test_bug1371453.html
new file mode 100644
index 0000000000..dc61d02caf
--- /dev/null
+++ b/layout/style/test/chrome/test_bug1371453.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test for Bug 1371453</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<link rel="stylesheet" href="data:text/css,{}">
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const Cu = SpecialPowers.Components.utils;
+const { ContentTaskUtils } = ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm");
+document.styleSheetChangeEventsEnabled = true;
+
+onload = runTest;
+
+async function runTest() {
+ const sheet = document.getElementsByTagName("link")[1].sheet;
+ sheet.insertRule('@import url("blahblah")', 0);
+
+ const rule = sheet.cssRules[0];
+ is(rule.type, CSSRule.IMPORT_RULE, "Got expected import rule.");
+ isnot(rule.styleSheet, null, "Import rule contains a stylesheet.");
+ isnot(rule.media, null, "Import rule contains a media list.");
+ is(rule.href, "blahblah", "Import rule contains expected href.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_bug418986-2.xhtml b/layout/style/test/chrome/test_bug418986-2.xhtml
new file mode 100644
index 0000000000..152cac004e
--- /dev/null
+++ b/layout/style/test/chrome/test_bug418986-2.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=418986
+-->
+<window title="Mozilla Bug 418986"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <style id="test-css" scoped="true"></style>
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986"
+ target="_blank">Mozilla Bug 418986</a>
+ <p id="display"></p>
+ <p id="pictures"></p>
+ </body>
+
+ <script type="text/javascript" src="bug418986-2.js"></script>
+ <!-- test code goes here -->
+ <script type="text/javascript">
+ // Run all tests now.
+ window.onload = function () {
+ add_task(async function() {
+ await test(false);
+ });
+ };
+ </script>
+</window>
diff --git a/layout/style/test/chrome/test_bug535806.xhtml b/layout/style/test/chrome/test_bug535806.xhtml
new file mode 100644
index 0000000000..7f4ec286bc
--- /dev/null
+++ b/layout/style/test/chrome/test_bug535806.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=535806
+-->
+<window title="Mozilla Bug 535806"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=535806"
+ target="_blank">Mozilla Bug 535806</a>
+ </body>
+
+ <iframe id="f"/>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 535806 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.addEventListener("load", function() {
+ $("f").setAttribute("src", "bug535806-html.html");
+ });
+
+ function htmlLoaded() {
+ $("f").setAttribute("src", "bug535806-xul.xhtml");
+ }
+
+ function xulLoaded() {
+ var doc = $("f").contentDocument;
+ is(doc.defaultView.getComputedStyle(doc.getElementById("s")).color,
+ "rgb(0, 128, 0)");
+ SimpleTest.finish();
+ }
+
+
+ ]]>
+ </script>
+</window>
diff --git a/layout/style/test/chrome/test_chrome_only_media_queries.html b/layout/style/test/chrome/test_chrome_only_media_queries.html
new file mode 100644
index 0000000000..1a2fb098c0
--- /dev/null
+++ b/layout/style/test/chrome/test_chrome_only_media_queries.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<title>Test for parsing of non-content-exposed media-queries.</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<script src="chrome-only-media-queries.js"></script>
+<style></style>
+<script>
+const SHEET = document.querySelector('style');
+
+SimpleTest.waitForExplicitFinish();
+
+async function testWithPref() {
+ await new Promise(r => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["layout.css.forced-colors.enabled", false],
+ ],
+ },
+ r
+ );
+ });
+ expectKnown("(forced-colors: none)");
+ expectKnown("(forced-colors: active)");
+ expectKnown("(forced-colors)");
+ SimpleTest.finish();
+}
+
+function expect(q, shouldBeKnown) {
+ is(matchMedia(q).media, q, "Serialization should roundtrip");
+ is(matchMedia(`${q} or (not ${q})`).matches, shouldBeKnown, `Query should${shouldBeKnown ? "" : " not"} be known`);
+}
+
+function expectKnown(q) {
+ expect(q, true);
+}
+
+function expectUnkown(q) {
+ expect(q, false);
+}
+
+// Test a toggle that should always match for `1` or `0`.
+function testToggle(toggle) {
+ expectKnown(`(${toggle})`);
+ expectKnown(`(${toggle}: 1)`);
+ expectKnown(`(${toggle}: 0)`);
+
+ expectUnkown(`(${toggle}: foo)`);
+ expectUnkown(`(${toggle}: true)`);
+ expectUnkown(`(${toggle}: false)`);
+ expectUnkown(`(${toggle}: -1)`);
+ expectUnkown(`(min-${toggle}: 0)`);
+ expectUnkown(`(max-${toggle}: 0)`);
+ expectUnkown(`(max-${toggle})`);
+ expectUnkown(`(min-${toggle})`);
+
+ let matches_1 = matchMedia(`(${toggle}: 1)`).matches;
+ let matches_0 = matchMedia(`(${toggle}: 0)`).matches;
+ isnot(matches_0, matches_1, `Should not match both true and false: ${toggle}`);
+ is(matches_0 || matches_1, true, `Should match at least one: ${toggle}`);
+}
+
+for (let toggle of CHROME_ONLY_TOGGLES) {
+ testToggle(toggle)
+}
+
+for (let query of CHROME_ONLY_QUERIES) {
+ expectKnown(query);
+}
+
+// These might be exposed to content by pref, we just want to make sure they're
+// always exposed to chrome.
+expectKnown("(prefers-contrast: more)")
+expectKnown("(prefers-contrast: no-preference)")
+expectKnown("(prefers-contrast: less)");
+expectKnown("(prefers-contrast)")
+
+expectKnown("(forced-colors: none)");
+expectKnown("(forced-colors: active)");
+expectKnown("(forced-colors)");
+
+expectUnkown("(-moz-platform: )");
+
+testWithPref();
+</script>
diff --git a/layout/style/test/chrome/test_constructable_stylesheets_chrome_only_rules.html b/layout/style/test/chrome/test_constructable_stylesheets_chrome_only_rules.html
new file mode 100644
index 0000000000..4d9647ba27
--- /dev/null
+++ b/layout/style/test/chrome/test_constructable_stylesheets_chrome_only_rules.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Test for chrome-only rules in constructable stylesheets</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+ add_task(async function chrome_rules_constructable_stylesheets() {
+ let sheet = new CSSStyleSheet();
+ sheet.replaceSync(".foo { -moz-default-appearance: none }");
+ is(sheet.cssRules[0].style.length, 1, "Should parse chrome-only property in chrome document");
+ });
+</script>
diff --git a/layout/style/test/chrome/test_display_mode.html b/layout/style/test/chrome/test_display_mode.html
new file mode 100644
index 0000000000..69e72d5ab8
--- /dev/null
+++ b/layout/style/test/chrome/test_display_mode.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1104916
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Display Mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ async function startTest() {
+ await new Promise(r => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["dom.security.featurePolicy.header.enabled", true],
+ ["dom.security.featurePolicy.webidl.enabled", true],
+ ],
+ },
+ r
+ );
+ });
+ // Chrome test run tests in iframe, and fullscreen is disabled by default.
+ // So run the test in a separate window.
+ window.open("display_mode.html", "display_mode", "width=500,height=500,resizable");
+ }
+ </script>
+</head>
+<body onload="startTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104916">Mozilla Bug 1104916</a>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_display_mode_reflow.html b/layout/style/test/chrome/test_display_mode_reflow.html
new file mode 100644
index 0000000000..01022207f3
--- /dev/null
+++ b/layout/style/test/chrome/test_display_mode_reflow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1256084
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Display Mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ async function startTest() {
+ await new Promise(r => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["dom.security.featurePolicy.header.enabled", true],
+ ["dom.security.featurePolicy.webidl.enabled", true],
+ ],
+ },
+ r
+ );
+ });
+ // Chrome test run tests in iframe, and fullscreen is disabled by default.
+ // So run the test in a separate window.
+ window.open("display_mode_reflow.html", "display_mode_reflow", "width=500,height=500,resizable");
+ }
+ </script>
+</head>
+<body onload="startTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1256084">Mozilla Bug 1256084</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_hover.html b/layout/style/test/chrome/test_hover.html
new file mode 100644
index 0000000000..019f537e8c
--- /dev/null
+++ b/layout/style/test/chrome/test_hover.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for :hover</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body onload="startTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<div id="display">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function startTest() {
+ // Run the test in a separate window so that the parent document doesn't have
+ // anything that will cause reflows and dispatch synth mouse moves when we don't
+ // want them and disturb our test.
+ window.open("hover_helper.html", "hover_helper", "width=200,height=300");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_moz_document_rules.html b/layout/style/test/chrome/test_moz_document_rules.html
new file mode 100644
index 0000000000..c28fc964ed
--- /dev/null
+++ b/layout/style/test/chrome/test_moz_document_rules.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for @-moz-document rules</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=398962">Mozilla Bug 398962</a>
+<iframe id="iframe" src="http://mochi.test:8888/tests/layout/style/test/chrome/moz_document_helper.html"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+var [gStyleSheetService, gIOService] = (function() {
+ return [
+ Cc["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Ci.nsIStyleSheetService),
+ Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ ];
+})();
+function set_user_sheet(sheeturi)
+{
+ var uri = gIOService.newURI(sheeturi);
+ gStyleSheetService.loadAndRegisterSheet(uri, gStyleSheetService.USER_SHEET);
+}
+function remove_user_sheet(sheeturi)
+{
+ var uri = gIOService.newURI(sheeturi);
+ gStyleSheetService.unregisterSheet(uri, gStyleSheetService.USER_SHEET);
+}
+
+function run()
+{
+ var iframe = document.getElementById("iframe");
+ var subdoc = iframe.contentDocument;
+ var subwin = iframe.contentWindow;
+ var cs = subwin.getComputedStyle(subdoc.getElementById("display"));
+ var zIndexCounter = 0;
+
+ function test_document_rule(urltests, shouldapply)
+ {
+ var zIndex = ++zIndexCounter;
+ var encodedRule = encodeURI("@-moz-document " + urltests + " { ") +
+ "%23" + // encoded hash character for "#display"
+ encodeURI("display { z-index: " + zIndex + " } }");
+ var sheeturi = "data:text/css," + encodedRule;
+ set_user_sheet(sheeturi);
+ if (shouldapply) {
+ is(cs.zIndex, String(zIndex),
+ "@-moz-document " + urltests +
+ " should apply to this document");
+ } else {
+ is(cs.zIndex, "auto",
+ "@-moz-document " + urltests +
+ " should NOT apply to this document");
+ }
+ remove_user_sheet(sheeturi);
+ }
+
+ test_document_rule("domain(mochi.test)", true);
+ test_document_rule("domain(\"mochi.test\")", true);
+ test_document_rule("domain('mochi.test')", true);
+ test_document_rule("domain('test')", true);
+ test_document_rule("domain(.test)", false);
+ test_document_rule("domain('.test')", false);
+ test_document_rule("domain('ochi.test')", false);
+ test_document_rule("domain(ochi.test)", false);
+ test_document_rule("url-prefix(http://moch)", true);
+ test_document_rule("url-prefix(http://och)", false);
+ test_document_rule("url-prefix(http://mochi.test)", true);
+ test_document_rule("url-prefix(http://mochi.test:88)", true);
+ test_document_rule("url-prefix(http://mochi.test:8888)", true);
+ test_document_rule("url-prefix(http://mochi.test:8888/)", true);
+ test_document_rule("url-prefix('http://mochi.test:8888/tests/layout/style/test/chrome/moz_document_helper.html')", true);
+ test_document_rule("url-prefix('http://mochi.test:8888/tests/layout/style/test/chrome/moz_document_helper.htmlx')", false);
+ test_document_rule("url(http://mochi.test:8888/)", false);
+ test_document_rule("url('http://mochi.test:8888/tests/layout/style/test/chrome/moz_document_helper.html')", true);
+ test_document_rule("url('http://mochi.test:8888/tests/layout/style/test/chrome/moz_document_helper.htmlx')", false);
+ test_document_rule("regexp(.*ochi.*)", false); // syntax error
+ test_document_rule("regexp('.*ochi.*')", true);
+ test_document_rule("regexp('ochi.*')", false);
+ test_document_rule("regexp('.*ochi')", false);
+ test_document_rule("regexp('http:.*ochi.*')", true);
+ test_document_rule("regexp('http:.*ochi')", false);
+ test_document_rule("regexp('http:.*oCHi.*')", false); // case sensitive
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/style/test/chrome/test_stylesheet_clone_import_rule.html b/layout/style/test/chrome/test_stylesheet_clone_import_rule.html
new file mode 100644
index 0000000000..ce2bef8016
--- /dev/null
+++ b/layout/style/test/chrome/test_stylesheet_clone_import_rule.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+
+<style>div { color: green; }</style>
+
+<link id="theOnlyLink" rel="stylesheet" type="text/css" href="import_useless1.css">
+
+<div id="theOnlyDiv">This text will change colors several times.</div>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ const Cu = SpecialPowers.Components.utils;
+ const { ContentTaskUtils } = ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm");
+
+ let theOnlyDiv = document.getElementById("theOnlyDiv");
+ let link = document.getElementById("theOnlyLink");
+ let stylesheet = link.sheet;
+
+ runTest().catch(function(reason) {
+ ok(false, "Failed with reason: " + reason);
+ }).then(function() {
+ SimpleTest.finish();
+ });
+
+ function cssRulesToString(cssRules) {
+ return Array.from(cssRules).map(rule => rule.cssText).join('');
+ }
+
+ async function runTest() {
+ // Test that the div is initially red (from base.css)
+ is(getComputedStyle(theOnlyDiv).color, "rgb(0, 128, 0)", "div begins as green.");
+
+ // Insert some import rules.
+ stylesheet.insertRule('@import url("import_useless2.css")', 0);
+ stylesheet.insertRule('@import url("import_useless2.css")', 1);
+
+ // Do some sanity checking of our import rules.
+ let primaryRules = stylesheet.cssRules;
+ await ContentTaskUtils.waitForCondition(function() {
+ try {
+ primaryRules[0].styleSheet.cssRules;
+ primaryRules[1].styleSheet.cssRules;
+ return true;
+ } catch (ex) {
+ return false;
+ }
+ });
+
+ // Make some helper variables for the comparison tests.
+ let importSheet1 = primaryRules[0].styleSheet;
+ let rules1 = importSheet1.cssRules;
+
+ let importSheet2 = primaryRules[1].styleSheet;
+ let rules2 = importSheet2.cssRules;
+
+ // Confirm that these two sheets are meaningfully the same.
+ is(cssRulesToString(rules1), cssRulesToString(rules2), "Cloned sheet rules are equivalent.");
+
+ // Add a color-changing rule to the first stylesheet.
+ importSheet1.insertRule('div { color: blue; }');
+ rules1 = importSheet1.cssRules;
+
+ // And make sure that it has an effect.
+ is(getComputedStyle(theOnlyDiv).color, "rgb(0, 0, 255)", "div becomes blue.");
+
+ // Make sure that the two sheets have different rules now.
+ isnot(cssRulesToString(rules1), cssRulesToString(rules2), "Cloned sheet rules are no longer equivalent.");
+
+ // Add a color-changing rule to the second stylesheet (that will mask the first).
+ importSheet2.insertRule('div { color: red; }');
+ // And make sure that it has an effect.
+ is(getComputedStyle(theOnlyDiv).color, "rgb(255, 0, 0)", "div becomes red.");
+
+ // Delete the second sheet by removing the import rule, and make sure the color changes back.
+ stylesheet.deleteRule(1);
+ is(getComputedStyle(theOnlyDiv).color, "rgb(0, 0, 255)", "div goes back to blue.");
+
+ // Delete the first sheet by removing the import rule, and make sure the color changes back.
+ stylesheet.deleteRule(0);
+ is(getComputedStyle(theOnlyDiv).color, "rgb(0, 128, 0)", "div goes back to green.");
+ }
+</script>
+</html>