Bug 610415 - Make :-moz-ui-invalid and :-moz-ui-valid not applying when the form element has novalidate attribute set. r+a=sicking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Fri, 17 Dec 2010 15:26:24 -0800
changeset 59454 cd6aa4d8c7bd264c5db2b5ddbbbba6d87c2f33cc
parent 59453 5a00160adc4e94611f98e497036d6ec061089def
child 59455 a01537c0bf1987f12f67b6834b2f3b72241ca64c
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs610415
milestone2.0b9pre
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 610415 - Make :-moz-ui-invalid and :-moz-ui-valid not applying when the form element has novalidate attribute set. r+a=sicking
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLFormElement.h
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLOutputElement.cpp
content/html/content/src/nsHTMLSelectElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/test/test_bug546995.html
layout/reftests/bugs/557087-1.html
layout/reftests/bugs/557087-2.html
layout/reftests/css-ui-invalid/button/button-novalidate.html
layout/reftests/css-ui-invalid/button/reftest.list
layout/reftests/css-ui-invalid/input/input-novalidate.html
layout/reftests/css-ui-invalid/input/reftest.list
layout/reftests/css-ui-invalid/output/output-novalidate.html
layout/reftests/css-ui-invalid/output/reftest.list
layout/reftests/css-ui-invalid/select/reftest.list
layout/reftests/css-ui-invalid/select/select-novalidate.html
layout/reftests/css-ui-invalid/textarea/reftest.list
layout/reftests/css-ui-invalid/textarea/textarea-novalidate.html
layout/reftests/css-ui-valid/button/button-novalidate.html
layout/reftests/css-ui-valid/button/reftest.list
layout/reftests/css-ui-valid/input/input-novalidate.html
layout/reftests/css-ui-valid/input/reftest.list
layout/reftests/css-ui-valid/output/output-novalidate.html
layout/reftests/css-ui-valid/output/reftest.list
layout/reftests/css-ui-valid/select/reftest.list
layout/reftests/css-ui-valid/select/select-novalidate.html
layout/reftests/css-ui-valid/textarea/reftest.list
layout/reftests/css-ui-valid/textarea/textarea-novalidate.html
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -703,18 +703,27 @@ nsHTMLButtonElement::RestoreState(nsPres
 }
 
 nsEventStates
 nsHTMLButtonElement::IntrinsicState() const
 {
   nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID | NS_EVENT_STATE_MOZ_UI_VALID
-                       : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+    if (IsValid()) {
+      state |= NS_EVENT_STATE_VALID;
+      if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        state |= NS_EVENT_STATE_MOZ_UI_VALID;
+      }
+    } else {
+      state |= NS_EVENT_STATE_INVALID;
+      if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        state |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      }
+    }
   }
 
   if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
     state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
   }
 
   return state;
 }
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -371,16 +371,46 @@ nsHTMLFormElement::SetAttr(PRInt32 aName
     PRBool notifiedObservers = mNotifiedObservers;
     ForgetCurrentSubmission();
     mNotifiedObservers = notifiedObservers;
   }
   return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
                                        aNotify);
 }
 
+nsresult
+nsHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                                const nsAString* aValue, PRBool aNotify)
+{
+  if (aName == nsGkAtoms::novalidate && aNameSpaceID == kNameSpaceID_None) {
+    // Update all form elements states because they might be [no longer]
+    // affected by :-moz-ui-valid or :-moz-ui-invalid.
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+
+      for (PRUint32 i = 0, length = mControls->mElements.Length();
+           i < length; ++i) {
+        doc->ContentStatesChanged(mControls->mElements[i], nsnull,
+                                  NS_EVENT_STATE_MOZ_UI_VALID |
+                                  NS_EVENT_STATE_MOZ_UI_INVALID);
+      }
+
+      for (PRUint32 i = 0, length = mControls->mNotInElements.Length();
+           i < length; ++i) {
+        doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
+                                  NS_EVENT_STATE_MOZ_UI_VALID |
+                                  NS_EVENT_STATE_MOZ_UI_INVALID);
+      }
+    }
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, aNotify);
+}
+
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, AcceptCharset, acceptcharset)
 NS_IMPL_ACTION_ATTR(nsHTMLFormElement, Action, action)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLFormElement, Autocomplete, autocomplete,
                                 kFormDefaultAutocomplete->tag)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLFormElement, Enctype, enctype,
                                 kFormDefaultEnctype->tag)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLFormElement, Method, method,
                                 kFormDefaultMethod->tag)
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -167,16 +167,18 @@ public:
   nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                    const nsAString& aValue, PRBool aNotify)
   {
     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
   }
   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                            nsIAtom* aPrefix, const nsAString& aValue,
                            PRBool aNotify);
+  virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                                const nsAString* aValue, PRBool aNotify);
 
   /**
    * Forget all information about the current submission (and the fact that we
    * are currently submitting at all).
    */
   void ForgetCurrentSubmission();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -555,25 +555,35 @@ protected:
    * @return Whether the invalid elemnet should have a UI for being invalid.
    * @note The caller has to be sure the element is invalid before calling.
    */
   bool ShouldShowInvalidUI() const {
     NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
                              "element is valid!");
 
     /**
+     * Never show the invalid UI if the form has the novalidate attribute set.
+     *
      * Always show the invalid UI if:
      * - the form has already tried to be submitted but was invalid;
      * - the element is suffering from a custom error;
      * - the element has had its value changed
      *
      * Otherwise, show the invalid UI if the element's value has been changed.
      */
-    if ((mForm && mForm->HasEverTriedInvalidSubmit()) ||
-        GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
+    }
+
+    if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
       return true;
     }
 
     switch (GetValueMode()) {
       case VALUE_MODE_DEFAULT:
         return true;
       case VALUE_MODE_DEFAULT_ON:
         return GetCheckedChanged();
@@ -588,18 +598,23 @@ protected:
 
   /**
    * Return whether an element should show the valid UI.
    *
    * @return Whether the valid UI should be shown.
    * @note This doesn't take into account the validity of the element.
    */
   bool ShouldShowValidUI() const {
-    if (mForm && mForm->HasEverTriedInvalidSubmit()) {
-      return true;
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
     }
 
     switch (GetValueMode()) {
       case VALUE_MODE_DEFAULT:
         return true;
       case VALUE_MODE_DEFAULT_ON:
         return GetCheckedChanged();
       case VALUE_MODE_VALUE:
--- a/content/html/content/src/nsHTMLOutputElement.cpp
+++ b/content/html/content/src/nsHTMLOutputElement.cpp
@@ -38,16 +38,17 @@
 #include "nsIDOMHTMLOutputElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsFormSubmission.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsStubMutationObserver.h"
 #include "nsIConstraintValidation.h"
 #include "nsIEventStateManager.h"
 #include "mozAutoDocUpdate.h"
+#include "nsHTMLFormElement.h"
 
 
 class nsHTMLOutputElement : public nsGenericHTMLFormElement,
                             public nsIDOMHTMLOutputElement,
                             public nsStubMutationObserver,
                             public nsIConstraintValidation
 {
 public:
@@ -201,18 +202,27 @@ nsHTMLOutputElement::ParseAttribute(PRIn
 
 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_MOZ_UI_VALID
-                      : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+  if (IsValid()) {
+    states |= NS_EVENT_STATE_VALID;
+    if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+      states |= NS_EVENT_STATE_MOZ_UI_VALID;
+    }
+  } else {
+    states |= NS_EVENT_STATE_INVALID;
+    if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+      states |= NS_EVENT_STATE_MOZ_UI_INVALID;
+    }
+  }
 
   return states;
 }
 
 NS_IMETHODIMP
 nsHTMLOutputElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   return nsGenericHTMLFormElement::GetForm(aForm);
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -522,37 +522,59 @@ protected:
    * @return Whether the invalid element should have a UI for being invalid.
    * @note The caller has to be sure the element is invalid before calling.
    */
   bool ShouldShowInvalidUI() const {
     NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
                              "element is valid!");
 
     /**
+     * Never show the invalid UI if the form has the novalidate attribute set.
+     *
      * Always show the invalid UI if:
      * - the form has already tried to be submitted but was invalid;
      * - the element is suffering from a custom error;
      *
      * Otherwise, show the invalid UI if the selection has been changed.
      */
-    return mSelectionHasChanged ||
-           (mForm && mForm->HasEverTriedInvalidSubmit()) ||
-           GetValidityState(VALIDITY_STATE_CUSTOM_ERROR);
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
+    }
+
+    if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
+      return true;
+    }
+
+    return mSelectionHasChanged;
   }
 
   /**
    * Return whether an element should show the valid UI.
    *
    * @return Whether the valid UI should be shown.
    * @note This doesn't take into account the validity of the element.
    */
   bool ShouldShowValidUI() const {
-    return mSelectionHasChanged ||
-           (mForm && mForm->HasEverTriedInvalidSubmit());
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
+    }
+
+    return mSelectionHasChanged;
   }
+
   /** The options[] array */
   nsRefPtr<nsHTMLOptionCollection> mOptions;
   /** false if the parser is in the middle of adding children. */
   PRPackedBool    mIsDoneAddingChildren;
   /** true if our disabled state has changed from the default **/
   PRPackedBool    mDisabledChanged;
   /** true if child nodes are being added or removed.
    *  Used by nsSafeOptionListMutation.
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -282,35 +282,58 @@ protected:
    * @return Whether the invalid elemnet should have a UI for being invalid.
    * @note The caller has to be sure the element is invalid before calling.
    */
   bool ShouldShowInvalidUI() const {
     NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
                              "element is valid!");
 
     /**
+     * Never show the invalid UI if the form has the novalidate attribute set.
+     *
      * Always show the invalid UI if:
      * - the form has already tried to be submitted but was invalid;
      * - the element is suffering from a custom error;
      *
      * Otherwise, show the invalid UI if the element's value has been changed.
      */
 
-    return (mForm && mForm->HasEverTriedInvalidSubmit()) ||
-           mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR);
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
+    }
+
+    if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
+      return true;
+    }
+
+    return mValueChanged;
   }
 
   /**
    * Return whether an element should show the valid UI.
    *
    * @return Whether the valid UI should be shown.
    * @note This doesn't take into account the validity of the element.
    */
   bool ShouldShowValidUI() const {
-    return (mForm && mForm->HasEverTriedInvalidSubmit()) || mValueChanged;
+    if (mForm) {
+      if (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
+        return false;
+      }
+      if (mForm->HasEverTriedInvalidSubmit()) {
+        return true;
+      }
+    }
+
+    return mValueChanged;
   }
 
   /**
    * Get the mutable state of the element.
    */
   PRBool IsMutable() const;
 };
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/button/button-novalidate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('b').setCustomValidity('foo');
+                document.documentElement.className = '';">
+    <form novalidate>
+      <button id='b' class='notinvalid'></button>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-invalid/button/reftest.list
+++ b/layout/reftests/css-ui-invalid/button/reftest.list
@@ -5,8 +5,9 @@
 == button-dyn-not-disabled.html button-ref.html
 == button-button.html button-ref.html
 == button-reset.html button-ref.html
 == button-type-invalid.html button-ref.html
 == button-type-barred.html button-ref.html
 == button-disabled-fieldset-1.html button-fieldset-ref.html
 == button-disabled-fieldset-2.html button-fieldset-ref.html
 == button-fieldset-legend.html button-fieldset-legend-ref.html
+== button-novalidate.html button-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-novalidate.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- When the form has @novalidate, :-moz-ui-invalid doesn't apply. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value = '';
+                document.documentElement.className = '';">
+    <form novalidate>
+      <input id='i' class='notinvalid' required>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-invalid/input/reftest.list
+++ b/layout/reftests/css-ui-invalid/input/reftest.list
@@ -36,9 +36,10 @@
 == input-file-required-invalid-changed.html input-file-ref.html
 == input-file-required-invalid-default.html input-file-ref.html
 == input-radio-required.html success-ref.html
 == input-radio-customerror.html success-ref.html
 == input-radio-dyn-valid-1.html success-ref.html
 == input-radio-dyn-valid-2.html success-ref.html
 == input-radio-nogroup-required-valid.html success-ref.html
 == input-radio-nogroup-required-invalid.html success-ref.html
+== input-novalidate.html input-ref.html
 # input type='hidden' shouldn't show
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/output/output-novalidate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('o').setCustomValidity('foo');
+                document.documentElement.className = '';">
+    <form novalidate>
+      <output id='o' class='notinvalid'>foo</output>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-invalid/output/reftest.list
+++ b/layout/reftests/css-ui-invalid/output/reftest.list
@@ -1,2 +1,3 @@
 == output-valid.html output-ref.html
 == output-invalid.html output-ref.html
+== output-novalidate.html output-ref.html
--- a/layout/reftests/css-ui-invalid/select/reftest.list
+++ b/layout/reftests/css-ui-invalid/select/reftest.list
@@ -10,8 +10,9 @@
 == select-required-invalid-changed-2.html select-required-ref.html
 == select-required-valid.html select-required-ref.html
 == select-required-multiple-invalid.html select-required-multiple-ref.html
 == select-required-multiple-invalid-changed.html select-required-multiple-ref.html
 == select-required-multiple-valid.html select-required-multiple-ref.html
 == select-disabled-fieldset-1.html select-fieldset-ref.html
 == select-disabled-fieldset-2.html select-fieldset-ref.html
 == select-fieldset-legend.html select-fieldset-legend-ref.html
+== select-novalidate.html select-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/select/select-novalidate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('s').setCustomValidity('foo');
+                document.documentElement.className = '';">
+    <form novalidate>
+      <select id='s' class='notinvalid'></select>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-invalid/textarea/reftest.list
+++ b/layout/reftests/css-ui-invalid/textarea/reftest.list
@@ -10,8 +10,9 @@
 == textarea-maxlength-valid.html textarea-ref.html
 == textarea-maxlength-invalid.html textarea-withtext-ref.html
 == textarea-maxlength-default-value-invalid.html textarea-withtext-ref.html
 == textarea-required-valid.html textarea-withtext-ref.html
 == textarea-required-invalid.html textarea-ref.html
 == textarea-required-invalid-changed.html textarea-ref.html
 == textarea-disabled-fieldset-1.html textarea-fieldset-ref.html
 == textarea-disabled-fieldset-2.html textarea-fieldset-ref.html
+== textarea-novalidate.html textarea-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-novalidate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('t').value = '';
+                document.documentElement.className = '';">
+    <form novalidate>
+      <textarea id='t' required class='notinvalid'></textarea>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-valid/button/button-novalidate.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <form novalidate>
+      <button class='notvalid'></button>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-valid/button/reftest.list
+++ b/layout/reftests/css-ui-valid/button/reftest.list
@@ -5,8 +5,9 @@
 == button-dyn-not-disabled.html button-ref.html
 == button-button.html button-ref.html
 == button-reset.html button-ref.html
 == button-type-invalid.html button-ref.html
 == button-type-barred.html button-ref.html
 == button-disabled-fieldset-1.html button-fieldset-ref.html
 == button-disabled-fieldset-2.html button-fieldset-ref.html
 == button-fieldset-legend.html button-fieldset-legend-ref.html
+== button-novalidate.html button-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-valid/input/input-novalidate.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- When the form has @novalidate, :-moz-ui-invalid doesn't apply. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value = 'foo';
+                document.documentElement.className = '';">
+    <form novalidate>
+      <input id='i' class='notvalid' required>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-valid/input/reftest.list
+++ b/layout/reftests/css-ui-valid/input/reftest.list
@@ -35,9 +35,10 @@
 == input-file-valid-changed.html input-file-ref.html
 == input-file-valid-default.html input-file-ref.html
 == input-radio-required.html success-ref.html
 == input-radio-customerror.html success-ref.html
 == input-radio-dyn-valid-1.html success-ref.html
 == input-radio-dyn-valid-2.html success-ref.html
 == input-radio-nogroup-required-valid.html success-ref.html
 == input-radio-nogroup-required-invalid.html success-ref.html
+== input-novalidate.html input-withtext-ref.html
 # input type='hidden' shouldn't show
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-valid/output/output-novalidate.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <form novalidate>
+      <output class='notvalid'>foo</output>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-valid/output/reftest.list
+++ b/layout/reftests/css-ui-valid/output/reftest.list
@@ -1,2 +1,3 @@
 == output-valid.html output-ref.html
 == output-invalid.html output-ref.html
+== output-novalidate.html output-ref.html
--- a/layout/reftests/css-ui-valid/select/reftest.list
+++ b/layout/reftests/css-ui-valid/select/reftest.list
@@ -10,8 +10,9 @@
 == select-required-valid-changed-1.html select-required-ref.html
 == select-required-valid-changed-2.html select-required-ref.html
 == select-required-multiple-invalid.html select-required-multiple-ref.html
 == select-required-multiple-valid.html select-required-multiple-ref.html
 == select-required-multiple-valid-changed.html select-required-multiple-ref.html
 == select-disabled-fieldset-1.html select-fieldset-ref.html
 == select-disabled-fieldset-2.html select-fieldset-ref.html
 == select-fieldset-legend.html select-fieldset-legend-ref.html
+== select-novalidate.html select-required-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-valid/select/select-novalidate.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('s').selectedIndex = 0;
+                document.documentElement.className = '';">
+    <form novalidate>
+      <select id='s' class='notvalid'>
+        <option>foo</option>
+      </select>
+    </form>
+  </body>
+</html>
--- a/layout/reftests/css-ui-valid/textarea/reftest.list
+++ b/layout/reftests/css-ui-valid/textarea/reftest.list
@@ -12,8 +12,9 @@
 == textarea-maxlength-valid-changed.html textarea-ref.html
 == textarea-maxlength-invalid.html textarea-withtext-ref.html
 == textarea-required-valid.html textarea-withtext-ref.html
 == textarea-required-valid-changed.html textarea-withtext-ref.html
 == textarea-required-invalid.html textarea-ref.html
 == textarea-disabled-fieldset-1.html textarea-fieldset-ref.html
 == textarea-disabled-fieldset-2.html textarea-fieldset-ref.html
 == textarea-fieldset-legend.html textarea-fieldset-legend-ref.html
+== textarea-novalidate.html textarea-withtext-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-valid/textarea/textarea-novalidate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('t').value = 'foo';
+                document.documentElement.className = '';">
+    <form novalidate>
+      <textarea id='t' required class='notvalid'></textarea>
+    </form>
+  </body>
+</html>