Bug 717181 - Make <fieldset> invalid if they contain an invalid form control; r=mounir
authorMina Almasry <almasry.mina@gmail.com>
Fri, 16 Aug 2013 20:32:47 -0400
changeset 143053 5b5a0de73c74
parent 143052 d608711909f4
child 143054 dbc04a661a1a
push id32611
push usermlamouri@mozilla.com
push dateMon, 19 Aug 2013 10:24:53 +0000
treeherdermozilla-inbound@5b5a0de73c74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmounir
bugs717181
milestone26.0a1
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 717181 - Make <fieldset> invalid if they contain an invalid form control; r=mounir This adds a CSS psuedo class to fieldset that acts much in the same way as the one on form.
content/html/content/public/nsIFormControl.h
content/html/content/src/HTMLFieldSetElement.cpp
content/html/content/src/HTMLFieldSetElement.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsIConstraintValidation.cpp
content/html/content/test/forms/test_validation.html
layout/reftests/css-invalid/fieldset/fieldset-add-invalid-barred.html
layout/reftests/css-invalid/fieldset/fieldset-add-invalid-element-dynamic.html
layout/reftests/css-invalid/fieldset/fieldset-add-invalid-element.html
layout/reftests/css-invalid/fieldset/fieldset-add-invalid-with-valid-element.html
layout/reftests/css-invalid/fieldset/fieldset-add-valid-element.html
layout/reftests/css-invalid/fieldset/fieldset-add-valid-with-invalid-element.html
layout/reftests/css-invalid/fieldset/fieldset-add-valid-with-no-element.html
layout/reftests/css-invalid/fieldset/fieldset-div-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid-barred.html
layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid-not-barred.html
layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-dynamic-valid.html
layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred-remove-barred.html
layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred-remove-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred.html
layout/reftests/css-invalid/fieldset/fieldset-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-is-barred.html
layout/reftests/css-invalid/fieldset/fieldset-nested-barred-ref.html
layout/reftests/css-invalid/fieldset/fieldset-nested-barred.html
layout/reftests/css-invalid/fieldset/fieldset-nested-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-nested-valid-invalid-ref.html
layout/reftests/css-invalid/fieldset/fieldset-nested-valid-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-ref.html
layout/reftests/css-invalid/fieldset/fieldset-remove-invalid-element.html
layout/reftests/css-invalid/fieldset/fieldset-static-invalid-barred.html
layout/reftests/css-invalid/fieldset/fieldset-static-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-static-valid.html
layout/reftests/css-invalid/fieldset/fieldset-valid-and-barred-remove-barred.html
layout/reftests/css-invalid/fieldset/fieldset-valid-and-barred.html
layout/reftests/css-invalid/fieldset/fieldset-valid.html
layout/reftests/css-invalid/fieldset/fieldset-with-invalid-element-add-barred-dynamic.html
layout/reftests/css-invalid/fieldset/fieldset-with-valid-and-invalid.html
layout/reftests/css-invalid/fieldset/fieldset-with-valid-element-add-barred-dynamic.html
layout/reftests/css-invalid/fieldset/reftest.list
layout/reftests/css-valid/fieldset/fieldset-invalid.html
layout/reftests/css-valid/fieldset/fieldset-ref.html
layout/reftests/css-valid/fieldset/fieldset-valid.html
layout/reftests/css-valid/fieldset/reftest.list
--- a/content/html/content/public/nsIFormControl.h
+++ b/content/html/content/public/nsIFormControl.h
@@ -10,16 +10,17 @@ class nsIDOMHTMLFormElement;
 class nsPresState;
 class nsString;
 class nsIFormProcessor;
 class nsFormSubmission;
 
 namespace mozilla {
 namespace dom {
 class Element;
+class HTMLFieldSetElement;
 } // namespace dom
 } // namespace mozilla
 
 enum FormControlsTypes {
   NS_FORM_FIELDSET = 1,
   NS_FORM_LABEL,
   NS_FORM_OUTPUT,
   NS_FORM_SELECT,
@@ -83,16 +84,22 @@ PR_STATIC_ASSERT((uint32_t)eInputElement
  */
 class nsIFormControl : public nsISupports
 {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFORMCONTROL_IID)
 
   /**
+   * Get the fieldset for this form control.
+   * @return the fieldset
+   */
+  virtual mozilla::dom::HTMLFieldSetElement *GetFieldSet() = 0;
+
+  /**
    * Get the form for this form control.
    * @return the form
    */
   virtual mozilla::dom::Element *GetFormElement() = 0;
 
   /**
    * Set the form for this form control.
    * @param aForm the form.  This must not be null.
--- a/content/html/content/src/HTMLFieldSetElement.cpp
+++ b/content/html/content/src/HTMLFieldSetElement.cpp
@@ -12,22 +12,23 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(FieldSet)
 
 namespace mozilla {
 namespace dom {
 
 HTMLFieldSetElement::HTMLFieldSetElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLFormElement(aNodeInfo)
   , mElements(nullptr)
   , mFirstLegend(nullptr)
+  , mInvalidElementsCount(0)
 {
   // <fieldset> is always barred from constraint validation.
   SetBarredFromConstraintValidation(true);
 
-  // We start out enabled
-  AddStatesSilently(NS_EVENT_STATE_ENABLED);
+  // We start out enabled and valid.
+  AddStatesSilently(NS_EVENT_STATE_ENABLED | NS_EVENT_STATE_VALID);
 }
 
 HTMLFieldSetElement::~HTMLFieldSetElement()
 {
   uint32_t length = mDependentElements.Length();
   for (uint32_t i = 0; i < length; ++i) {
     mDependentElements[i]->ForgetFieldSet(this);
   }
@@ -206,16 +207,42 @@ HTMLFieldSetElement::RemoveChildAt(uint3
   nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
 
   if (firstLegendHasChanged) {
     NotifyElementsForFirstLegendChange(aNotify);
   }
 }
 
 void
+HTMLFieldSetElement::AddElement(nsGenericHTMLFormElement* aElement)
+{
+  mDependentElements.AppendElement(aElement);
+
+  // We need to update the validity of the fieldset.
+  nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
+  if (cvElmt &&
+      cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
+    UpdateValidity(false);
+  }
+}
+
+void
+HTMLFieldSetElement::RemoveElement(nsGenericHTMLFormElement* aElement)
+{
+  mDependentElements.RemoveElement(aElement);
+
+  // We need to update the validity of the fieldset.
+  nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
+  if (cvElmt &&
+      cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
+    UpdateValidity(true);
+  }
+}
+
+void
 HTMLFieldSetElement::NotifyElementsForFirstLegendChange(bool aNotify)
 {
   /**
    * NOTE: this could be optimized if only call when the fieldset is currently
    * disabled.
    * This should also make sure that mElements is set when we happen to be here.
    * However, this method shouldn't be called very often in normal use cases.
    */
@@ -226,16 +253,56 @@ HTMLFieldSetElement::NotifyElementsForFi
 
   uint32_t length = mElements->Length(true);
   for (uint32_t i = 0; i < length; ++i) {
     static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
       ->FieldSetFirstLegendChanged(aNotify);
   }
 }
 
+void
+HTMLFieldSetElement::UpdateValidity(bool aElementValidity)
+{
+  if (aElementValidity) {
+    --mInvalidElementsCount;
+  } else {
+    ++mInvalidElementsCount;
+  }
+
+  MOZ_ASSERT(mInvalidElementsCount >= 0);
+
+  // The fieldset validity has just changed if:
+  // - there are no more invalid elements ;
+  // - or there is one invalid elmement and an element just became invalid.
+  if (!mInvalidElementsCount || (mInvalidElementsCount == 1 && !aElementValidity)) {
+    UpdateState(true);
+  }
+
+  // We should propagate the change to the fieldset parent chain.
+  if (mFieldSet) {
+    mFieldSet->UpdateValidity(aElementValidity);
+  }
+
+  return;
+}
+
+nsEventStates
+HTMLFieldSetElement::IntrinsicState() const
+{
+  nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
+
+  if (mInvalidElementsCount) {
+    state |= NS_EVENT_STATE_INVALID;
+  } else {
+    state |= NS_EVENT_STATE_VALID;
+  }
+
+  return state;
+}
+
 JSObject*
 HTMLFieldSetElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLFieldSetElementBinding::Wrap(aCx, aScope, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLFieldSetElement.h
+++ b/content/html/content/src/HTMLFieldSetElement.h
@@ -50,23 +50,19 @@ public:
   NS_IMETHOD_(uint32_t) GetType() const MOZ_OVERRIDE { return NS_FORM_FIELDSET; }
   NS_IMETHOD Reset() MOZ_OVERRIDE;
   NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) MOZ_OVERRIDE;
   virtual bool IsDisabledForEvents(uint32_t aMessage) MOZ_OVERRIDE;
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   const nsIContent* GetFirstLegend() const { return mFirstLegend; }
 
-  void AddElement(nsGenericHTMLFormElement* aElement) {
-    mDependentElements.AppendElement(aElement);
-  }
+  void AddElement(nsGenericHTMLFormElement* aElement);
 
-  void RemoveElement(nsGenericHTMLFormElement* aElement) {
-    mDependentElements.RemoveElement(aElement);
-  }
+  void RemoveElement(nsGenericHTMLFormElement* aElement);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFieldSetElement,
                                            nsGenericHTMLFormElement)
 
   // WebIDL
   bool Disabled() const
   {
     return GetBoolAttr(nsGkAtoms::disabled);
@@ -92,16 +88,31 @@ public:
   // XPCOM Validity is OK for us
 
   // XPCOM GetValidationMessage is OK for us
 
   // XPCOM CheckValidity is OK for us
 
   // XPCOM SetCustomValidity is OK for us
 
+  virtual nsEventStates IntrinsicState() const;
+
+
+  /*
+   * This method will update the fieldset's validity.  This method has to be
+   * called by fieldset elements whenever their validity state or status regarding
+   * constraint validation changes.
+   *
+   * @note If an element becomes barred from constraint validation, it has to
+   * be considered as valid.
+   *
+   * @param aElementValidityState the new validity state of the element
+   */
+  void UpdateValidity(bool aElementValidityState);
+
 protected:
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
 private:
 
   /**
    * Notify all elements (in mElements) that the first legend of the fieldset
@@ -115,15 +126,22 @@ private:
 
   // listed form controls elements.
   nsRefPtr<nsContentList> mElements;
 
   // List of elements which have this fieldset as first fieldset ancestor.
   nsTArray<nsGenericHTMLFormElement*> mDependentElements;
 
   nsIContent* mFirstLegend;
+
+  /**
+   * Number of invalid and candidate for constraint validation
+   * elements in the fieldSet the last time UpdateValidity has been called.
+   *
+   * @note Should only be used by UpdateValidity() and IntrinsicState()!
+   */
+  int32_t mInvalidElementsCount;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLFieldSetElement_h */
-
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2100,16 +2100,22 @@ nsGenericHTMLFormElement::ClearForm(bool
 }
 
 Element*
 nsGenericHTMLFormElement::GetFormElement()
 {
   return mForm;
 }
 
+HTMLFieldSetElement*
+nsGenericHTMLFormElement::GetFieldSet()
+{
+  return mFieldSet;
+}
+
 nsresult
 nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   NS_ENSURE_ARG_POINTER(aForm);
   NS_IF_ADDREF(*aForm = mForm);
   return NS_OK;
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1270,16 +1270,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   nsINode* GetParentObject() const;
 
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   // nsIFormControl
+  virtual mozilla::dom::HTMLFieldSetElement* GetFieldSet();
   virtual mozilla::dom::Element* GetFormElement() MOZ_OVERRIDE;
   mozilla::dom::HTMLFormElement* GetForm() const
   {
     return mForm;
   }
   virtual void SetForm(nsIDOMHTMLFormElement* aForm) MOZ_OVERRIDE;
   virtual void ClearForm(bool aRemoveFromForm) MOZ_OVERRIDE;
 
--- a/content/html/content/src/nsIConstraintValidation.cpp
+++ b/content/html/content/src/nsIConstraintValidation.cpp
@@ -3,22 +3,26 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIConstraintValidation.h"
 
 #include "nsAString.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLFormElement.h"
+#include "mozilla/dom/HTMLFieldSetElement.h"
 #include "mozilla/dom/ValidityState.h"
 #include "nsIFormControl.h"
 #include "nsContentUtils.h"
 
 const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
 
+using namespace mozilla;
+using namespace mozilla::dom;
+
 nsIConstraintValidation::nsIConstraintValidation()
   : mValidityBitField(0)
   // By default, all elements are subjects to constraint validation.
   , mBarredFromConstraintValidation(false)
 {
 }
 
 nsIConstraintValidation::~nsIConstraintValidation()
@@ -126,26 +130,30 @@ nsIConstraintValidation::SetValidityStat
   bool previousValidity = IsValid();
 
   if (aValue) {
     mValidityBitField |= aState;
   } else {
     mValidityBitField &= ~aState;
   }
 
-  // Inform the form element if our validity has changed.
+  // Inform the form and fieldset elements if our validity has changed.
   if (previousValidity != IsValid() && IsCandidateForConstraintValidation()) {
     nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
     NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
 
-    mozilla::dom::HTMLFormElement* form =
-      static_cast<mozilla::dom::HTMLFormElement*>(formCtrl->GetFormElement());
+    HTMLFormElement* form =
+      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
     if (form) {
       form->UpdateValidity(IsValid());
     }
+    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
+      if (fieldSet) {
+      fieldSet->UpdateValidity(IsValid());
+    }
   }
 }
 
 void
 nsIConstraintValidation::SetCustomValidity(const nsAString& aError)
 {
   mCustomValidity.Assign(aError);
   SetValidityState(VALIDITY_STATE_CUSTOM_ERROR, !mCustomValidity.IsEmpty());
@@ -153,25 +161,29 @@ nsIConstraintValidation::SetCustomValidi
 
 void
 nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred)
 {
   bool previousBarred = mBarredFromConstraintValidation;
 
   mBarredFromConstraintValidation = aBarred;
 
-  // Inform the form element if our status regarding constraint validation
-  // is going to change.
+  // Inform the form and fieldset elements if our status regarding constraint
+  // validation is going to change.
   if (!IsValid() && previousBarred != mBarredFromConstraintValidation) {
     nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
     NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
 
-    mozilla::dom::HTMLFormElement* form =
-      static_cast<mozilla::dom::HTMLFormElement*>(formCtrl->GetFormElement());
+    // If the element is going to be barred from constraint validation, we can
+    // inform the form and fieldset that we are now valid. Otherwise, we are now
+    // invalid.
+    HTMLFormElement* form =
+      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
     if (form) {
-      // If the element is going to be barred from constraint validation,
-      // we can inform the form that we are now valid.
-      // Otherwise, we are now invalid.
       form->UpdateValidity(aBarred);
     }
+    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
+    if (fieldSet) {
+      fieldSet->UpdateValidity(aBarred);
+    }
   }
 }
 
--- a/content/html/content/test/forms/test_validation.html
+++ b/content/html/content/test/forms/test_validation.html
@@ -84,18 +84,18 @@ function checkConstraintValidationAPIDef
   ok(element.validity.valid, "The element should be valid by default");
 
   ok(element.checkValidity(), "The element should be valid by default");
 }
 
 function checkDefaultPseudoClass()
 {
   is(window.getComputedStyle(document.getElementById('f'), null)
-       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
-     "Nor :valid and :invalid should apply");
+       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+     ":valid should apply");
 
   is(window.getComputedStyle(document.getElementById('o'), null)
        .getPropertyValue('background-color'), "rgb(0, 255, 0)",
      ":valid should apply");
 
   is(window.getComputedStyle(document.getElementById('obj'), null)
        .getPropertyValue('background-color'), "rgb(0, 0, 0)",
      "Nor :valid and :invalid should apply");
@@ -222,27 +222,34 @@ function checkCustomError(element, isBar
        "When the element has a custom validity message, validation message should return it");
   } else {
     is(element.validationMessage, "",
        "An element barred from constraint validation can't have a validation message");
   }
   ok(element.validity.customError, "The element should suffer from a custom error");
   ok(!element.validity.valid, "The element should not be valid with a custom error");
 
-  is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
-     ":invalid pseudo-classs should apply");
+  if (element.tagName == "FIELDSET") {
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       isBarred ? "rgb(0, 255, 0)" : "rgb(255, 0, 0)",
+       ":invalid pseudo-classs should apply" + element.tagName);
+  }
+  else {
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
+       ":invalid pseudo-classs should apply" + element.tagName);
+  }
 
   element.setCustomValidity("");
   is(element.validationMessage, "", "The element should not have a validation message when reseted");
   ok(!element.validity.customError, "The element should not suffer anymore from a custom error");
   ok(element.validity.valid, "The element should now be valid");
 
   is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     isBarred ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
+     isBarred && element.tagName != "FIELDSET" ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
      ":valid pseudo-classs should apply");
 }
 
 function checkCheckValidity(element)
 {
   element.setCustomValidity("message");
   ok(!element.checkValidity(), "checkValidity() should return false when the element is not valid");
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-invalid-barred.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- fieldset with invalid barred for constraint validation element -->
+<html>
+  <head>
+    <style>
+      fieldset:valid { display: none ;}
+    </style>
+    <script>
+      function onloadHandler()
+      {
+        document.getElementById("fieldset").appendChild(document.getElementById('i'));
+        document.documentElement.className = '';
+      }
+    </script>
+  </head>
+  <body onload="onloadHandler();">
+    <input required readonly id="i">
+    <fieldset id="fieldset">
+    </fieldset>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-invalid-element-dynamic.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- fieldset with no elements and invalid element is added dynamically -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display:none }
+    </style>
+    <script>
+      function onLoadHandler()
+      {
+        document.getElementById("fieldset").appendChild(document.getElementById('i'));
+        document.documentElement.className='';
+      }
+    </script>
+  </head>
+  <body onload='onLoadHandler();'>
+    <input id='i' required>
+    <fieldset id="fieldset">
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-invalid-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--fieldset with one invalid element and another invalid one is added dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").appendChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <input id='i' type='email' value='foo'>
+    <fieldset id="fieldset">
+      <input id='j' type='email' value='foo'>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-invalid-with-valid-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--fieldset with one valid element and invalid one is added dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").appendChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <input id='i' type='email' value='bar'>
+    <fieldset id="fieldset">
+      <input id='j' type='text' value='foo'>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-valid-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--fieldset with one valid element and another valid one is added dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").appendChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <input id='i' type='text' value='bar'>
+    <fieldset id="fieldset">
+      <input id='j' type='text' value='foo'>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-valid-with-invalid-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--fieldset with one invalid element and another valid one is added dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").appendChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <input id='i' type='text' value='foo'>
+    <fieldset id="fieldset">
+      <input id='j' type='email' value='emailfoo'>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-add-valid-with-no-element.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!--fieldset with no valid element and another valid one is added dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").appendChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <input id='i' type='text' value='foo'>
+    <fieldset id="fieldset">
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-div-invalid.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- fieldset with input in div should become invalid -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display: none ;}
+    </style>
+  </head>
+  <body>
+    <fieldset>
+      <div>
+        <input required value="">
+      </div>
+    </fieldset>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid-barred.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and dynamically made it element with
+barred constraints -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById('i').readOnly = true;
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='i' required>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid-not-barred.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById('i').readOnly = false;
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='i' required readonly>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-dynamic-invalid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element which is made invalid dynamically -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById('i').value = '';
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='i' value='foo' required>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-dynamic-valid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!--  fieldset with one valid element which is made valid dynamically -->
+<html>
+  <head>
+    <style>
+      fieldset:valid {display: none;}
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById('i').value = 'foo';
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload="onloadHandler();">
+    <fieldset id="fieldset">
+      <input id='i' required>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred-remove-barred.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and a barred for constraint
+validation element then you remove the second element -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").removeChild(document.getElementById('j'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input required>
+      <input id='j' value='foo' readonly>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred-remove-invalid.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and a barred for constraint
+validation element then you remove the second element -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").removeChild(document.getElementById('j'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='j' required>
+      <input value='foo' readonly>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-invalid-and-barred.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and a barred for constraint validation element -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+ <body>
+   <fieldset id="fieldset">
+    <input required>
+    <input id='i' value='foo' readonly required>
+   </fieldset>
+ </body>
+</html>
--- a/layout/reftests/css-invalid/fieldset/fieldset-invalid.html
+++ b/layout/reftests/css-invalid/fieldset/fieldset-invalid.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-  <!-- Test: even if it has a custom error, a fieldset should not be affected by
-             :invalid pseudo-class. -->
-  <style>
-    fieldset { background-color: green; }
-    fieldset:invalid { background-color: red; }
-  </style>
-  <body onload="document.getElementById('f').setCustomValidity('foo'); document.documentElement.className='';">
-    <fieldset id='f'></fieldset>
-  </body>
+<!-- Invalid fieldset -->
+<style>
+  fieldset:invalid { display: none; }
+</style>
+<body onload="document.getElementById('input').setCustomValidity('foo'); document.documentElement.className='';">
+<fieldset>
+  <input id="input">
+</fieldset>
+</body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-is-barred.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<!-- Fieldset is barred -->
+<style>
+  fieldset:valid { display: none; }
+</style>
+<body onload="document.getElementById('f').setCustomValidity('foo'); document.documentElement.className='';">
+<fieldset id="f">
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-nested-barred-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Reference for input in nested fieldset becomes barred -->
+<html>
+  <body>
+    <fieldset style="background-color: green;">
+      <fieldset style="background-color: green;">
+        <input id='i' required readonly>
+      </fieldset>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-nested-barred.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- input in nested fieldset becomes barred -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { background-color: green; }
+      fieldset:invalid { background-color: red; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById('i').readOnly = true;
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset>
+      <fieldset>
+        <input id='i' required>
+      </fieldset>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-nested-invalid.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Nested fieldsets should all become invalid -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display: none ;}
+    </style>
+  </head>
+  <body>
+    <fieldset>
+      <fieldset>
+        <input required value="">
+      </fieldset>
+    </fieldset>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-nested-valid-invalid-ref.html
@@ -0,0 +1,22 @@
+
+<!DOCTYPE html>
+<!-- Nested fieldsets should be assigned validation correctly -->
+<html>
+  <head>
+    <style>
+      fieldset:valid { background-color: green; }
+      fieldset:invalid { background-color: red; }
+    </style>
+  </head>
+  <body>
+    <fieldset style="background-color:red;">
+      <fieldset style="background-color: green;">
+        <input>
+      </fieldset>
+      <fieldset style="background-color: red">
+        <input required>
+      </fieldset>
+    </fieldset>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-nested-valid-invalid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- Nested fieldsets should be assigned validation correctly -->
+<html>
+  <head>
+    <style>
+      fieldset:valid { background-color: green; }
+      fieldset:invalid { background-color: red; }
+    </style>
+  </head>
+  <body>
+    <fieldset>
+      <fieldset>
+        <input>
+      </fieldset>
+      <fieldset>
+        <input required>
+      </fieldset>
+    </fieldset>
+  </body>
+</html>
+
deleted file mode 100644
--- a/layout/reftests/css-invalid/fieldset/fieldset-ref.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <body>
-    <fieldset style="background-color: green;"></fieldset>
-  </body>
-</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-remove-invalid-element.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and the element is dynamically removed -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").removeChild(document.getElementById('i'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='i' type='email' value='foo'>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-static-invalid-barred.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <body>
+    <fieldset id="fieldset">
+      <input required readonly>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-static-invalid.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!--fieldset with only invalid elements -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <body>
+    <fieldset id="fieldset">
+      <input required>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-static-valid.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- fieldset with only valid elements -->
+<html>
+ <head>
+   <style>
+     fieldset:valid { display: none; }
+   </style>
+ </head>
+ <body>
+   <fieldset id="fieldset">
+    <input value='foo' required>
+   </fieldset>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-valid-and-barred-remove-barred.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- fieldset with one valid element and a barred for constraint
+validation element then you remove the second element -->
+<html class='reftest-wait'>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+  <script>
+    function onloadHandler()
+    {
+      document.getElementById("fieldset").removeChild(document.getElementById('j'));
+      document.documentElement.className = '';
+    }
+  </script>
+  <body onload='onloadHandler();'>
+    <fieldset id="fieldset">
+      <input id='i' value='foo' required>
+      <input id='j' value='bar' readonly>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-valid-and-barred.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- fieldset with one invalid element and a barred for constraint validation element -->
+<html>
+  <head>
+    <style>
+      fieldset:valid { display: none; }
+    </style>
+  </head>
+ <body>
+   <fieldset id="fieldset">
+    <input id='i' value='bar' required>
+    <input id='i' value='foo' readonly required>
+   </fieldset>
+ </body>
+</html>
--- a/layout/reftests/css-invalid/fieldset/fieldset-valid.html
+++ b/layout/reftests/css-invalid/fieldset/fieldset-valid.html
@@ -1,12 +1,14 @@
 <!DOCTYPE html>
+<!-- Valid fieldset. -->
 <html>
-  <!-- Test: fieldset is always barred from constraint validation.
-             It should not be affected by :invalid pseudo-class. -->
-  <style>
-    fieldset { background-color: green; }
-    fieldset:invalid { background-color: red; }
-  </style>
+  <head>
+    <style>
+      fieldset:valid { display: none ;}
+    </style>
+  </head>
   <body>
-    <fieldset></fieldset>
+    <fieldset>
+    </fieldset>
   </body>
 </html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-with-invalid-element-add-barred-dynamic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- fieldset with one valid element and you dynamically add a barred constraint
+validation element -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display:none }
+    </style>
+    <script>
+      function onLoadHandler()
+      {
+        document.getElementById("fieldset").appendChild(document.getElementById('i'));
+        document.documentElement.className='';
+      }
+    </script>
+  </head>
+  <body onload='onLoadHandler();'>
+    <input id='i' value='foo' readonly>
+    <fieldset id="fieldset">
+      <input required>
+    </fieldset>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-with-valid-and-invalid.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- fieldset with invalid and valid elements -->
+<html>
+  <head>
+    <style>
+      fieldset:invalid { display: none; }
+    </style>
+  </head>
+  <body>
+    <fieldset>
+      <input id='i' value='foo'>
+      <input required>
+    <fieldset/>
+  <body>
+<html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-invalid/fieldset/fieldset-with-valid-element-add-barred-dynamic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- fieldset with one valid element and you dynamically add a barred constraint
+validation element -->
+<html>
+  <head>
+    <style>
+      fieldset:valid { display:none }
+    </style>
+    <script>
+      function onLoadHandler()
+      {
+        document.getElementById("fieldset").appendChild(document.getElementById('i'));
+        document.documentElement.className='';
+      }
+    </script>
+  </head>
+  <body onload='onLoadHandler();'>
+    <input id='i' value='foo' readonly>
+    <fieldset id="fieldset" id="fieldset">
+      <input id='j' value='bar' required>
+    </fieldset>
+  </body>
+</html>
--- a/layout/reftests/css-invalid/fieldset/reftest.list
+++ b/layout/reftests/css-invalid/fieldset/reftest.list
@@ -1,2 +1,29 @@
-== fieldset-valid.html fieldset-ref.html
-== fieldset-invalid.html fieldset-ref.html
+== fieldset-valid.html about:blank
+== fieldset-invalid.html about:blank
+== fieldset-add-invalid-barred.html about:blank
+== fieldset-add-invalid-element-dynamic.html about:blank
+== fieldset-add-invalid-element.html about:blank
+== fieldset-add-invalid-with-valid-element.html about:blank
+== fieldset-add-valid-element.html about:blank
+== fieldset-add-valid-with-invalid-element.html about:blank
+== fieldset-add-valid-with-no-element.html about:blank
+== fieldset-dynamic-invalid-barred.html about:blank
+== fieldset-dynamic-invalid-not-barred.html about:blank
+== fieldset-dynamic-invalid.html about:blank
+== fieldset-dynamic-valid.html about:blank
+== fieldset-invalid-and-barred-remove-barred.html about:blank
+== fieldset-invalid-and-barred-remove-invalid.html about:blank
+== fieldset-invalid-and-barred.html about:blank
+== fieldset-remove-invalid-element.html about:blank
+== fieldset-static-invalid-barred.html about:blank
+== fieldset-static-invalid.html about:blank
+== fieldset-static-valid.html about:blank
+== fieldset-valid-and-barred-remove-barred.html about:blank
+== fieldset-valid-and-barred.html about:blank
+== fieldset-with-invalid-element-add-barred-dynamic.html about:blank
+== fieldset-with-valid-and-invalid.html about:blank
+== fieldset-with-valid-element-add-barred-dynamic.html about:blank
+== fieldset-nested-invalid.html about:blank
+== fieldset-div-invalid.html about:blank
+== fieldset-nested-valid-invalid.html fieldset-nested-valid-invalid-ref.html
+== fieldset-nested-barred.html fieldset-nested-barred-ref.html
--- a/layout/reftests/css-valid/fieldset/fieldset-invalid.html
+++ b/layout/reftests/css-valid/fieldset/fieldset-invalid.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-  <!-- Test: fieldset is always barred from constraint validation.
-             It should not be affected by :valid pseudo-class. -->
+  <!-- Test: fieldset undergoes constraint validation.
+             It should be affected by :invalid pseudo-class. -->
   <style>
-    fieldset { background-color: green; }
-    fieldset:valid { background-color: red; }
+    fieldset:invalid { display: none; }
   </style>
   <body onload="document.getElementById('f').setCustomValidity('foo'); document.documentElement.className='';">
-    <fieldset id='f'></fieldset>
+    <fieldset>
+      <input id='f'>
+    </fieldset>
   </body>
 </html>
deleted file mode 100644
--- a/layout/reftests/css-valid/fieldset/fieldset-ref.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <body>
-    <fieldset style="background-color: green;"></fieldset>
-  </body>
-</html>
--- a/layout/reftests/css-valid/fieldset/fieldset-valid.html
+++ b/layout/reftests/css-valid/fieldset/fieldset-valid.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
-  <!-- Test: fieldset is always barred from constraint validation.
-             It should not be affected by :valid pseudo-class. -->
+  <!-- Test: fieldset undergoes constraint validation.
+             It should be affected by :valid pseudo-class. -->
   <style>
-    fieldset { background-color: green; }
-    fieldset:valid { background-color: red; }
+    fieldset:valid { display: none; }
   </style>
   <body>
-    <fieldset></fieldset>
+    <fieldset>
+    </fieldset>
   </body>
 </html>
--- a/layout/reftests/css-valid/fieldset/reftest.list
+++ b/layout/reftests/css-valid/fieldset/reftest.list
@@ -1,2 +1,2 @@
-== fieldset-valid.html fieldset-ref.html
-== fieldset-invalid.html fieldset-ref.html
+== fieldset-valid.html about:blank
+== fieldset-invalid.html about:blank