Bug 622558 - When .setCustomValidity() is used, :-moz-ui-invalid should always apply. r+a=sicking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 20 Jan 2011 12:05:29 +0100
changeset 60936 1908930ddfffab289eaf828c8149bb05c63bb277
parent 60935 6c8c7ff7505a23d4560b17ed7c023f0bcbecc8d3
child 60937 ab793a47ac07e55344c9edc4b3e1d567408145a0
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)
bugs622558
milestone2.0b10pre
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 622558 - When .setCustomValidity() is used, :-moz-ui-invalid should always apply. r+a=sicking
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLSelectElement.cpp
content/html/content/src/nsHTMLSelectElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/test_bug622558.html
layout/reftests/css-ui-invalid/select/reftest.list
layout/reftests/css-ui-invalid/select/select-novalidate.html
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -3305,32 +3305,33 @@ nsHTMLInputElement::IntrinsicState() con
   }
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
 
-      if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) &&
-          ShouldShowInvalidUI()) {
+      if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
+          (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) &&
+           ShouldShowValidityUI())) {
         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
       }
     }
 
     // :-moz-ui-valid applies if all of the following conditions are true:
     // 1. The element is not focused, or had either :-moz-ui-valid or
     //    :-moz-ui-invalid applying before it was focused ;
     // 2. The element is either valid or isn't allowed to have
     //    :-moz-ui-invalid applying ;
     // 3. The rules to have :-moz-ui-valid applying are fulfilled
-    //    (see ShouldShowValidUI()).
-    if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI) &&
-        (IsValid() || !GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI)) &&
-        ShouldShowValidUI()) {
+    //    (see ShouldShowValidityUI()).
+    if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI) && ShouldShowValidityUI() &&
+        (IsValid() || (!state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
+                       !GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI)))) {
       state |= NS_EVENT_STATE_MOZ_UI_VALID;
     }
   }
 
   if (PlaceholderApplies() && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     // TODO: we really need a GetValue(...) const method, see bug 585097
     nsTextEditorState* edState = GetEditorState();
@@ -4580,19 +4581,19 @@ nsHTMLInputElement::GetFilterFromAccept(
 
 void
 nsHTMLInputElement::UpdateValidityUIBits(bool aIsFocused)
 {
   if (aIsFocused) {
     // If the invalid UI is shown, we should show it while focusing (and
     // update). Otherwise, we should not.
     SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI,
-                !IsValid() && ShouldShowInvalidUI());
+                !IsValid() && ShouldShowValidityUI());
 
     // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
     // UI while typing.
-    SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, ShouldShowValidUI());
+    SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, ShouldShowValidityUI());
   } else {
     SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
     SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, PR_TRUE);
   }
 }
 
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -555,79 +555,38 @@ protected:
    * Returns whether the value has been changed since the element has been created.
    * @return Whether the value has been changed since the element has been created.
    */
   PRBool GetValueChanged() const {
     return GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
   }
 
   /**
-   * Return if an invalid element should have a specific UI for being invalid
-   * (with :-moz-ui-invalid pseudo-class.
+   * Return if an element should have a specific validity UI
+   * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
-   * @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.
+   * @return Whether the elemnet should have a validity UI.
    */
-  bool ShouldShowInvalidUI() const {
-    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
-                             "element is valid!");
-
+  bool ShouldShowValidityUI() const {
     /**
-     * Never show the invalid UI if the form has the novalidate attribute set.
+     * Never show the validity UI if the form has the novalidate attribute set.
+     * Always show the validity UI if the form has already tried to be submitted
+     * but was invalid.
      *
-     * 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.
+     * Otherwise, show the validity UI if the element's value has been changed.
      */
     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();
-      case VALUE_MODE_VALUE:
-      case VALUE_MODE_FILENAME:
-        return GetValueChanged();
-      default:
-        NS_NOTREACHED("We should not be there: there are no other modes.");
-        return false;
-    }
-  }
-
-  /**
-   * 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) {
-      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:
       case VALUE_MODE_FILENAME:
         return GetValueChanged();
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1567,21 +1567,21 @@ nsHTMLSelectElement::PreHandleEvent(nsEv
 }
 
 nsresult
 nsHTMLSelectElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
     // If the invalid UI is shown, we should show it while focused and
     // update the invalid/valid UI.
-    mCanShowInvalidUI = !IsValid() && ShouldShowInvalidUI();
+    mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
 
     // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
     // UI while focused.
-    mCanShowValidUI = ShouldShowValidUI();
+    mCanShowValidUI = ShouldShowValidityUI();
 
     // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
     // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
   } else if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     mCanShowInvalidUI = PR_TRUE;
     mCanShowValidUI = PR_TRUE;
 
     nsIDocument* doc = GetCurrentDoc();
@@ -1601,31 +1601,32 @@ nsHTMLSelectElement::IntrinsicState() co
   nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
 
-      if (mCanShowInvalidUI && ShouldShowInvalidUI()) {
+      if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
+          (mCanShowInvalidUI && ShouldShowValidityUI())) {
         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
       }
     }
 
     // :-moz-ui-valid applies if all the following are true:
     // 1. The element is not focused, or had either :-moz-ui-valid or
     //    :-moz-ui-invalid applying before it was focused ;
     // 2. The element is either valid or isn't allowed to have
     //    :-moz-ui-invalid applying ;
     // 3. The rules to have :-moz-ui-valid applying are fulfilled
-    //    (see ShouldShowValidUI()).
-    if (mCanShowValidUI &&
-        (IsValid() || !mCanShowInvalidUI) &&
-        ShouldShowValidUI()) {
+    //    (see ShouldShowValidityUI()).
+    if (mCanShowValidUI && ShouldShowValidityUI() &&
+        (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
+                       !mCanShowInvalidUI))) {
       state |= NS_EVENT_STATE_MOZ_UI_VALID;
     }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -511,67 +511,38 @@ protected:
     return PR_TRUE;
   }
 
   nsresult SetSelectedIndexInternal(PRInt32 aIndex, PRBool aNotify);
 
   void SetSelectionChanged(PRBool aValue, PRBool aNotify);
 
   /**
-   * Return whether an invalid element should have a specific UI for being invalid
-   * (with :-moz-ui-invalid pseudo-class).
+   * Return whether an element should have a validity UI.
+   * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
-   * @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.
+   * @return Whether the element should have a validity UI.
    */
-  bool ShouldShowInvalidUI() const {
-    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
-                             "element is valid!");
-
+  bool ShouldShowValidityUI() const {
     /**
-     * Never show the invalid UI if the form has the novalidate attribute set.
+     * Never show the validity UI if the form has the novalidate attribute set.
+     * Always show the validity UI if the form has already tried to be submitted
+     * but was invalid.
      *
-     * 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.
+     * Otherwise, show the validity UI if the selection has been changed.
      */
     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 {
-    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 **/
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -271,68 +271,39 @@ protected:
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
                                 const nsAString* aValue, PRBool aNotify);
 
   /**
-   * Return if an invalid element should have a specific UI for being invalid
-   * (with :-moz-ui-invalid pseudo-class.
+   * Return if an element should have a specific validity UI
+   * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
-   * @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.
+   * @return Whether the elemnet should have a validity UI.
    */
-  bool ShouldShowInvalidUI() const {
-    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
-                             "element is valid!");
-
+  bool ShouldShowValidityUI() const {
     /**
-     * Never show the invalid UI if the form has the novalidate attribute set.
+     * Never show the validity UI if the form has the novalidate attribute set.
+     * Always show the validity UI if the form has already tried to be submitted
+     * but was invalid.
      *
-     * 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.
+     * Otherwise, show the validity UI if the element's value has been changed.
      */
 
     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 {
-    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;
 };
@@ -796,21 +767,21 @@ nsHTMLTextAreaElement::PostHandleEvent(n
 
   if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
       aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     nsEventStates states;
 
     if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
       // If the invalid UI is shown, we should show it while focusing (and
       // update). Otherwise, we should not.
-      mCanShowInvalidUI = !IsValid() && ShouldShowInvalidUI();
+      mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
 
       // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
       // UI while typing.
-      mCanShowValidUI = ShouldShowValidUI();
+      mCanShowValidUI = ShouldShowValidityUI();
 
       // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
       // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
     } else { // NS_BLUR_CONTENT
       mCanShowInvalidUI = PR_TRUE;
       mCanShowValidUI = PR_TRUE;
       states |= NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
@@ -1100,34 +1071,32 @@ nsHTMLTextAreaElement::IntrinsicState() 
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
       // NS_EVENT_STATE_MOZ_UI_INVALID always apply if the element suffers from
       // VALIDITY_STATE_CUSTOM_ERROR.
-      // Otherwise, it applies if the value has been modified.
-      // NS_EVENT_STATE_MOZ_UI_INVALID always applies if the form submission has
-      // been tried while invalid.
-      if (mCanShowInvalidUI && ShouldShowInvalidUI()) {
+      if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
+          mCanShowInvalidUI && ShouldShowValidityUI()) {
         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
       }
     }
 
     // :-moz-ui-valid applies if all the following are true:
     // 1. The element is not focused, or had either :-moz-ui-valid or
     //    :-moz-ui-invalid applying before it was focused ;
     // 2. The element is either valid or isn't allowed to have
     //    :-moz-ui-invalid applying ;
     // 3. The rules to have :-moz-ui-valid applying are fulfilled
-    //    (see ShouldShowValidUI()).
-    if (mCanShowValidUI &&
-        (IsValid() || !mCanShowInvalidUI) &&
-        ShouldShowValidUI()) {
+    //    (see ShouldShowValidityUI()).
+    if (mCanShowValidUI && ShouldShowValidityUI() &&
+        (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
+                       !mCanShowInvalidUI))) {
       state |= NS_EVENT_STATE_MOZ_UI_VALID;
     }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     nsAutoString value;
     GetValueInternal(value, PR_TRUE);
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -247,12 +247,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug613722.html \
 		test_bug613979.html \
 		test_bug615833.html \
 		test_bug601030.html \
 		test_bug610687.html \
 		test_bug618948.html \
 		test_bug623291.html \
 		test_bug619278.html \
+		test_bug622558.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug622558.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=622558
+-->
+<head>
+  <title>Test for Bug 622558</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=622558">Mozilla Bug 622558</a>
+<p id="display"></p>
+<div id="content">
+  <form>
+    <input>
+    <textarea></textarea>
+    <select><option>foo</option></select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 622558 **/
+
+function checkSelectors(aElement)
+{
+  ok(aElement.mozMatchesSelector(":-moz-ui-invalid"),
+     ":-moz-ui-invalid should match for " + aElement);
+  ok(!aElement.mozMatchesSelector(":-moz-ui-valid"),
+     ":-moz-ui-valid should not match for " + aElement);
+}
+
+var input = document.getElementsByTagName('input')[0];
+var textarea = document.getElementsByTagName('textarea')[0];
+var select = document.getElementsByTagName('select')[0];
+
+select.addEventListener("focus", function() {
+  select.removeEventListener("focus", arguments.callee, false);
+
+  SimpleTest.executeSoon(function() {
+    select.setCustomValidity('foo');
+
+    SimpleTest.executeSoon(function() {
+      checkSelectors(select);
+      SimpleTest.finish();
+    });
+  });
+}, false);
+
+textarea.addEventListener("focus", function() {
+  textarea.removeEventListener("focus", arguments.callee, false);
+
+  SimpleTest.executeSoon(function() {
+    textarea.setCustomValidity('foo');
+
+    SimpleTest.executeSoon(function() {
+      checkSelectors(textarea);
+      select.focus();
+    });
+  });
+}, false);
+
+input.addEventListener("focus", function() {
+  input.removeEventListener("focus", arguments.callee, false);
+
+  SimpleTest.executeSoon(function() {
+    input.setCustomValidity('foo');
+
+    SimpleTest.executeSoon(function() {
+      checkSelectors(input);
+      textarea.focus();
+    });
+  });
+}, false);
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  input.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/reftests/css-ui-invalid/select/reftest.list
+++ b/layout/reftests/css-ui-invalid/select/reftest.list
@@ -10,9 +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
+== select-novalidate.html select-required-ref.html
--- a/layout/reftests/css-ui-invalid/select/select-novalidate.html
+++ b/layout/reftests/css-ui-invalid/select/select-novalidate.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
-<html class="reftest-wait">
+<html>
   <link rel='stylesheet' type='text/css' href='style.css'>
-  <body onload="document.getElementById('s').setCustomValidity('foo');
-                document.documentElement.className = '';">
+  <body>
     <form novalidate>
-      <select id='s' class='notinvalid'></select>
+      <select required id='s' class='notinvalid'>
+        <option selected value="">foo</option>
+      </select>
     </form>
   </body>
 </html>