<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=345624
-->
<head>
  <title>Test for Bug 345624</title>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <style>
    input, textarea, fieldset, button, select, keygen, output, object { background-color: rgb(0,0,0) !important; }
    :valid   { background-color: rgb(0,255,0) !important; }
    :invalid { background-color: rgb(255,0,0) !important; }
  </style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345624">Mozilla Bug 345624</a>
<p id="display"></p>
<div id="content" style="display: none">
  <fieldset id='f'></fieldset>
  <input id='i' oninvalid="invalidEventHandler(event);">
  <button id='b' oninvalid="invalidEventHandler(event);"></button>
  <select id='s' oninvalid="invalidEventHandler(event);"></select>
  <textarea id='t' oninvalid="invalidEventHandler(event);"></textarea>
  <output id='o' oninvalid="invalidEventHandler(event);"></output>
  <keygen id='k'></keygen>
  <object id='obj'></object>
</div>
<pre id="test">
<script type="application/javascript">

/** Test for Bug 345624 **/

var gInvalid = false;

function invalidEventHandler(aEvent)
{
  function checkInvalidEvent(aEvent)
  {
    is(aEvent.type, "invalid", "Invalid event type should be invalid");
    ok(!aEvent.bubbles, "Invalid event should not bubble");
    ok(aEvent.cancelable, "Invalid event should be cancelable");
  }

  checkInvalidEvent(aEvent);

  gInvalid = true;
}

function checkConstraintValidationAPIExist(element)
{
  ok('willValidate' in element, "willValidate is not available in the DOM");
  ok('validationMessage' in element, "validationMessage is not available in the DOM");
  ok('validity' in element, "validity is not available in the DOM");

  if ('validity' in element) {
    validity = element.validity;
    ok('valueMissing' in validity, "validity.valueMissing is not available in the DOM");
    ok('typeMismatch' in validity, "validity.typeMismatch is not available in the DOM");
    ok('badInput' in validity, "validity.badInput is not available in the DOM");
    ok('patternMismatch' in validity, "validity.patternMismatch is not available in the DOM");
    ok('tooLong' in validity, "validity.tooLong is not available in the DOM");
    ok('rangeUnderflow' in validity, "validity.rangeUnderflow is not available in the DOM");
    ok('rangeOverflow' in validity, "validity.rangeOverflow is not available in the DOM");
    ok('stepMismatch' in validity, "validity.stepMismatch is not available in the DOM");
    ok('customError' in validity, "validity.customError is not available in the DOM");
    ok('valid' in validity, "validity.valid is not available in the DOM");
  }
}

function checkConstraintValidationAPIDefaultValues(element)
{
  // Not checking willValidate because the default value depends of the element

  is(element.validationMessage, "", "validationMessage default value should be empty string");

  ok(!element.validity.valueMissing, "The element should not suffer from a constraint validation");
  ok(!element.validity.typeMismatch, "The element should not suffer from a constraint validation");
  ok(!element.validity.badInput, "The element should not suffer from a constraint validation");
  ok(!element.validity.patternMismatch, "The element should not suffer from a constraint validation");
  ok(!element.validity.tooLong, "The element should not suffer from a constraint validation");
  ok(!element.validity.rangeUnderflow, "The element should not suffer from a constraint validation");
  ok(!element.validity.rangeOverflow, "The element should not suffer from a constraint validation");
  ok(!element.validity.stepMismatch, "The element should not suffer from a constraint validation");
  ok(!element.validity.customError, "The element should not suffer from a constraint validation");
  ok(element.validity.valid, "The element should be valid by default");

  ok(element.checkValidity(), "The element should be valid by default");
}

function checkDefaultPseudoClass()
{
  is(window.getComputedStyle(document.getElementById('f'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid should apply");

  is(window.getComputedStyle(document.getElementById('o'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid should apply");

  is(window.getComputedStyle(document.getElementById('obj'), null)
       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
     "Nor :valid and :invalid should apply");

  todo_is(window.getComputedStyle(document.getElementById('k'), null)
       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
     "Nor :valid and :invalid should apply");

  is(window.getComputedStyle(document.getElementById('s'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid pseudo-class should apply");

  is(window.getComputedStyle(document.getElementById('i'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid pseudo-class should apply");

  is(window.getComputedStyle(document.getElementById('t'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid pseudo-class should apply");

  is(window.getComputedStyle(document.getElementById('b'), null)
       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
     ":valid pseudo-class should apply");
}

function checkSpecificWillValidate()
{
  // fieldset, output, object, keygen (TODO) and select elements
  ok(!document.getElementById('f').willValidate, "Fielset element should be barred from constraint validation");
  ok(!document.getElementById('obj').willValidate, "Object element should be barred from constraint validation");
  todo(!document.getElementById('k').willValidate, "Keygen element should be barred from constraint validation");
  ok(document.getElementById('o').willValidate, "Output element should not be barred from constraint validation");
  ok(document.getElementById('s').willValidate, "Select element should not be barred from constraint validation");

  // input element
  i = document.getElementById('i');
  i.type = "hidden";
  ok(!i.willValidate, "Hidden state input should be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  i.type = "reset";
  ok(!i.willValidate, "Reset button state input should be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  i.type = "button";
  ok(!i.willValidate, "Button state input should be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  i.type = "image";
  ok(i.willValidate, "Image state input should not be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid and :invalid should apply");
  i.type = "submit";
  ok(i.willValidate, "Submit state input should not be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid and :invalid should apply");
  i.type = "number";
  ok(i.willValidate, "Number state input should not be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid pseudo-class should apply");
  i.type = "";
  i.readOnly = 'true';
  ok(!i.willValidate, "Readonly input should be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  i.removeAttribute('readOnly');
  ok(i.willValidate, "Default input element should not be barred from constraint validation");
  is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid pseudo-class should apply");

  // button element
  b = document.getElementById('b');
  b.type = "reset";
  ok(!b.willValidate, "Reset state button should be barred from constraint validation");
  is(window.getComputedStyle(b, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  b.type = "button";
  ok(!b.willValidate, "Button state button should be barred from constraint validation");
  is(window.getComputedStyle(b, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  b.type = "submit";
  ok(b.willValidate, "Submit state button should not be barred from constraint validation");
  is(window.getComputedStyle(b, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid and :invalid should apply");
  b.type = "";
  ok(b.willValidate, "Default button element should not be barred from constraint validation");
  is(window.getComputedStyle(b, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid pseudo-class should apply");

  // textarea element
  t = document.getElementById('t');
  t.readOnly = true;
  ok(!t.willValidate, "Readonly textarea should be barred from constraint validation");
  is(window.getComputedStyle(t, null).getPropertyValue('background-color'),
     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
  t.removeAttribute('readOnly');
  ok(t.willValidate, "Default textarea element should not be barred from constraint validation");
  is(window.getComputedStyle(t, null).getPropertyValue('background-color'),
     "rgb(0, 255, 0)", ":valid pseudo-class should apply");

  // TODO: PROGRESS
  // TODO: METER
}

function checkCommonWillValidate(element)
{
  // Not checking the default value because it has been checked previously.

  // Not checking output elements because they can't be disabled.
  if (element.tagName != 'OUTPUT') {
    element.disabled = true;
    ok(!element.willValidate, "Disabled element should be barred from constraint validation");

    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
       "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");

    element.removeAttribute('disabled');
  }

  // TODO: If an element has a datalist element ancestor, it is barred from constraint validation.
}

function checkCustomError(element, isBarred)
{
  element.setCustomValidity("message");
  if (!isBarred) {
    is(element.validationMessage, "message",
       "When the element has a custom validity message, validation message should return it");
  } else {
    is(element.validationMessage, "",
       "An element barred from constraint validation can't have a validation message");
  }
  ok(element.validity.customError, "The element should suffer from a custom error");
  ok(!element.validity.valid, "The element should not be valid with a custom error");

  if (element.tagName == "FIELDSET") {
    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
       isBarred ? "rgb(0, 255, 0)" : "rgb(255, 0, 0)",
       ":invalid pseudo-classs should apply" + element.tagName);
  }
  else {
    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
       isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
       ":invalid pseudo-classs should apply" + element.tagName);
  }

  element.setCustomValidity("");
  is(element.validationMessage, "", "The element should not have a validation message when reseted");
  ok(!element.validity.customError, "The element should not suffer anymore from a custom error");
  ok(element.validity.valid, "The element should now be valid");

  is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
     isBarred && element.tagName != "FIELDSET" ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
     ":valid pseudo-classs should apply");
}

function checkCheckValidity(element)
{
  element.setCustomValidity("message");
  ok(!element.checkValidity(), "checkValidity() should return false when the element is not valid");

  ok(gInvalid, "Invalid event should have been handled");

  gInvalid = false;
  element.setCustomValidity("");

  ok(element.checkValidity(), "Element should be valid");
  ok(!gInvalid, "Invalid event should not have been handled");
}

function checkValidityStateObjectAliveWithoutElement(element)
{
  // We are creating a temporary element and getting it's ValidityState object.
  // Then, we make sure it is removed by the garbage collector and we check the
  // ValidityState default values (it should not crash).

  var v = document.createElement(element).validity;
  SpecialPowers.gc();

  ok(!v.valueMissing,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.typeMismatch,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.badInput,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.patternMismatch,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.tooLong,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.rangeUnderflow,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.rangeOverflow,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.stepMismatch,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(!v.customError,
    "When the element is not alive, it shouldn't suffer from constraint validation");
  ok(v.valid, "When the element is not alive, it should be valid");
}

checkConstraintValidationAPIExist(document.getElementById('f'));
checkConstraintValidationAPIExist(document.getElementById('i'));
checkConstraintValidationAPIExist(document.getElementById('b'));
checkConstraintValidationAPIExist(document.getElementById('s'));
checkConstraintValidationAPIExist(document.getElementById('t'));
checkConstraintValidationAPIExist(document.getElementById('k'));
checkConstraintValidationAPIExist(document.getElementById('o'));
checkConstraintValidationAPIExist(document.getElementById('obj'));

checkConstraintValidationAPIDefaultValues(document.getElementById('f'));
checkConstraintValidationAPIDefaultValues(document.getElementById('i'));
checkConstraintValidationAPIDefaultValues(document.getElementById('b'));
checkConstraintValidationAPIDefaultValues(document.getElementById('s'));
checkConstraintValidationAPIDefaultValues(document.getElementById('t'));
checkConstraintValidationAPIDefaultValues(document.getElementById('k'));
checkConstraintValidationAPIDefaultValues(document.getElementById('o'));
checkConstraintValidationAPIDefaultValues(document.getElementById('obj'));

checkDefaultPseudoClass();

checkSpecificWillValidate();

// Not checking button, fieldset, object and keygen
// because they are always barred from constraint validation.
checkCommonWillValidate(document.getElementById('i'));
checkCommonWillValidate(document.getElementById('s'));
checkCommonWillValidate(document.getElementById('t'));
checkCommonWillValidate(document.getElementById('o'));

/* TODO: add "keygen" element */
checkCustomError(document.getElementById('i'), false);
checkCustomError(document.getElementById('s'), false);
checkCustomError(document.getElementById('t'), false);
checkCustomError(document.getElementById('o'), false);
checkCustomError(document.getElementById('b'), false);
checkCustomError(document.getElementById('f'), true);
checkCustomError(document.getElementById('obj'), true);

// Not checking button, fieldset, object and keygen
// because they are always barred from constraint validation.
checkCheckValidity(document.getElementById('i'));
checkCheckValidity(document.getElementById('s'));
checkCheckValidity(document.getElementById('t'));
checkCheckValidity(document.getElementById('o'));

/* TODO: add "keygen" element */
checkValidityStateObjectAliveWithoutElement("fieldset");
checkValidityStateObjectAliveWithoutElement("input");
checkValidityStateObjectAliveWithoutElement("button");
checkValidityStateObjectAliveWithoutElement("select");
checkValidityStateObjectAliveWithoutElement("textarea");
checkValidityStateObjectAliveWithoutElement("output");
checkValidityStateObjectAliveWithoutElement("object");

</script>
</pre>
</body>
</html>