Bug 605124 (5/5) - :-moz-ui-invalid should not apply while typing if it was not applied on focus. r=bz a=bsmedberg
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 24 Nov 2010 00:50:53 +0100
changeset 58147 253651b1e9c2f5baf3e2bb423994f51ec502a2e6
parent 58146 5be85a60b133680a48ead8085bb9f7cca9c2e016
child 58148 a3fabf61118eb10deff968dd22cc9aa7a8399e4a
push id17175
push usermlamouri@mozilla.com
push dateWed, 24 Nov 2010 10:15:50 +0000
treeherdermozilla-central@7f5cd850578e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, bsmedberg
bugs605124
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 605124 (5/5) - :-moz-ui-invalid should not apply while typing if it was not applied on focus. r=bz a=bsmedberg
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/test_bug605124-1.html
content/html/content/test/test_bug605124-2.html
content/html/content/test/test_bug605124.html
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -1701,30 +1701,19 @@ nsHTMLFormElement::CheckValidFormSubmiss
   // Do not check form validity if there is no observer for
   // NS_INVALIDFORMSUBMIT_SUBJECT.
   if (NS_SUCCEEDED(rv) && hasObserver) {
     nsCOMPtr<nsIMutableArray> invalidElements =
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!CheckFormValidity(invalidElements.get())) {
-      nsCOMPtr<nsISupports> inst;
-      nsCOMPtr<nsIFormSubmitObserver> observer;
-      PRBool more = PR_TRUE;
-      while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
-        theEnum->GetNext(getter_AddRefs(inst));
-        observer = do_QueryInterface(inst);
-
-        if (observer) {
-          observer->NotifyInvalidSubmit(this,
-                                        static_cast<nsIArray*>(invalidElements));
-        }
-      }
-
       // For the first invalid submission, we should update element states.
+      // We have to do that _before_ calling the observers so we are sure they
+      // will not interfere (like focusing the element).
       if (!mEverTriedInvalidSubmit) {
         mEverTriedInvalidSubmit = true;
 
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           /*
            * We are going to call ContentStatesChanged assuming elements want to
            * be notified because we can't know.
@@ -1745,16 +1734,29 @@ nsHTMLFormElement::CheckValidFormSubmiss
           for (PRUint32 i = 0, length = mControls->mNotInElements.Length();
                i < length; ++i) {
             doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
                                       NS_EVENT_STATE_MOZ_UI_INVALID);
           }
         }
       }
 
+      nsCOMPtr<nsISupports> inst;
+      nsCOMPtr<nsIFormSubmitObserver> observer;
+      PRBool more = PR_TRUE;
+      while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
+        theEnum->GetNext(getter_AddRefs(inst));
+        observer = do_QueryInterface(inst);
+
+        if (observer) {
+          observer->NotifyInvalidSubmit(this,
+                                        static_cast<nsIArray*>(invalidElements));
+        }
+      }
+
       // The form is invalid. Observers have been alerted. Do not submit.
       return false;
     }
   } else {
     NS_WARNING("There is no observer for \"invalidformsubmit\". \
 One should be implemented!");
   }
 
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -110,17 +110,16 @@
 #include "nsIPopupWindowManager.h"
 #include "nsGlobalWindow.h"
 
 // input type=image
 #include "nsImageLoadingContent.h"
 #include "nsIDOMWindowInternal.h"
 
 #include "mozAutoDocUpdate.h"
-#include "nsHTMLFormElement.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 
 #include "nsTextEditRules.h"
 
 // JS headers are needed for the pattern attribute.
 #include "jsapi.h"
@@ -621,16 +620,17 @@ nsHTMLInputElement::nsHTMLInputElement(a
                                        FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mType(kInputDefaultType->value),
     mBitField(0)
 {
   SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser);
   SET_BOOLBIT(mBitField, BF_INHIBIT_RESTORATION,
       aFromParser & mozilla::dom::FROM_PARSER_FRAGMENT);
+  SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
   mInputData.mState = new nsTextEditorState(this);
   NS_ADDREF(mInputData.mState);
   
   if (!gUploadLastDir)
     nsHTMLInputElement::InitUploadLastDir();
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
@@ -2108,26 +2108,43 @@ SelectTextFieldOnFocus()
 
 nsresult
 nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (!aVisitor.mPresContext) {
     return NS_OK;
   }
 
-  if (PlaceholderApplies() &&
-      HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
-      // TODO: checking if the value is empty could be a good idea but we do not
+  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.
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI,
+                  !IsValid() && ShouldShowInvalidUI());
+      // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID given that
+      // the state should not change.
+    } else { // NS_BLUR_CONTENT
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
+      states |= NS_EVENT_STATE_MOZ_UI_INVALID;
+    }
+
+    if (PlaceholderApplies() &&
+        HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+        // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
-      (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
-       aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   // ignore the activate event fired by the "Browse..." button
   // (file input controls fire their own) (bug 500885)
   if (mType == NS_FORM_INPUT_FILE) {
     nsCOMPtr<nsIContent> maybeButton =
       do_QueryInterface(aVisitor.mEvent->originalTarget);
@@ -3305,30 +3322,19 @@ nsHTMLInputElement::IntrinsicState() con
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
-      ValueModeType valueMode = GetValueMode();
-      // If the element is suffering from VALIDITY_STATE_CUSTOM_ERROR,
-      // NS_EVENT_STATE_MOZ_UI_INVALID always apply.
-      // Otherwise, NS_EVENT_STATE_MOZ_UI_INVALID applies if the element's value
-      // has been modified.
-      // For VALUE_MODE_DEFAULT case, value being modified has no sense.
-      // NS_EVENT_STATE_MOZ_UI_INVALID always applies if the form submission has
-      // been tried while invalid.
-      if ((mForm && mForm->HasEverTriedInvalidSubmit()) ||
-          (valueMode == VALUE_MODE_DEFAULT ||
-           GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
-           (valueMode == VALUE_MODE_DEFAULT_ON && GetCheckedChanged()) ||
-           ((valueMode == VALUE_MODE_FILENAME || valueMode == VALUE_MODE_VALUE) &&
-            GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)))) {
+
+      if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) &&
+          ShouldShowInvalidUI()) {
         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
       }
     }
   }
 
   if (PlaceholderApplies() && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     // TODO: we really need a GetValue(...) const method, see bug 585097
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -40,37 +40,38 @@
 #define nsHTMLInputElement_h__
 
 #include "nsGenericHTMLElement.h"
 #include "nsImageLoadingContent.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsITextControlElement.h"
 #include "nsIPhonetic.h"
 #include "nsIDOMNSEditableElement.h"
-
 #include "nsTextEditorState.h"
 #include "nsCOMPtr.h"
 #include "nsIConstraintValidation.h"
 #include "nsDOMFile.h"
+#include "nsHTMLFormElement.h" // for ShouldShowInvalidUI()
 
 //
 // Accessors for mBitField
 //
 #define BF_DISABLED_CHANGED 0
 #define BF_HANDLING_CLICK 1
 #define BF_VALUE_CHANGED 2
 #define BF_CHECKED_CHANGED 3
 #define BF_CHECKED 4
 #define BF_HANDLING_SELECT_EVENT 5
 #define BF_SHOULD_INIT_CHECKED 6
 #define BF_PARSER_CREATING 7
 #define BF_IN_INTERNAL_ACTIVATE 8
 #define BF_CHECKED_IS_TOGGLED 9
 #define BF_INDETERMINATE 10
 #define BF_INHIBIT_RESTORATION 11
+#define BF_CAN_SHOW_INVALID_UI 12
 
 #define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \
                                         ? PR_TRUE : PR_FALSE)
 #define SET_BOOLBIT(bitfield, field, b) ((b) \
                                         ? ((bitfield) |=  (0x01 << (field))) \
                                         : ((bitfield) &= ~(0x01 << (field))))
 
 class nsDOMFileList;
@@ -528,16 +529,54 @@ protected:
 
   /**
    * Set the current default value to the value of the input element.
    * @note You should not call this method if GetValueMode() doesn't return
    * VALUE_MODE_VALUE.
    */
   nsresult SetDefaultValueAsValue();
 
+  /**
+   * Return if an invalid element should have a specific UI for being invalid
+   * (with :-moz-ui-invalid pseudo-class.
+   *
+   * @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!");
+
+    /**
+     * 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)) {
+      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 GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
+      default:
+        NS_NOTREACHED("We should not be there: there are no other modes.");
+        return false;
+    }
+  }
+
   nsCOMPtr<nsIControllers> mControllers;
 
   /**
    * The type of this input (<input type=...>) as an integer.
    * @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
    */
   PRUint8                  mType;
   /**
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -233,19 +233,22 @@ protected:
   PRPackedBool             mHandlingSelect;
   /** Whether or not we are done adding children (always PR_TRUE if not
       created by a parser */
   PRPackedBool             mDoneAddingChildren;
   /** Whether state restoration should be inhibited in DoneAddingChildren. */
   PRPackedBool             mInhibitStateRestoration;
   /** Whether our disabled state has changed from the default **/
   PRPackedBool             mDisabledChanged;
+  /** Whether we should make :-moz-ui-invalid apply on the element. **/
+  PRPackedBool             mCanShowInvalidUI;
+
   /** The state of the text editor (selection controller and the editor) **/
   nsRefPtr<nsTextEditorState> mState;
-  
+
   NS_IMETHOD SelectAll(nsPresContext* aPresContext);
   /**
    * Get the value, whether it is from the content or the frame.
    * @param aValue the value [out]
    * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
    *        value.  If this is true, linebreaks will not be inserted even if
    *        wrap=hard.
    */
@@ -266,16 +269,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 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!");
+
+    /**
+     * 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);
+  }
+
+  /**
    * Get the mutable state of the element.
    */
   PRBool IsMutable() const;
 };
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
@@ -283,16 +309,17 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 nsHTMLTextAreaElement::nsHTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                              FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mValueChanged(PR_FALSE),
     mHandlingSelect(PR_FALSE),
     mDoneAddingChildren(!aFromParser),
     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     mDisabledChanged(PR_FALSE),
+    mCanShowInvalidUI(PR_TRUE),
     mState(new nsTextEditorState(this))
 {
   AddMutationObserver(this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement,
@@ -725,25 +752,41 @@ nsHTMLTextAreaElement::PreHandleEvent(ns
 
 nsresult
 nsHTMLTextAreaElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (aVisitor.mEvent->message == NS_FORM_SELECTED) {
     mHandlingSelect = PR_FALSE;
   }
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
+  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();
+      // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID given that
+      // the state should not change.
+    } else { // NS_BLUR_CONTENT
+      mCanShowInvalidUI = PR_TRUE;
+      states |= NS_EVENT_STATE_MOZ_UI_INVALID;
+    }
+
+    if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
       // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
-      (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
-       aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   // Reset the flag for other content besides this text field
   aVisitor.mEvent->flags |= (aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH)
     ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
 
   return NS_OK;
@@ -1017,18 +1060,17 @@ nsHTMLTextAreaElement::IntrinsicState() 
       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 ((mForm && mForm->HasEverTriedInvalidSubmit()) ||
-          (mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR))) {
+      if (mCanShowInvalidUI && ShouldShowInvalidUI()) {
         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
       }
     }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     nsAutoString value;
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -238,13 +238,14 @@ include $(topsrcdir)/config/rules.mk
 		test_bug606817.html \
 		test_bug297761.html \
 		file_bug297761.html \
 		test_bug607145.html \
 		test_bug601061.html \
 		test_bug596511.html \
 		reflect.js \
 		test_bug613113.html \
-		test_bug605124.html \
+		test_bug605124-1.html \
+		test_bug605124-2.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
rename from content/html/content/test/test_bug605124.html
rename to content/html/content/test/test_bug605124-1.html
--- a/content/html/content/test/test_bug605124.html
+++ b/content/html/content/test/test_bug605124-1.html
@@ -7,17 +7,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 605124</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=605124">Mozilla Bug 605124</a>
 <p id="display"></p>
-<div id="content" style="display: none">
+<div id="content">
   <form>
     <textarea required></textarea>
     <input required>
     <button type='submit'></button>
   </form>
 
   <table>
     <form>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug605124-2.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+-->
+<head>
+  <title>Test for Bug 605124</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=605124">Mozilla Bug 605124</a>
+<p id="display"></p>
+<div id="content">
+  <input required>
+  <textarea required></textarea>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605124 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+  is(aElement.mozMatchesSelector(":-moz-ui-invalid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+}
+
+function checkElement(aElement)
+{
+  checkPseudoClass(aElement, false);
+
+  // Focusing while :-moz-ui-invalid doesn't apply,
+  // the pseudo-class should not apply while typing.
+  aElement.focus();
+  checkPseudoClass(aElement, false);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, false);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, false);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, false);
+  aElement.value = '';
+  checkPseudoClass(aElement, false);
+
+  aElement.blur();
+  checkPseudoClass(aElement, true);
+
+  // Focusing while :-moz-ui-invalid applies,
+  // the pseudo-class should apply while typing if appropriate.
+  aElement.focus();
+  checkPseudoClass(aElement, true);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, false);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, true);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, false);
+  aElement.value = '';
+  checkPseudoClass(aElement, true);
+}
+
+checkElement(document.getElementsByTagName('input')[0]);
+checkElement(document.getElementsByTagName('textarea')[0]);
+
+</script>
+</pre>
+</body>
+</html>