Backed out 5 changesets (bug 1511138) for browser chrome failures on browser_selectpopup_colors.js. CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Thu, 06 Dec 2018 20:14:00 +0200
changeset 508765 90189bd84466d74e64d4eb2a33138152e84b241c
parent 508764 65e99e6399b925b8ce7ce17b687096b94bf9f2e1
child 508786 a52c254930e8fd4372d2a439b8e0e6cc10a351f8
child 508802 d42abd4f7941db6d412df94678684d1d40a67cff
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1511138
milestone65.0a1
backs out65e99e6399b925b8ce7ce17b687096b94bf9f2e1
5f5d1c50a34213414f3f6a1f05b575e4f2176daf
e03afbff55f39243019d88945601b4ce75ae65c0
d2d3e7a822b5f4c78e58d6552bbd1c8fa431a8ef
e601a2fbd07720ad837cef0567233f2a631e038a
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 5 changesets (bug 1511138) for browser chrome failures on browser_selectpopup_colors.js. CLOSED TREE Backed out changeset 65e99e6399b9 (bug 1511138) Backed out changeset 5f5d1c50a342 (bug 1511138) Backed out changeset e03afbff55f3 (bug 1511138) Backed out changeset d2d3e7a822b5 (bug 1511138) Backed out changeset e601a2fbd077 (bug 1511138)
browser/components/extensions/test/browser/browser_ext_popup_background.js
dom/base/test/test_bug564863.xhtml
dom/tests/mochitest/general/test_offsets.js
layout/reftests/cssom/computed-style-cross-window-ref.html
layout/reftests/cssom/computed-style-cross-window.html
layout/style/nsComputedDOMStyle.cpp
layout/style/test/chrome/bug418986-2.js
layout/style/test/test_bug1203766.html
layout/style/test/test_default_bidi_css.html
testing/web-platform/meta/css/cssom/getComputedStyle-detached-subtree.html.ini
testing/web-platform/tests/css/css-fonts/font-variant-alternates-parsing.html
toolkit/components/extensions/test/browser/browser_ext_themes_autocomplete_popup.js
toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
toolkit/modules/LightweightThemeConsumer.jsm
--- a/browser/components/extensions/test/browser/browser_ext_popup_background.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_background.js
@@ -15,20 +15,17 @@ async function testPanel(browser, standA
            "rgb(255, 255, 255)", "Arrow fill should be set to #fff when no background is supplied and popup is standAlone");
       } else {
         let default_background =
           getComputedStyle(document.documentElement).getPropertyValue("--arrowpanel-background");
         // Need to apply the color to a node and get the computed value
         // to resolve CSS named colors such as -moz-field.
         let span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
         span.style.color = default_background;
-        span.style.display = "none";
-        document.documentElement.appendChild(span);
         let default_background_computed = getComputedStyle(span).color;
-        span.remove();
 
         is(getComputedStyle(arrow).fill, default_background_computed, "Arrow fill should be the default one");
       }
       return;
     }
 
     is(getComputedStyle(arrowContent).backgroundColor, background, "Arrow content should have correct background");
     is(getComputedStyle(arrow).fill, background, "Arrow should have correct background");
--- a/dom/base/test/test_bug564863.xhtml
+++ b/dom/base/test/test_bug564863.xhtml
@@ -76,22 +76,21 @@ function checkHasId(test) {
   is($("div_id"), div, "div getElementById " + test);
   is($("a_id"),   a,   "a getElementById " + test);
   is($("xul_id"), xul, "xul getElementById " + test);
   is($("svg_id"), svg, "svg getElementById " + test);
   is($("ns_id"),  nsx, "ns getElementById " + test);
 }
 
 function checkHasIdNoGEBI(test) {
-  const connected = test != "removed node";
-  is(div_cs.color, connected ? "rgb(10, 10, 10)" : "", "div color " + test);
-  is(a_cs.color,   connected ? "rgb(20, 20, 20)" : "", "a color " + test);
-  is(xul_cs.color, connected ? "rgb(30, 30, 30)" : "", "xul color " + test);
-  is(svg_cs.color, connected ? "rgb(40, 40, 40)" : "", "svg color " + test);
-  is(nsx_cs.color, connected ? "rgb(50, 50, 50)" : "", "nsx color " + test);
+  is(div_cs.color, "rgb(10, 10, 10)", "div color " + test);
+  is(a_cs.color,   "rgb(20, 20, 20)", "a color " + test);
+  is(xul_cs.color, "rgb(30, 30, 30)", "xul color " + test);
+  is(svg_cs.color, "rgb(40, 40, 40)", "svg color " + test);
+  is(nsx_cs.color, "rgb(50, 50, 50)", "nsx color " + test);
 
   is(div.id, "div_id", "div id " + test);
   is(a.id,   "a_id",   "a id " + test);
   is(xul.id, "xul_id", "xul id " + test);
   is(svg.id, "svg_id", "svg id " + test);
   is (nsx.getAttribute("id"), "ns_id", "ns id " + test);
 }
 
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -211,12 +211,12 @@ function checkCoords(element, type, left
     ok(element[type + "Width"] == 0 && element[type + "Height"] == 0,
        element.id + " has zero " + type + " width and height");
 }
 
 function gcs(element, prop)
 {
   var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ?
                    element.getAttribute(prop) : getComputedStyle(element, "")[prop];
-  if (propVal == "auto" || element.id == "nonappended")
+  if (propVal == "auto")
     return 0;
   return parseFloat(propVal);
 }
--- a/layout/reftests/cssom/computed-style-cross-window-ref.html
+++ b/layout/reftests/cssom/computed-style-cross-window-ref.html
@@ -13,30 +13,38 @@ var gRunCount = 2;
 function run() {
   if (--gRunCount != 0)
     return;
 
   var i = document.getElementById("i");
 
   var pout = document.getElementById("out");
   var poutnone = document.getElementById("outnone");
+  var poutdet = document.createElement("p");
   var pin = i.contentDocument.getElementsByTagName("p")[0];
   var pinnone = i.contentDocument.getElementsByTagName("p")[1];
+  var pindet = i.contentDocument.createElement("p");
 
   document.getElementById("res1").style.color =
     window.getComputedStyle(pin).color;
 
   document.getElementById("res2").style.color =
     i.contentWindow.getComputedStyle(pout).color;
 
   document.getElementById("res3").style.color =
     window.getComputedStyle(pinnone).color;
 
   document.getElementById("res4").style.color =
     i.contentWindow.getComputedStyle(poutnone).color;
+
+  document.getElementById("res5").style.color =
+    window.getComputedStyle(pindet).color;
+
+  document.getElementById("res6").style.color =
+    i.contentWindow.getComputedStyle(poutdet).color;
 }
 
 </script>
 <body onload="run()">
 
 <p id="out">This is a paragraph outside the iframe.</p>
 <div style="display:none"><p id="outnone">This is a paragraph outside the iframe.</p></div>
 
@@ -52,8 +60,16 @@ is.</div>
 
 <div style="color:fuchsia">This paragraph is the color that
 outerWindow.getComputedStyle says the display:none paragraph inside the
 iframe is.</div>
 
 <div style="color:blue">This paragraph is the color that
 iframeWindow.getComputedStyle says the display:none paragraph outside
 the iframe is.</div>
+
+<div style="color:blue">This paragraph is the color that
+outerWindow.getComputedStyle says the detached paragraph inside the
+iframe is.</div>
+
+<div style="color:fuchsia">This paragraph is the color that
+iframeWindow.getComputedStyle says the detached paragraph outside
+the iframe is.</div>
--- a/layout/reftests/cssom/computed-style-cross-window.html
+++ b/layout/reftests/cssom/computed-style-cross-window.html
@@ -14,31 +14,39 @@ var gRunCount = 2;
 function run() {
   if (--gRunCount != 0)
     return;
 
   var i = document.getElementById("i");
 
   var pout = document.getElementById("out");
   var poutnone = document.getElementById("outnone");
+  var poutdet = document.createElement("p");
   var pin = i.contentDocument.getElementsByTagName("p")[0];
   var pinnone = i.contentDocument.getElementsByTagName("p")[1];
+  var pindet = i.contentDocument.createElement("p");
 
   document.getElementById("res1").style.color =
     window.getComputedStyle(pin).color;
 
   document.getElementById("res2").style.color =
     i.contentWindow.getComputedStyle(pout).color;
 
   document.getElementById("res3").style.color =
     window.getComputedStyle(pinnone).color;
 
   document.getElementById("res4").style.color =
     i.contentWindow.getComputedStyle(poutnone).color;
 
+  document.getElementById("res5").style.color =
+    window.getComputedStyle(pindet).color;
+
+  document.getElementById("res6").style.color =
+    i.contentWindow.getComputedStyle(poutdet).color;
+
   document.documentElement.removeAttribute("class");
 }
 
 </script>
 <body onload="run()">
 
 <p id="out">This is a paragraph outside the iframe.</p>
 <div style="display:none"><p id="outnone">This is a paragraph outside the iframe.</p></div>
@@ -55,8 +63,16 @@ is.</div>
 
 <div id="res3">This paragraph is the color that
 outerWindow.getComputedStyle says the display:none paragraph inside the
 iframe is.</div>
 
 <div id="res4">This paragraph is the color that
 iframeWindow.getComputedStyle says the display:none paragraph outside
 the iframe is.</div>
+
+<div id="res5">This paragraph is the color that
+outerWindow.getComputedStyle says the detached paragraph inside the
+iframe is.</div>
+
+<div id="res6">This paragraph is the color that
+iframeWindow.getComputedStyle says the detached paragraph outside
+the iframe is.</div>
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -503,22 +503,21 @@ already_AddRefed<ComputedStyle> nsComput
     }
   }
 
   CSSPseudoElementType pseudoType = GetPseudoType(aPseudo);
   if (aPseudo && pseudoType >= CSSPseudoElementType::Count) {
     return nullptr;
   }
 
-  if (!aElement->IsInComposedDoc()) {
-    // Don't return styles for disconnected elements, that makes no sense. This
-    // can only happen with a non-null presShell for cross-document calls.
-    //
-    // FIXME(emilio, bug 1483798): This should also not return styles for
-    // elements outside of the flat tree, not just outside of the document.
+  if (aElement->IsInNativeAnonymousSubtree() && !aElement->IsInComposedDoc()) {
+    // Normal web content can't access NAC, but Accessibility, DevTools and
+    // Editor use this same API and this may get called for anonymous content.
+    // Computing the style of a pseudo-element that doesn't have a parent
+    // doesn't really make sense.
     return nullptr;
   }
 
   // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
   if (inDocWithShell && aStyleType == eAll &&
       !aElement->IsHTMLElement(nsGkAtoms::area)) {
--- a/layout/style/test/chrome/bug418986-2.js
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -226,17 +226,21 @@ var generateCSSLines = function (resisti
     lines += windows_versions.map(val => "(-moz-os-version: " + val + ")").join(", ") +
              " { #-moz-os-version { background-color: " + (resisting ? "red" : "green") + ";} }\n";
   }
   return lines;
 };
 
 // __green__.
 // Returns the computed color style corresponding to green.
-var green = "rgb(0, 128, 0)";
+var green = (function () {
+  let temp = document.createElement("span");
+  temp.style.backgroundColor = "green";
+  return getComputedStyle(temp).backgroundColor;
+})();
 
 // __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);
@@ -248,19 +252,17 @@ var testCSS = function (resisting) {
 };
 
 // __testOSXFontSmoothing(resisting)__.
 // When fingerprinting resistance is enabled, the `getComputedStyle`
 // should always return `undefined` for `MozOSXFontSmoothing`.
 var testOSXFontSmoothing = function (resisting) {
   let div = document.createElement("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) {
--- a/layout/style/test/test_bug1203766.html
+++ b/layout/style/test/test_bug1203766.html
@@ -31,82 +31,82 @@ body > .a { display: none; color: green;
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(function() {
 
   // Element that goes from being out of the document to in the document.
   var e = document.createElement("div");
   e.className = "x";
   var cs = getComputedStyle(e);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   document.body.appendChild(e);
   is(cs.color, "rgb(0, 128, 0)");
 
   // Element that goes from in the document (and display:none) to out of
   // the document.
   e = document.querySelector(".y");
   cs = getComputedStyle(e);
   is(cs.color, "rgb(255, 0, 0)");
   e.remove();
-  is(cs.color, "");
+  is(cs.color, "rgb(0, 128, 0)");
 
   // Element that is removed from an out-of-document tree.
   e = document.createElement("div");
   f = document.createElement("span");
   f.className = "z";
   e.appendChild(f);
   cs = getComputedStyle(f);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   f.remove();
-  is(cs.color, "");
+  is(cs.color, "rgb(0, 128, 0)");
 
   // Element going from not in document to in document and display:none.
   e = document.createElement("div");
   e.className = "a";
   cs = getComputedStyle(e);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   document.body.appendChild(e);
   is(cs.color, "rgb(0, 128, 0)");
 
   // Element going from not in document to in document and child of
   // display:none element.
   e = document.createElement("div");
   e.className = "c";
   cs = getComputedStyle(e);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   document.querySelector(".b").appendChild(e);
   is(cs.color, "rgb(0, 128, 0)");
 
   // Element that is added to an out-of-document tree.
   e = document.createElement("div");
   e.className = "d";
   f = document.createElement("span");
   f.className = "e";
   cs = getComputedStyle(f);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   e.appendChild(f);
-  is(cs.color, "");
+  is(cs.color, "rgb(0, 128, 0)");
 
   // Element that is outside the document when an attribute is modified to
   // cause a different rule to match.
   e = document.createElement("div");
   e.className = "f";
   cs = getComputedStyle(e);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   e.className = "g";
-  is(cs.color, "");
+  is(cs.color, "rgb(0, 128, 0)");
 
   // Element that is outside the document when an ancestor is modified to
   // cause a different rule to match.
   e = document.createElement("div");
   e.className = "h";
   f = document.createElement("span");
   f.className = "i";
   e.appendChild(f);
   cs = getComputedStyle(f);
-  is(cs.color, "");
+  is(cs.color, "rgb(255, 0, 0)");
   e.className = "j";
-  is(cs.color, "");
+  is(cs.color, "rgb(0, 128, 0)");
 
   SimpleTest.finish();
 });
 </script>
 </pre>
--- a/layout/style/test/test_default_bidi_css.html
+++ b/layout/style/test/test_default_bidi_css.html
@@ -16,17 +16,16 @@
 
 /** Test for default bidi css  **/
 function styleOf(name, attributes) {
     var element = document.createElement(name);
     for (var name in attributes) {
         var value = attributes[name];
         element.setAttribute(name, value);
     }
-    document.body.appendChild(element);
     return getComputedStyle(element);
 }
 
 var tests = [
     ['div', {}, 'ltr', 'isolate'],
     ['div', {'dir': 'ltr'}, 'ltr', 'isolate'],
     ['div', {'dir': 'rtl'}, 'rtl', 'isolate'],
     ['div', {'dir': 'auto'}, 'ltr', 'isolate'],
--- a/testing/web-platform/meta/css/cssom/getComputedStyle-detached-subtree.html.ini
+++ b/testing/web-platform/meta/css/cssom/getComputedStyle-detached-subtree.html.ini
@@ -1,9 +1,15 @@
 [getComputedStyle-detached-subtree.html]
+  [getComputedStyle returns no style for detached element]
+    expected: FAIL
+
   [getComputedStyle returns no style for element in non-rendered iframe (display: none)]
     expected: FAIL
 
   [getComputedStyle returns no style for element outside the flat tree]
     expected: FAIL
 
   [getComputedStyle returns no style for descendant outside the flat tree]
     expected: FAIL
+
+  [getComputedStyle returns no style for shadow tree outside of flattened tree]
+    expected: FAIL
--- a/testing/web-platform/tests/css/css-fonts/font-variant-alternates-parsing.html
+++ b/testing/web-platform/tests/css/css-fonts/font-variant-alternates-parsing.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <meta charset="utf-8">
 <title>CSS Test:  font-variant-alternates: historical-forms; parses case-insensitively</title>
 <link rel="author" title="Emilio Cobos Álvarez" href="emilio@crisal.io">
 <link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-variant-alternates-prop">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<div></div>
 <script>
 test(function() {
-  let div = document.querySelector('div');
+  let div = document.createElement('div');
   div.style.fontVariantAlternates = "Historical-Forms";
   assert_equals(
     getComputedStyle(div).fontVariantAlternates,
     "historical-forms",
     "historical-forms is parsed case-insensitively"
   );
 });
 </script>
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_autocomplete_popup.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_autocomplete_popup.js
@@ -236,17 +236,15 @@ add_task(async function test_popup_url()
                "brighttext should not be set!");
   Assert.equal(root.getAttribute("lwt-popup-darktext"),
                "",
                "darktext should not be set!");
 
   // Calculate what GrayText should be. May differ between platforms.
   let span = document.createXULElement("span");
   span.style.color = "GrayText";
-  document.documentElement.appendChild(span);
   let GRAY_TEXT = window.getComputedStyle(span).color;
-  span.remove();
 
   separator = document.getAnonymousElementByAttribute(results[1], "anonid", "separator");
   Assert.equal(window.getComputedStyle(separator).color,
                GRAY_TEXT,
                `Urlbar popup separator color should be set to ${GRAY_TEXT}`);
 });
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
@@ -49,57 +49,16 @@ add_task(async function test_sanitizatio
     window.getComputedStyle(navbar).color,
     "rgb(0, 0, 0)",
     "All CSS variables should always compute to black."
   );
 
   await extension.unload();
 });
 
-add_task(async function test_sanitization_important() {
-  // This test checks that the sanitizer cannot be fooled with !important
-  let stylesheetAttr = `href="data:text/css,*{color:red!important}" type="text/css"`;
-  let stylesheet =
-    document.createProcessingInstruction("xml-stylesheet", stylesheetAttr);
-  let load = BrowserTestUtils.waitForEvent(stylesheet, "load");
-  document.insertBefore(stylesheet, document.documentElement);
-  await load;
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      "theme": {
-        "colors": {
-          "frame": ACCENT_COLOR,
-          "tab_background_text": TEXT_COLOR,
-          "bookmark_text": "green",
-        },
-      },
-    },
-  });
-
-  await extension.startup();
-
-  let navbar = document.querySelector("#nav-bar");
-  Assert.equal(
-    window.getComputedStyle(navbar).color,
-    "rgb(255, 0, 0)",
-    "Sheet applies"
-  );
-
-  stylesheet.remove();
-
-  Assert.equal(
-    window.getComputedStyle(navbar).color,
-    "rgb(0, 128, 0)",
-    "Shouldn't be able to fool the color sanitizer with !important"
-  );
-
-  await extension.unload();
-});
-
 add_task(async function test_sanitization_transparent() {
   // This test checks whether transparent values are applied properly
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "theme": {
         "colors": {
           "frame": ACCENT_COLOR,
           "tab_background_text": TEXT_COLOR,
--- a/toolkit/modules/LightweightThemeConsumer.jsm
+++ b/toolkit/modules/LightweightThemeConsumer.jsm
@@ -214,61 +214,57 @@ LightweightThemeConsumer.prototype = {
   _setExperiment(active, experiment, properties) {
     const root = this._doc.documentElement;
     if (this._lastExperimentData) {
       const { stylesheet, usedVariables } = this._lastExperimentData;
       if (stylesheet) {
         stylesheet.remove();
       }
       if (usedVariables) {
-        for (const [variable] of usedVariables) {
+        for (const variable of usedVariables) {
           _setProperty(root, false, variable);
         }
       }
     }
-
-    this._lastExperimentData = {};
-
-    if (!active || !experiment) {
-      return;
-    }
-
-    let usedVariables = [];
-    if (properties.colors) {
-      for (const property in properties.colors) {
-        const cssVariable = experiment.colors[property];
-        const value = _sanitizeCSSColor(root.ownerDocument, properties.colors[property]);
-        usedVariables.push([cssVariable, value]);
+    if (active && experiment) {
+      this._lastExperimentData = {};
+      if (experiment.stylesheet) {
+        /* Stylesheet URLs are validated using WebExtension schemas */
+        let stylesheetAttr = `href="${experiment.stylesheet}" type="text/css"`;
+        let stylesheet = this._doc.createProcessingInstruction("xml-stylesheet",
+          stylesheetAttr);
+        this._doc.insertBefore(stylesheet, root);
+        this._lastExperimentData.stylesheet = stylesheet;
       }
-    }
-
-    if (properties.images) {
-      for (const property in properties.images) {
-        const cssVariable = experiment.images[property];
-        usedVariables.push([cssVariable, `url(${properties.images[property]})`]);
-      }
-    }
-    if (properties.properties) {
-      for (const property in properties.properties) {
-        const cssVariable = experiment.properties[property];
-        usedVariables.push([cssVariable, properties.properties[property]]);
+      let usedVariables = [];
+      if (properties.colors) {
+        for (const property in properties.colors) {
+          const cssVariable = experiment.colors[property];
+          const value = _sanitizeCSSColor(root.ownerDocument, properties.colors[property]);
+          _setProperty(root, active, cssVariable, value);
+          usedVariables.push(cssVariable);
+        }
       }
-    }
-    for (const [variable, value] of usedVariables) {
-      _setProperty(root, true, variable, value);
-    }
-    this._lastExperimentData.usedVariables = usedVariables;
-
-    if (experiment.stylesheet) {
-      /* Stylesheet URLs are validated using WebExtension schemas */
-      let stylesheetAttr = `href="${experiment.stylesheet}" type="text/css"`;
-      let stylesheet = this._doc.createProcessingInstruction("xml-stylesheet",
-        stylesheetAttr);
-      this._doc.insertBefore(stylesheet, root);
-      this._lastExperimentData.stylesheet = stylesheet;
+      if (properties.images) {
+        for (const property in properties.images) {
+          const cssVariable = experiment.images[property];
+          _setProperty(root, active, cssVariable, `url(${properties.images[property]})`);
+          usedVariables.push(cssVariable);
+        }
+      }
+      if (properties.properties) {
+        for (const property in properties.properties) {
+          const cssVariable = experiment.properties[property];
+          _setProperty(root, active, cssVariable, properties.properties[property]);
+          usedVariables.push(cssVariable);
+        }
+      }
+      this._lastExperimentData.usedVariables = usedVariables;
+    } else {
+      this._lastExperimentData = null;
     }
   },
 };
 
 function _getContentProperties(doc, active, data) {
   if (!active) {
     return {};
   }
@@ -292,68 +288,52 @@ function _setProperty(elem, active, vari
   if (active && value) {
     elem.style.setProperty(variableName, value);
   } else {
     elem.style.removeProperty(variableName);
   }
 }
 
 function _setProperties(root, active, themeData) {
-  let properties = [];
-
   for (let map of [toolkitVariableMap, ThemeVariableMap]) {
     for (let [cssVarName, definition] of map) {
       const {
         lwtProperty,
         optionalElementID,
         processColor,
         isColor = true,
       } = definition;
       let elem = optionalElementID ? root.ownerDocument.getElementById(optionalElementID)
                                    : root;
+
       let val = themeData[lwtProperty];
       if (isColor) {
         val = _sanitizeCSSColor(root.ownerDocument, val);
         if (processColor) {
           val = processColor(_parseRGBA(val), elem);
         }
       }
-      properties.push([elem, cssVarName, val]);
+      _setProperty(elem, active, cssVarName, val);
     }
   }
-
-  // Set all the properties together, since _sanitizeCSSColor flushes.
-  for (const [elem, cssVarName, val] of properties) {
-    _setProperty(elem, active, cssVarName, val);
-  }
 }
 
 function _sanitizeCSSColor(doc, cssColor) {
   if (!cssColor) {
     return null;
   }
   const HTML_NS = "http://www.w3.org/1999/xhtml";
   // style.color normalizes color values and makes invalid ones black, so a
   // simple round trip gets us a sanitized color value.
-  // Use !important so that the theme's stylesheets cannot override us.
   let div = doc.createElementNS(HTML_NS, "div");
-  div.style.setProperty("color", "black", "important");
-  div.style.setProperty("display", "none", "important");
+  div.style.color = "black";
   let span = doc.createElementNS(HTML_NS, "span");
-  span.style.setProperty("color", cssColor, "important");
-
-  // CSS variables are not allowed and should compute to black.
-  if (span.style.color.includes("var(")) {
-    span.style.color = "";
-  }
-
+  span.style.color = cssColor;
   div.appendChild(span);
-  doc.documentElement.appendChild(div);
   cssColor = doc.defaultView.getComputedStyle(span).color;
-  div.remove();
   return cssColor;
 }
 
 function _parseRGBA(aColorString) {
   if (!aColorString) {
     return null;
   }
   var rgba = aColorString.replace(/(rgba?\()|(\)$)/g, "").split(",");