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 usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
bugs610415
milestone2.0b9pre
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>