Bug 604673 - <output> should be subject for constraint validation. r=smaug ui-r=limi a=bsmedberg
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 17 Nov 2010 17:30:03 +0100
changeset 57659 2eb1331a2bc42c081fde0511796195244c85e724
parent 57658 1b4d00d79ae61cca9f4aa94c35a36356d325a924
child 57660 6cfd43067a07e1e7d818c02b6a7b1ea62e8ace02
push id17028
push usermlamouri@mozilla.com
push dateWed, 17 Nov 2010 20:47:23 +0000
treeherdermozilla-central@2eb1331a2bc4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, limi, bsmedberg
bugs604673
milestone2.0b8pre
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
Bug 604673 - <output> should be subject for constraint validation. r=smaug ui-r=limi a=bsmedberg
content/html/content/src/nsHTMLOutputElement.cpp
content/html/content/test/test_bug345624-1.html
layout/reftests/css-invalid/output/output-invalid.html
layout/reftests/css-invalid/output/output-ref.html
layout/reftests/css-invalid/output/output-valid.html
layout/reftests/css-invalid/output/reftest.list
layout/reftests/css-invalid/output/style.css
layout/reftests/css-invalid/reftest.list
layout/reftests/css-valid/output/output-invalid.html
layout/reftests/css-valid/output/output-ref.html
layout/reftests/css-valid/output/output-valid.html
layout/reftests/css-valid/output/reftest.list
layout/reftests/css-valid/output/style.css
layout/reftests/css-valid/reftest.list
layout/style/forms.css
--- a/content/html/content/src/nsHTMLOutputElement.cpp
+++ b/content/html/content/src/nsHTMLOutputElement.cpp
@@ -36,16 +36,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMHTMLOutputElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsFormSubmission.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsStubMutationObserver.h"
 #include "nsIConstraintValidation.h"
+#include "nsIEventStateManager.h"
+#include "mozAutoDocUpdate.h"
 
 
 class nsHTMLOutputElement : public nsGenericHTMLFormElement,
                             public nsIDOMHTMLOutputElement,
                             public nsStubMutationObserver,
                             public nsIConstraintValidation
 {
 public:
@@ -76,16 +78,18 @@ public:
 
   virtual bool IsDisabled() const { return PR_FALSE; }
 
   nsresult Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const;
 
   PRBool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
                         const nsAString& aValue, nsAttrValue& aResult);
 
+  nsEventStates IntrinsicState() const;
+
   // This function is called when a callback function from nsIMutationObserver
   // has to be used to update the defaultValue attribute.
   void DescendantsChanged();
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
@@ -110,18 +114,16 @@ protected:
 NS_IMPL_NS_NEW_HTML_ELEMENT(Output)
 
 
 nsHTMLOutputElement::nsHTMLOutputElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLFormElement(aNodeInfo)
   , mValueModeFlag(eModeDefault)
 {
   AddMutationObserver(this);
-  // <output> is always barred from constraint validation.
-  SetBarredFromConstraintValidation(PR_TRUE);
 }
 
 nsHTMLOutputElement::~nsHTMLOutputElement()
 {
   if (mTokenList) {
     mTokenList->DropReference();
   }
 }
@@ -142,17 +144,32 @@ NS_INTERFACE_TABLE_HEAD(nsHTMLOutputElem
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLOutputElement)
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLOutputElement)
 
 
 NS_IMPL_STRING_ATTR(nsHTMLOutputElement, Name, name)
 
 // nsIConstraintValidation
-NS_IMPL_NSICONSTRAINTVALIDATION(nsHTMLOutputElement)
+NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLOutputElement)
+
+NS_IMETHODIMP
+nsHTMLOutputElement::SetCustomValidity(const nsAString& aError)
+{
+  nsIConstraintValidation::SetCustomValidity(aError);
+
+  nsIDocument* doc = GetCurrentDoc();
+  if (doc) {
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+    doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
+                                            NS_EVENT_STATE_VALID);
+  }
+
+  return NS_OK;
+}
 
 NS_IMETHODIMP
 nsHTMLOutputElement::Reset()
 {
   mValueModeFlag = eModeDefault;
   nsresult rv = nsContentUtils::SetNodeTextContent(this, mDefaultValue,
                                                    PR_TRUE);
   return rv;
@@ -175,16 +192,28 @@ nsHTMLOutputElement::ParseAttribute(PRIn
       return PR_TRUE;
     }
   }
 
   return nsGenericHTMLFormElement::ParseAttribute(aNamespaceID, aAttribute,
                                                   aValue, aResult);
 }
 
+nsEventStates
+nsHTMLOutputElement::IntrinsicState() const
+{
+  nsEventStates states = nsGenericHTMLFormElement::IntrinsicState();
+
+  // We don't have to call IsCandidateForConstraintValidation()
+  // because <output> can't be barred from constraint validation.
+  states |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+
+  return states;
+}
+
 NS_IMETHODIMP
 nsHTMLOutputElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   return nsGenericHTMLFormElement::GetForm(aForm);
 }
 
 NS_IMETHODIMP
 nsHTMLOutputElement::GetType(nsAString& aType)
--- a/content/html/content/test/test_bug345624-1.html
+++ b/content/html/content/test/test_bug345624-1.html
@@ -18,18 +18,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <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>
-  <output id='o'></output>
   <object id='obj'></object>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 345624 **/
 
 var gInvalid = false;
@@ -89,18 +89,18 @@ function checkConstraintValidationAPIDef
 
 function checkDefaultPseudoClass()
 {
   is(window.getComputedStyle(document.getElementById('f'), null)
        .getPropertyValue('background-color'), "rgb(0, 0, 0)",
      "Nor :valid and :invalid should apply");
 
   is(window.getComputedStyle(document.getElementById('o'), null)
-       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
-     "Nor :valid and :invalid should apply");
+       .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");
@@ -121,19 +121,19 @@ function checkDefaultPseudoClass()
        .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('o').willValidate, "Output 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");
@@ -184,23 +184,26 @@ function checkSpecificWillValidate()
   // TODO: PROGRESS
   // TODO: METER
 }
 
 function checkCommonWillValidate(element)
 {
   // Not checking the default value because it has been checked previously.
 
-  element.disabled = true;
-  ok(!element.willValidate, "Disabled element should be barred from constraint validation");
+  // 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");
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
 
-  element.removeAttribute('disabled');
+    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) {
@@ -288,38 +291,40 @@ checkConstraintValidationAPIDefaultValue
 checkConstraintValidationAPIDefaultValues(document.getElementById('k'));
 checkConstraintValidationAPIDefaultValues(document.getElementById('o'));
 checkConstraintValidationAPIDefaultValues(document.getElementById('obj'));
 
 checkDefaultPseudoClass();
 
 checkSpecificWillValidate();
 
-// Not checking fieldset, output, object and keygen
+// Not checking fieldset, object and keygen
 // because they are always barred from constraint validation.
 checkCommonWillValidate(document.getElementById('i'));
 checkCommonWillValidate(document.getElementById('b'));
 checkCommonWillValidate(document.getElementById('s'));
 checkCommonWillValidate(document.getElementById('t'));
+checkCommonWillValidate(document.getElementById('o'));
 
 /* TODO: add "keygen" element */
 checkCustomError(document.getElementById('i'), false);
 checkCustomError(document.getElementById('b'), false);
 checkCustomError(document.getElementById('s'), false);
 checkCustomError(document.getElementById('t'), false);
+checkCustomError(document.getElementById('o'), false);
 checkCustomError(document.getElementById('f'), true);
-checkCustomError(document.getElementById('o'), true);
 checkCustomError(document.getElementById('obj'), true);
 
-// Not checking fieldset, output, object and keygen
+// Not checking fieldset, object and keygen
 // because they are always barred from constraint validation.
 checkCheckValidity(document.getElementById('i'));
 checkCheckValidity(document.getElementById('b'));
 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");
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/output/output-invalid.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if output has a custom error, it should not be affected by :valid
+             pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('b').setCustomValidity('foo'); document.documentElement.className='';">
+    <output class='invalid' id='b'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/output/output-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <output class='ref'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/output/output-valid.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if output has no custom error and is not barred from constraint
+             validation, it should be affected by :valid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <output class='notinvalid'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/output/reftest.list
@@ -0,0 +1,2 @@
+== output-valid.html output-ref.html
+== output-invalid.html output-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/output/style.css
@@ -0,0 +1,22 @@
+/* Override default style */
+output {
+  color: black;
+}
+
+output.notinvalid {
+  color: green;
+}
+output.notinvalid:invalid {
+  color: red;
+}
+
+output.invalid {
+  color: red;
+}
+output.invalid:invalid {
+  color: green;
+}
+
+output.ref {
+  color: green;
+}
--- a/layout/reftests/css-invalid/reftest.list
+++ b/layout/reftests/css-invalid/reftest.list
@@ -1,11 +1,12 @@
 # :invalid should not apply on fieldset (always barred from constraint validation)
 include fieldset/reftest.list
 
 # :invalid should apply on the following elements
 include button/reftest.list
 include input/reftest.list
 include select/reftest.list
 include textarea/reftest.list
+include output/reftest.list
 
 # default :invalid style
 include default-style/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-valid/output/output-invalid.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if output has a custom error, it should not be affected by :valid
+             pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('b').setCustomValidity('foo'); document.documentElement.className='';">
+    <output class='notvalid' id='b'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-valid/output/output-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <output class='ref'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-valid/output/output-valid.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if output has no custom error and is not barred from constraint
+             validation, it should be affected by :valid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <output class='valid'>foo</output>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-valid/output/reftest.list
@@ -0,0 +1,2 @@
+== output-valid.html output-ref.html
+== output-invalid.html output-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-valid/output/style.css
@@ -0,0 +1,22 @@
+/* Override default style */
+output {
+  color: black;
+}
+
+output.notvalid {
+  color: green;
+}
+output.notvalid:valid {
+  color: red;
+}
+
+output.valid {
+  color: red;
+}
+output.valid:valid {
+  color: green;
+}
+
+output.ref {
+  color: green;
+}
--- a/layout/reftests/css-valid/reftest.list
+++ b/layout/reftests/css-valid/reftest.list
@@ -1,8 +1,11 @@
 # :valid should not apply on fieldset (always barred from constraint validation)
 include fieldset/reftest.list
 
 # :valid should apply on the following elements
 include button/reftest.list
 include input/reftest.list
 include select/reftest.list
 include textarea/reftest.list
+include output/reftest.list
+include output/reftest.list
+include output/reftest.list
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -619,24 +619,25 @@ optgroup:before {
   */
 input[type="file"] > input[type="text"] {
   direction: ltr !important;
   text-align: inherit;
 }
 
 /**
  * Set default style for invalid elements.
- * <output> is the only element which may not follow this rule (but color: red).
- * However it is barred from constraint validation per the specifications so, at
- * least for now, it will not be affected by :invalid.
  */
-:invalid {
+:not(output):invalid {
   box-shadow: 0 0 1.5px 1px red;
 }
 
+output:invalid {
+  color: red;
+}
+
 @media print {
   input, textarea, select, button {
     -moz-user-input: none !important;
   }
 
   input[type="file"] { height: 2em; }
 }