Bug 605124 (3/5) - :-moz-ui-invalid shouldn't apply if the element hasn't been modified. r=bz a=bsmedberg
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 24 Nov 2010 00:49:50 +0100
changeset 58145 15b5a12e17ac6e2dd3cf4829aa381b44be160ea2
parent 58144 f88a334e605e9aab7eda0d14d69788d4ac3c5c15
child 58146 5be85a60b133680a48ead8085bb9f7cca9c2e016
push id17175
push usermlamouri@mozilla.com
push dateWed, 24 Nov 2010 10:15:50 +0000
treeherderautoland@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 (3/5) - :-moz-ui-invalid shouldn't apply if the element hasn't been modified. r=bz a=bsmedberg
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
layout/reftests/css-ui-invalid/input/input-checkbox-required-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-checkbox-required-invalid-default.html
layout/reftests/css-ui-invalid/input/input-dyn-not-readonly-changed.html
layout/reftests/css-ui-invalid/input/input-dyn-not-readonly-not-changed.html
layout/reftests/css-ui-invalid/input/input-dyn-not-readonly.html
layout/reftests/css-ui-invalid/input/input-email-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-email-invalid-default.html
layout/reftests/css-ui-invalid/input/input-email-invalid.html
layout/reftests/css-ui-invalid/input/input-fieldset-legend.html
layout/reftests/css-ui-invalid/input/input-file-ref.html
layout/reftests/css-ui-invalid/input/input-file-required-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-file-required-invalid-default.html
layout/reftests/css-ui-invalid/input/input-maxlength-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-maxlength-invalid-default.html
layout/reftests/css-ui-invalid/input/input-maxlength-invalid.html
layout/reftests/css-ui-invalid/input/input-pattern-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-pattern-invalid-default.html
layout/reftests/css-ui-invalid/input/input-pattern-invalid.html
layout/reftests/css-ui-invalid/input/input-radio-required-invalid-changed-2.html
layout/reftests/css-ui-invalid/input/input-radio-required-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-radio-required-invalid-default-2.html
layout/reftests/css-ui-invalid/input/input-radio-required-invalid-default.html
layout/reftests/css-ui-invalid/input/input-required-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-required-invalid-default.html
layout/reftests/css-ui-invalid/input/input-required-invalid.html
layout/reftests/css-ui-invalid/input/input-url-invalid-changed.html
layout/reftests/css-ui-invalid/input/input-url-invalid-default.html
layout/reftests/css-ui-invalid/input/input-url-invalid.html
layout/reftests/css-ui-invalid/input/reftest.list
layout/reftests/css-ui-invalid/input/style.css
layout/reftests/css-ui-invalid/input/success-ref.html
layout/reftests/css-ui-invalid/textarea/reftest.list
layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly-changed.html
layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly-not-changed.html
layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly.html
layout/reftests/css-ui-invalid/textarea/textarea-fieldset-legend.html
layout/reftests/css-ui-invalid/textarea/textarea-maxlength-default-value-invalid.html
layout/reftests/css-ui-invalid/textarea/textarea-required-invalid-changed.html
layout/reftests/css-ui-invalid/textarea/textarea-required-invalid.html
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1433,22 +1433,34 @@ nsHTMLInputElement::SetValueInternal(con
   return nsGenericHTMLFormElement::SetAttr(kNameSpaceID_None,
                                            nsGkAtoms::value, aValue,
                                            PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetValueChanged(PRBool aValueChanged)
 {
+  PRBool valueChangedBefore = GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
+
   SET_BOOLBIT(mBitField, BF_VALUE_CHANGED, aValueChanged);
+
   if (!aValueChanged) {
     if (!IsSingleLineTextControl(PR_FALSE)) {
       FreeData();
     }
   }
+
+  if (valueChangedBefore != aValueChanged) {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_INVALID);
+    }
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::GetChecked(PRBool* aChecked)
 {
   *aChecked = GetChecked();
   return NS_OK;
@@ -1467,29 +1479,41 @@ nsHTMLInputElement::DoSetCheckedChanged(
   if (mType == NS_FORM_INPUT_RADIO) {
     if (GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED) != aCheckedChanged) {
       nsCOMPtr<nsIRadioVisitor> visitor;
       NS_GetRadioSetCheckedChangedVisitor(aCheckedChanged,
                                           getter_AddRefs(visitor));
       VisitGroup(visitor, aNotify);
     }
   } else {
-    SetCheckedChangedInternal(aCheckedChanged);
+    SetCheckedChangedInternal(aCheckedChanged, aNotify);
   }
 }
 
 void
-nsHTMLInputElement::SetCheckedChangedInternal(PRBool aCheckedChanged)
+nsHTMLInputElement::SetCheckedChangedInternal(PRBool aCheckedChanged,
+                                              PRBool aNotify)
 {
+  PRBool checkedChangedBefore = GetCheckedChanged();
+
   SET_BOOLBIT(mBitField, BF_CHECKED_CHANGED, aCheckedChanged);
+
+  if (aNotify && checkedChangedBefore != aCheckedChanged) {
+    nsIDocument* document = GetCurrentDoc();
+    if (document) {
+      mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, aNotify);
+      document->ContentStatesChanged(this, nsnull,
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
+    }
+  }
 }
 
 
 PRBool
-nsHTMLInputElement::GetCheckedChanged()
+nsHTMLInputElement::GetCheckedChanged() const
 {
   return GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetChecked(PRBool aChecked)
 {
   return DoSetChecked(aChecked, PR_TRUE, PR_TRUE);
@@ -3277,18 +3301,34 @@ nsHTMLInputElement::IntrinsicState() con
 
   if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID
-                       : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+    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.
+      if (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))) {
+        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
     nsTextEditorState* edState = GetEditorState();
     nsAutoString value;
 
@@ -3403,17 +3443,17 @@ nsHTMLInputElement::AddedToRadioGroup(PR
   //
   PRBool checkedChanged = GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED);
   nsCOMPtr<nsIRadioVisitor> visitor;
   nsresult rv = NS_GetRadioGetCheckedChangedVisitor(&checkedChanged, this,
                                            getter_AddRefs(visitor));
   if (NS_FAILED(rv)) { return; }
   
   VisitGroup(visitor, aNotify);
-  SetCheckedChangedInternal(checkedChanged);
+  SetCheckedChangedInternal(checkedChanged, aNotify);
   
   //
   // Add the radio to the radio group container.
   //
   nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
   if (container) {
     nsAutoString name;
     if (GetNameIfExists(name)) {
@@ -4128,17 +4168,17 @@ public:
 
   virtual ~nsRadioSetCheckedChangedVisitor() { }
 
   NS_IMETHOD Visit(nsIFormControl* aRadio, PRBool* aStop)
   {
     nsRefPtr<nsHTMLInputElement> radio =
       static_cast<nsHTMLInputElement*>(aRadio);
     NS_ASSERTION(radio, "Visit() passed a null button!");
-    radio->SetCheckedChangedInternal(mCheckedChanged);
+    radio->SetCheckedChangedInternal(mCheckedChanged, PR_TRUE);
     return NS_OK;
   }
 
 protected:
   PRPackedBool mCheckedChanged;
 };
 
 //
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -213,18 +213,18 @@ public:
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(PRBool aNotify);
 
   // nsIFileControlElement
   void GetDisplayFileName(nsAString& aFileName) const;
   const nsCOMArray<nsIDOMFile>& GetFiles();
   void SetFiles(const nsCOMArray<nsIDOMFile>& aFiles, bool aSetValueChanged);
 
-  void SetCheckedChangedInternal(PRBool aCheckedChanged);
-  PRBool GetCheckedChanged();
+  void SetCheckedChangedInternal(PRBool aCheckedChanged, PRBool aNotify);
+  PRBool GetCheckedChanged() const;
   void AddedToRadioGroup(PRBool aNotify = PR_TRUE);
   void WillRemoveFromRadioGroup();
   /**
    * Get the radio group container for this button (form or document)
    * @return the radio group container (or null if no form or document)
    */
   virtual already_AddRefed<nsIRadioGroupContainer> GetRadioGroupContainer();
 
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -563,22 +563,27 @@ nsHTMLTextAreaElement::SetValueChanged(P
 {
   PRBool previousValue = mValueChanged;
 
   mValueChanged = aValueChanged;
   if (!aValueChanged && !mState->IsEmpty()) {
     mState->EmptyValue();
   }
 
-  if (mValueChanged != previousValue &&
-      HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+  if (mValueChanged != previousValue) {
+    nsEventStates states = NS_EVENT_STATE_MOZ_UI_INVALID;
+
+    if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
@@ -1002,18 +1007,27 @@ nsHTMLTextAreaElement::IntrinsicState() 
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID
-                       : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+    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.
+      if (mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
+        state |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      }
+    }
   }
 
   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;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-checkbox-required-invalid-changed.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness has changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').checked = false;
+                if (!document.getElementById('i').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i' type='checkbox' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-checkbox-required-invalid-default.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness hasn't changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="if (document.getElementById('i').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i' type='checkbox' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-dyn-not-readonly-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input is no longer readonly, invalid and has it's value changed,
+             it's affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').removeAttribute('readonly');
+                document.getElementById('i').value = '';
+                document.documentElement.className='';">
+    <input class='invalid' id='i' readonly required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-dyn-not-readonly-not-changed.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input is no longer readonly and invalid but it's value hasn't
+             changed, it's affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').removeAttribute('readonly'); document.documentElement.className='';">
+    <input class='notinvalid' id='i' readonly required>
+  </body>
+</html>
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-dyn-not-readonly.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-  <!-- Test: if input is no longer readonly, it is candidate for constraint
-             validation and should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body onload="document.getElementById('i').removeAttribute('readonly'); document.documentElement.className='';">
-    <input class='invalid' id='i' readonly required>
-  </body>
-</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-email-invalid-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its value has changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value = 'foo';
+                document.documentElement.className='';">
+    <input id='i' class='invalid' type='email' value='bar'>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-email-invalid-default.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its default value hasn't been changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' type='email' value='foo'>
+  </body>
+</html>
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-email-invalid.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!-- Test: if input isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body>
-    <input class='invalid' type='email' value='foo'>
-  </body>
-</html>
-
--- a/layout/reftests/css-ui-invalid/input/input-fieldset-legend.html
+++ b/layout/reftests/css-ui-invalid/input/input-fieldset-legend.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html>
-  <!-- Test: if input has a disabled fieldset ancestor, but is in the first
-             legend, it is not barred from constraint validation and should be
+  <!-- Test: if input is invalid but it's value hasn't changed, it's not
              affected by :-moz-ui-invalid pseudo-class. -->
   <link rel='stylesheet' type='text/css' href='style.css'>
   <body>
     <fieldset disabled>
       <legend>
-        <input class='invalid' required>
+        <input class='notinvalid' required>
       </legend>
     </fieldset>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-file-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type='file' style="background-color: green;">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-file-required-invalid-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its default value hasn't been changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value='';
+                document.documentElement.className='';">
+    <input id='i' class='invalid' type='file' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-file-required-invalid-default.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its default value hasn't been changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' type='file' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-maxlength-invalid-changed.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its value has changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <!-- TODO: this is valid until bug bug 613016 and bug 613019 are fixed. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value='foo';
+                document.documentElement.className='';">
+    <input class='notinvalid' maxlength="2" id='i'>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-maxlength-invalid-default.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its default value hasn't been changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' maxlength="2" value="foo">
+  </body>
+</html>
+
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-maxlength-invalid.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-  <!-- Test: if input isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
-  <!-- TODO: this is valid until bug bug 613016 and bug 613019 are fixed. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body onload="document.getElementById('i').value='foo'; document.documentElement.className='';">
-    <input class='notinvalid' maxlength="2" id='i'>
-  </body>
-</html>
-
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-pattern-invalid-changed.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its value isn't the default value,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value='foo';
+                document.documentElement.className='';">
+    <input id='i' class='invalid' pattern='bar' value='f'>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-pattern-invalid-default.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its value is stil lthe default value,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' pattern='bar' value='foo'>
+  </body>
+</html>
+
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-pattern-invalid.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!-- Test: if input isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body>
-    <input class='invalid' pattern='bar' value='foo'>
-  </body>
-</html>
-
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-radio-required-invalid-changed-2.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness has changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i2').checked = false;
+                if (document.getElementById('i1').mozMatchesSelector(':-moz-ui-invalid') ||
+                    !document.getElementById('i2').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i1' type='radio'>
+    <input id='i2' type='radio' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-radio-required-invalid-changed.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness has changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').checked = false;
+                if (!document.getElementById('i').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i' type='radio' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-radio-required-invalid-default-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness hasn't changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="if (document.getElementById('i1').mozMatchesSelector(':-moz-ui-invalid') ||
+                    document.getElementById('i2').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i1' type='radio'>
+    <input id='i2' type='radio' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-radio-required-invalid-default.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its checkedness hasn't changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="if (document.getElementById('i').mozMatchesSelector(':-moz-ui-invalid')) {
+                  document.body.textContent='FAIL';
+                } else {
+                  document.body.textContent='SUCCESS';
+                }
+                document.documentElement.className='';">
+    <input id='i' type='radio' required>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-required-invalid-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value='';
+                document.documentElement.className='';">
+    <input id='i' class='invalid' required>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-required-invalid-default.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its value is still the default value,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' required>
+  </body>
+</html>
+
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-required-invalid.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!-- Test: if input isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body>
-    <input class='invalid' required>
-  </body>
-</html>
-
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-url-invalid-changed.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             and its value is not the default value,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('i').value='foo';
+                document.documentElement.className='';">
+    <input id='i' class='invalid' type='url'>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/input-url-invalid-default.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if input isn't valid nor barred from constraint validation,
+             but its value is the default value,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <input class='notinvalid' type='url' value='foo'>
+  </body>
+</html>
+
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/input/input-url-invalid.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!-- Test: if input isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body>
-    <input class='invalid' type='url' value='foo'>
-  </body>
-</html>
-
--- a/layout/reftests/css-ui-invalid/input/reftest.list
+++ b/layout/reftests/css-ui-invalid/input/reftest.list
@@ -1,26 +1,40 @@
 == input-valid.html input-ref.html
 == input-customerror.html input-ref.html
 == input-disabled.html input-ref.html
 == input-dyn-disabled.html input-ref.html
 == input-dyn-not-disabled.html input-ref.html
 == input-readonly.html input-ref.html
 == input-dyn-readonly.html input-ref.html
-== input-dyn-not-readonly.html input-ref.html
+== input-dyn-not-readonly-not-changed.html input-ref.html
+== input-dyn-not-readonly-changed.html input-ref.html
 == input-maxlength-valid.html input-ref.html
-== input-maxlength-invalid.html input-withtext-ref.html
+== input-maxlength-invalid-changed.html input-withtext-ref.html
+== input-maxlength-invalid-default.html input-withtext-ref.html
 == input-required-valid.html input-withtext-ref.html
-== input-required-invalid.html input-ref.html
+== input-required-invalid-default.html input-ref.html
+== input-required-invalid-changed.html input-ref.html
 == input-button.html input-button-ref.html
 == input-reset.html input-button-ref.html
-== input-email-invalid.html input-withtext-ref.html
+== input-email-invalid-default.html input-withtext-ref.html
+== input-email-invalid-changed.html input-withtext-ref.html
 == input-email-valid.html input-email-ref.html
-== input-url-invalid.html input-withtext-ref.html
+== input-url-invalid-changed.html input-withtext-ref.html
+== input-url-invalid-default.html input-withtext-ref.html
 == input-url-valid.html input-url-ref.html
 == input-pattern-valid.html input-withtext-ref.html
-== input-pattern-invalid.html input-withtext-ref.html
+== input-pattern-invalid-default.html input-withtext-ref.html
+== input-pattern-invalid-changed.html input-withtext-ref.html
 == input-type-barred.html input-button-ref.html
 == input-type-invalid.html input-ref.html
 == input-disabled-fieldset-1.html input-fieldset-ref.html
 == input-disabled-fieldset-2.html input-fieldset-ref.html
 == input-fieldset-legend.html input-fieldset-legend-ref.html
+== input-checkbox-required-invalid-changed.html success-ref.html
+== input-checkbox-required-invalid-default.html success-ref.html
+== input-radio-required-invalid-changed.html success-ref.html
+== input-radio-required-invalid-default.html success-ref.html
+== input-radio-required-invalid-changed-2.html success-ref.html
+== input-radio-required-invalid-default-2.html success-ref.html
+== input-file-required-invalid-changed.html input-file-ref.html
+== input-file-required-invalid-default.html input-file-ref.html
 # input type='hidden' shouldn't show
--- a/layout/reftests/css-ui-invalid/input/style.css
+++ b/layout/reftests/css-ui-invalid/input/style.css
@@ -11,8 +11,13 @@ input.notinvalid:-moz-ui-invalid {
 }
 
 input.invalid {
   background-color: red;
 }
 input.invalid:-moz-ui-invalid {
   background-color: green;
 }
+
+input[type=checkbox],
+input[type=radio] {
+  display: none;
+}
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/input/success-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    SUCCESS
+  </body>
+</html>
--- a/layout/reftests/css-ui-invalid/textarea/reftest.list
+++ b/layout/reftests/css-ui-invalid/textarea/reftest.list
@@ -1,15 +1,17 @@
 == textarea-valid.html textarea-ref.html
 == textarea-customerror.html textarea-ref.html
 == textarea-disabled.html textarea-ref.html
 == textarea-dyn-disabled.html textarea-ref.html
 == textarea-dyn-not-disabled.html textarea-ref.html
 == textarea-readonly.html textarea-ref.html
 == textarea-dyn-readonly.html textarea-ref.html
-== textarea-dyn-not-readonly.html textarea-ref.html
+== textarea-dyn-not-readonly-not-changed.html textarea-ref.html
+== textarea-dyn-not-readonly-changed.html textarea-ref.html
 == 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-fieldset-legend.html textarea-fieldset-legend-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if textarea is no longer readonly and has it's value changed,
+             it's affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('t').removeAttribute('readonly');
+                document.getElementById('t').value = '';
+                document.documentElement.className='';">
+    <textarea class='invalid' id='t' readonly required></textarea>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly-not-changed.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if textarea is no longer readonly, but doesn't have it's value changed,
+             it's not affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('t').removeAttribute('readonly'); document.documentElement.className='';">
+    <textarea class='notinvalid' id='t' readonly required></textarea>
+  </body>
+</html>
deleted file mode 100644
--- a/layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-  <!-- Test: if textarea is no longer readonly, it is candidate for constraint
-             validation and should be affected by :-moz-ui-invalid pseudo-class. -->
-  <link rel='stylesheet' type='text/css' href='style.css'>
-  <body onload="document.getElementById('t').removeAttribute('readonly'); document.documentElement.className='';">
-    <textarea class='invalid' id='t' readonly required></textarea>
-  </body>
-</html>
--- a/layout/reftests/css-ui-invalid/textarea/textarea-fieldset-legend.html
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-fieldset-legend.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
-  <!-- Test: if textarea has a disabled fieldset ancestor, but is in the first
-             legend, it is not barred from constraint validation and should be
+  <!-- Test: if textarea has a disabled fieldset ancestor, is in the first
+             legend but has its value not changed, it should not be
              affected by :-moz-ui-invalid pseudo-class. -->
   <link rel='stylesheet' type='text/css' href='style.css'>
   <body>
     <fieldset disabled>
       <legend>
-        <textarea class='invalid' required></textarea>
+        <textarea class='notinvalid' required></textarea>
       </legend>
     </fieldset>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-maxlength-default-value-invalid.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: if textarea has it's default value invalid, it should not be
+             affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body>
+    <textarea class='notinvalid' maxlength="2">foo</textarea>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-required-invalid-changed.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: if textarea isn't valid and it's value has been changed,
+             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <link rel='stylesheet' type='text/css' href='style.css'>
+  <body onload="document.getElementById('t').value = '';
+                document.documentElement.className='';">
+    <textarea id='t' class='invalid' required></textarea>
+  </body>
+</html>
+
--- a/layout/reftests/css-ui-invalid/textarea/textarea-required-invalid.html
+++ b/layout/reftests/css-ui-invalid/textarea/textarea-required-invalid.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
-  <!-- Test: if textarea isn't valid nor barred from constraint validation,
-             it should be affected by :-moz-ui-invalid pseudo-class. -->
+  <!-- Test: if textarea isn't valid but it's value hasn't been changed,
+             it should not be affected by :-moz-ui-invalid pseudo-class. -->
   <link rel='stylesheet' type='text/css' href='style.css'>
   <body>
-    <textarea class='invalid' required></textarea>
+    <textarea class='notinvalid' required></textarea>
   </body>
 </html>