<?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"?>
<?xml-stylesheet type="text/css" href="test_bug708874.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=708874
-->
<window title="Mozilla Bug 708874"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="RunTests();">
  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
  <script type="application/javascript">
  <![CDATA[

/** Test for Bug 708874 - API for locking pseudo-class state of an element **/

var DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
               .getService(Components.interfaces.inIDOMUtils);
var DOMWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                     .getInterface(Components.interfaces.nsIDOMWindowUtils);

var defaultColor = "rgb(0, 0, 0)";
var disabledColor = "rgb(40, 0, 0)";

function RunTests() {
  testLockEnabled();
  testLockDisabled();
  testVisited();
  testMultiple();
  testInvalid();
  testNotElement();
}

function testLockEnabled() {
  var button = document.getElementById("test-button");

  /* starting state is enabled */
  button.removeAttribute("disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
     "doesn't have lock at start");

  is(window.getComputedStyle(button).color, defaultColor,
     "color is default color before locking on");

  is(button.matches(":disabled"), false,
     "doesn't match selector at start");

  /* lock */
  DOMUtils.addPseudoClassLock(button, ":disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), true,
     "hasPseudoClassLock is true after locking");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style applied after adding lock");

  is(button.matches(":disabled"), true,
     "matches selector after adding lock");

  /* change state to disabled */
  button.setAttribute("disabled", "disabled");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style still applied after really disabling");

  is(button.matches(":disabled"), true,
    "matches selector after adding lock");

  /* remove lock */
  DOMUtils.removePseudoClassLock(button, ":disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
     "hasPseudoClassLock is false after removing on lock");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style still applied after removing lock");

  is(button.matches(":disabled"), true,
     "matches selector after removing lock");

  /* change state to enabled */
  button.removeAttribute("disabled");

  is(window.getComputedStyle(button).color, defaultColor,
     "back to default style after un-disabling");

  is(button.matches(":disabled"), false,
    "doesn't match selector after enabling");
}


function testLockDisabled() {
  var button = document.getElementById("test-button");

  /* starting state is disabled */
  button.setAttribute("disabled", "disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
     "doesn't have lock at start");

  is(window.getComputedStyle(button).color, disabledColor,
     "color is :disabled color before locking");

  is(button.matches(":disabled"), true,
    "matches selector before locking");

  /* lock */
  DOMUtils.addPseudoClassLock(button, ":disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), true,
     "hasPseudoClassLock is true after locking");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style still applied after adding on lock");

  is(button.matches(":disabled"), true,
   "matches selector after locking");

  /* change state to enabled */
  button.removeAttribute("disabled");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style applied after enabling");

  is(button.matches(":disabled"), true,
   "matches selector after enabling with lock on");

  /* remove lock */
  DOMUtils.removePseudoClassLock(button, ":disabled");

  is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
     "hasPseudoClassLock is false after removing on lock");

  is(window.getComputedStyle(button).color, defaultColor,
     "default style applied after removing lock");

  is(button.matches(":disabled"), false,
     "doesn't match selector after unlocking");

  /* change state to disabled */
  button.setAttribute("disabled", "disabled");

  is(window.getComputedStyle(button).color, disabledColor,
     ":disabled style applied after disabling after unlocking");

  is(button.matches(":disabled"), true,
    "matches selector again after disabling");
}

function testVisited() {
  var link = document.getElementById("test-link");
  var visitedColor = "rgb(20, 0, 0)";
  var unvisitedColor = "rgb(30, 0, 0)";

  /* lock visited */
  DOMUtils.addPseudoClassLock(link, ":visited");

  is(DOMUtils.hasPseudoClassLock(link, ":visited"), true,
     "hasPseudoClassLock is true after adding lock");

  var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
                null, "color");
  is(color, visitedColor, "color is :visited color after locking");

  /* lock unvisited */
  DOMUtils.addPseudoClassLock(link, ":link");

  is(DOMUtils.hasPseudoClassLock(link, ":link"), true,
     "hasPseudoClassLock is true after adding :link lock");

  is(DOMUtils.hasPseudoClassLock(link, ":visited"), false,
     "hasPseudoClassLock is false for :visited after adding :link lock");

  var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
                 null, "color");
  is(color, unvisitedColor, "color is :link color after locking :link");

  /* lock visited back on */
  DOMUtils.addPseudoClassLock(link, ":visited");

  is(DOMUtils.hasPseudoClassLock(link, ":visited"), true,
    "hasPseudoClassLock is true after adding :visited lock");

  is(DOMUtils.hasPseudoClassLock(link, ":link"), false,
    "hasPseudoClassLock is false for :link after adding :visited lock");

  var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
               null, "color");
  is(color, visitedColor, "color is :visited color after locking back on");
}

function testMultiple() {
  var div = document.getElementById("test-div");

  var styles = {
    ":hover": {
      property: "color",
      value: "rgb(10, 0, 0)",
      defaultValue: "rgb(0, 0, 0)"
    },
    ":active": {
      property: "font-family",
      value: "Arial",
      defaultValue: "serif"
    },
    ":focus": {
      property: "font-weight",
      value: "800",
      defaultValue: "400"
    }
  };

  for (var pseudo in styles) {
    DOMUtils.addPseudoClassLock(div, pseudo);
  }

  for (var pseudo in styles) {
    is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
       "hasPseudoClassLock is true after locking");

    var style = styles[pseudo];
    is(window.getComputedStyle(div).getPropertyValue(style.property),
       style.value, "style for pseudo-class is applied after locking");

    is(div.matches(pseudo), true,
       "matches selector after locking");
  }

  DOMUtils.clearPseudoClassLocks(div);

  for (var pseudo in styles) {
    is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
       "hasPseudoClassLock is false after clearing");

    is(window.getComputedStyle(div).getPropertyValue(style.property),
       style.defaultValue, "style is back to default after clearing");

    is(div.matches(pseudo), false,
      "doesn't match selector after unlocking");
  }
}

function testInvalid() {
  var div = document.getElementById("test-div");
  var pseudos = ["not a valid pseudo-class", ":ny-link", ":first-child"];

  for (var i = 0; i < pseudos.length; i++) {
    var pseudo = pseudos[i];

    // basically make sure these don't crash the browser.
    DOMUtils.addPseudoClassLock(div, pseudo);

    is(DOMUtils.hasPseudoClassLock(div, pseudo), false);

    DOMUtils.removePseudoClassLock(div, pseudo);
  }
}

function testNotElement() {
  for (var value of [null, undefined, {}]) {
    SimpleTest.doesThrow(() => DOMUtils.hasPseudoClassLock(value, ":hover"),
                         "hasPseudoClassLock should throw for " + value);
    SimpleTest.doesThrow(() => DOMUtils.addPseudoClassLock(value, ":hover"),
                         "addPseudoClassLock should throw for " + value);
    SimpleTest.doesThrow(() => DOMUtils.removePseudoClassLock(value, ":hover"),
                         "removePseudoClassLock should throw for " + value);
    SimpleTest.doesThrow(() => DOMUtils.clearPseudoClassLocks(value),
                         "clearPseudoClassLocks should throw for " + value);
  }
}
  ]]>
  </script>

  <body xmlns="http://www.w3.org/1999/xhtml">
    <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=708874"
       target="_blank">Mozilla Bug 708874</a>

    <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=708874">
      Mozilla Bug 708874 - API for locking pseudo-class state of an element
    </a>

    <a id="test-link" href="http://notavisitedwebsite.com">
      test link
    </a>

    <div id="test-div">
      test div
    </div>

    <button id="test-button">
      test button
    </button>
  </body>

</window>