Bug 605125 (5/5) - :-moz-ui-valid should not apply while typing if the element had no style when it was focused. r=bz a=jst
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 24 Nov 2010 00:56:24 +0100
changeset 58155 c2d742db6304d51cb9a476d487cca530aca85ae7
parent 58154 8761f71f04b5a0596226c9051d27412b1e1e517d
child 58156 e077f4fddd25c02be9cc08fc6222cf0f1b6cbcd7
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, jst
bugs605125
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 605125 (5/5) - :-moz-ui-valid should not apply while typing if the element had no style when it was focused. r=bz a=jst
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_bug605125-1.html
content/html/content/test/test_bug605125-2.html
content/html/content/test/test_bug605125.html
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -621,16 +621,17 @@ nsHTMLInputElement::nsHTMLInputElement(a
   : 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);
+  SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, PR_TRUE);
   mInputData.mState = new nsTextEditorState(this);
   NS_ADDREF(mInputData.mState);
   
   if (!gUploadLastDir)
     nsHTMLInputElement::InitUploadLastDir();
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
@@ -2113,21 +2114,27 @@ nsHTMLInputElement::PostHandleEvent(nsEv
       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.
+
+      // 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());
+
+      // 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
       SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
-      states |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, PR_TRUE);
+      states |= NS_EVENT_STATE_MOZ_UI_VALID | 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
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
@@ -3316,35 +3323,37 @@ nsHTMLInputElement::IntrinsicState() con
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
-
-      // NS_EVENT_STATE_MOZ_UI_VALID applies if the value has been changed.
-      // This doesn't apply to elements with value mode default.
-      ValueModeType valueMode = GetValueMode();
-      if ((mForm && mForm->HasEverTriedInvalidSubmit()) ||
-          valueMode == VALUE_MODE_DEFAULT ||
-          (valueMode == VALUE_MODE_DEFAULT_ON && GetCheckedChanged()) ||
-          ((valueMode == VALUE_MODE_VALUE ||
-            valueMode == VALUE_MODE_FILENAME) && GetValueChanged())) {
-        state |= NS_EVENT_STATE_MOZ_UI_VALID;
-      }
     } else {
       state |= NS_EVENT_STATE_INVALID;
 
       if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) &&
           ShouldShowInvalidUI()) {
         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()) {
+      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();
     nsAutoString value;
 
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -62,16 +62,17 @@
 #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 BF_CAN_SHOW_VALID_UI 13
 
 #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;
@@ -577,16 +578,41 @@ protected:
       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 && 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();
+      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
@@ -235,16 +235,18 @@ protected:
       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;
+  /** Whether we should make :-moz-ui-valid apply on the element. **/
+  PRPackedBool             mCanShowValidUI;
 
   /** 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]
@@ -292,16 +294,26 @@ protected:
      * Otherwise, show the invalid UI if the element's value has been changed.
      */
 
     return (mForm && mForm->HasEverTriedInvalidSubmit()) ||
            mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR);
   }
 
   /**
+   * 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;
+  }
+
+  /**
    * Get the mutable state of the element.
    */
   PRBool IsMutable() const;
 };
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
@@ -310,16 +322,17 @@ nsHTMLTextAreaElement::nsHTMLTextAreaEle
                                              FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mValueChanged(PR_FALSE),
     mHandlingSelect(PR_FALSE),
     mDoneAddingChildren(!aFromParser),
     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     mDisabledChanged(PR_FALSE),
     mCanShowInvalidUI(PR_TRUE),
+    mCanShowValidUI(PR_TRUE),
     mState(new nsTextEditorState(this))
 {
   AddMutationObserver(this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement,
@@ -761,21 +774,27 @@ 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();
-      // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID given that
-      // the state should not change.
+
+      // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
+      // UI while typing.
+      mCanShowValidUI = ShouldShowValidUI();
+
+      // 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;
-      states |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      mCanShowValidUI = PR_TRUE;
+      states |= NS_EVENT_STATE_MOZ_UI_VALID | 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
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
@@ -1054,30 +1073,40 @@ nsHTMLTextAreaElement::IntrinsicState() 
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
-      if ((mForm && mForm->HasEverTriedInvalidSubmit()) || mValueChanged) {
-        state |= NS_EVENT_STATE_MOZ_UI_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()) {
         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()) {
+      state |= NS_EVENT_STATE_MOZ_UI_VALID;
+    }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     nsAutoString value;
     GetValueInternal(value, PR_TRUE);
     if (value.IsEmpty()) {
       state |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -240,13 +240,14 @@ include $(topsrcdir)/config/rules.mk
 		file_bug297761.html \
 		test_bug607145.html \
 		test_bug601061.html \
 		test_bug596511.html \
 		reflect.js \
 		test_bug613113.html \
 		test_bug605124-1.html \
 		test_bug605124-2.html \
-		test_bug605125.html \
+		test_bug605125-1.html \
+		test_bug605125-2.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
rename from content/html/content/test/test_bug605125.html
rename to content/html/content/test/test_bug605125-1.html
copy from content/html/content/test/test_bug605124-2.html
copy to content/html/content/test/test_bug605125-2.html
--- a/content/html/content/test/test_bug605124-2.html
+++ b/content/html/content/test/test_bug605125-2.html
@@ -1,43 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+https://bugzilla.mozilla.org/show_bug.cgi?id=605125
 -->
 <head>
-  <title>Test for Bug 605124</title>
+  <title>Test for Bug 605125</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605125">Mozilla Bug 605125</a>
 <p id="display"></p>
 <div id="content">
-  <input required>
-  <textarea required></textarea>
+  <input>
+  <textarea></textarea>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 605124 **/
+/** Test for Bug 605125 **/
 
 function checkPseudoClass(aElement, aExpected)
 {
-  is(aElement.mozMatchesSelector(":-moz-ui-invalid"), aExpected,
-     "mozMatchesSelector(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+  is(aElement.mozMatchesSelector(":-moz-ui-valid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-valid') should return " + aExpected + " for " + aElement);
 }
 
 function checkElement(aElement)
 {
   checkPseudoClass(aElement, false);
 
-  // Focusing while :-moz-ui-invalid doesn't apply,
+  // Focusing while :-moz-ui-valid 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);
@@ -45,30 +45,49 @@ function checkElement(aElement)
   aElement.value = 'f';
   checkPseudoClass(aElement, false);
   aElement.value = '';
   checkPseudoClass(aElement, false);
 
   aElement.blur();
   checkPseudoClass(aElement, true);
 
-  // Focusing while :-moz-ui-invalid applies,
+  // Focusing while :-moz-ui-valid applies,
   // the pseudo-class should apply while typing if appropriate.
   aElement.focus();
   checkPseudoClass(aElement, true);
   // with keys
   synthesizeKey('f', {});
-  checkPseudoClass(aElement, false);
+  checkPseudoClass(aElement, true);
   synthesizeKey('VK_BACK_SPACE', {});
   checkPseudoClass(aElement, true);
   // with .value
   aElement.value = 'f';
-  checkPseudoClass(aElement, false);
+  checkPseudoClass(aElement, true);
   aElement.value = '';
   checkPseudoClass(aElement, true);
+
+  aElement.blur();
+  aElement.required = true;
+  checkPseudoClass(aElement, false);
+
+  // Focusing while :-moz-ui-invalid applies,
+  // the pseudo-class should apply while typing if appropriate.
+  aElement.focus();
+  checkPseudoClass(aElement, false);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, true);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, false);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, true);
+  aElement.value = '';
+  checkPseudoClass(aElement, false);
 }
 
 checkElement(document.getElementsByTagName('input')[0]);
 checkElement(document.getElementsByTagName('textarea')[0]);
 
 </script>
 </pre>
 </body>