Bug 1088761 - Support reportValidity() for form controls. r=smaug
☠☠ backed out by 5c41600ba212 ☠ ☠
authorJohn Dai <jdai@mozilla.com>
Wed, 11 May 2016 04:24:00 +0200
changeset 321628 403d9c0855448083a772f4ea52e089675fd4f2ce
parent 321627 3ff28843a8f8cbe2da800035d140f1fbe468d385
child 321629 f757f585e618b16f25e9b38274ca0e5f0c9da07d
push id9671
push userraliiev@mozilla.com
push dateMon, 06 Jun 2016 20:27:52 +0000
treeherdermozilla-aurora@cea65ca3d0bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1088761
milestone49.0a1
Bug 1088761 - Support reportValidity() for form controls. r=smaug
browser/modules/FormSubmitObserver.jsm
dom/html/HTMLButtonElement.h
dom/html/HTMLFieldSetElement.h
dom/html/HTMLFormElement.h
dom/html/HTMLInputElement.h
dom/html/HTMLObjectElement.h
dom/html/HTMLSelectElement.h
dom/html/HTMLTextAreaElement.h
dom/html/nsIConstraintValidation.cpp
dom/html/nsIConstraintValidation.h
dom/webidl/HTMLButtonElement.webidl
dom/webidl/HTMLFieldSetElement.webidl
dom/webidl/HTMLFormElement.webidl
dom/webidl/HTMLInputElement.webidl
dom/webidl/HTMLObjectElement.webidl
dom/webidl/HTMLOutputElement.webidl
dom/webidl/HTMLSelectElement.webidl
dom/webidl/HTMLTextAreaElement.webidl
mobile/android/chrome/content/browser.js
--- a/browser/modules/FormSubmitObserver.jsm
+++ b/browser/modules/FormSubmitObserver.jsm
@@ -99,23 +99,23 @@ FormSubmitObserver.prototype =
   {
     // We are going to handle invalid form submission attempt by focusing the
     // first invalid element and show the corresponding validation message in a
     // panel attached to the element.
     if (!aInvalidElements.length) {
       return;
     }
 
-    // Insure that this is the FormSubmitObserver associated with the form
+    // Insure that this is the FormSubmitObserver associated with the
     // element / window this notification is about.
-    if (this._content != aFormElement.ownerDocument.defaultView.top.document.defaultView) {
+    let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
+    if (this._content != element.ownerDocument.defaultView.top.document.defaultView) {
       return;
     }
 
-    let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
     if (!(element instanceof HTMLInputElement ||
           element instanceof HTMLTextAreaElement ||
           element instanceof HTMLSelectElement ||
           element instanceof HTMLButtonElement)) {
       return;
     }
 
     // Update validation message before showing notification
--- a/dom/html/HTMLButtonElement.h
+++ b/dom/html/HTMLButtonElement.h
@@ -160,16 +160,17 @@ public:
     SetHTMLAttr(nsGkAtoms::value, aValue, aRv);
   }
 
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
 
 protected:
   virtual ~HTMLButtonElement();
 
   uint8_t mType;
   bool mDisabledChanged;
   bool mInInternalActivate;
--- a/dom/html/HTMLFieldSetElement.h
+++ b/dom/html/HTMLFieldSetElement.h
@@ -21,16 +21,17 @@ namespace dom {
 class HTMLFieldSetElement final : public nsGenericHTMLFormElement,
                                   public nsIDOMHTMLFieldSetElement,
                                   public nsIConstraintValidation
 {
 public:
   using nsGenericHTMLFormElement::GetForm;
   using nsIConstraintValidation::Validity;
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
 
   explicit HTMLFieldSetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLFieldSetElement, fieldset)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -382,16 +382,21 @@ public:
 
   // XPCOM Reset() is OK
 
   bool CheckValidity()
   {
     return CheckFormValidity(nullptr);
   }
 
+  bool ReportValidity()
+  {
+    return CheckValidFormSubmission();
+  }
+
   Element*
   IndexedGetter(uint32_t aIndex, bool &aFound);
 
   already_AddRefed<nsISupports>
   NamedGetter(const nsAString& aName, bool &aFound);
 
   void GetSupportedNames(nsTArray<nsString>& aRetval);
 
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -104,16 +104,17 @@ class HTMLInputElement final : public ns
                                public nsITextControlElement,
                                public nsIPhonetic,
                                public nsIDOMNSEditableElement,
                                public nsIConstraintValidation
 {
 public:
   using nsIConstraintValidation::GetValidationMessage;
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::WillValidate;
   using nsIConstraintValidation::Validity;
   using nsGenericHTMLFormElementWithState::GetForm;
 
   HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                    mozilla::dom::FromParser aFromParser);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLInputElement, input)
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -151,16 +151,17 @@ public:
   }
   void SetHeight(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::height, aValue, aRv);
   }
   using nsObjectLoadingContent::GetContentDocument;
   nsPIDOMWindowOuter* GetContentWindow();
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
   void GetAlign(DOMString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::align, aValue);
   }
   void SetAlign(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::align, aValue, aRv);
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -262,16 +262,17 @@ public:
   void GetValue(DOMString& aValue);
   // Uses XPCOM SetValue.
 
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
 
   using nsINode::Remove;
 
 
   // nsINode
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -260,16 +260,17 @@ public:
   void SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError);
   // XPCOM GetValue/SetValue are fine
   uint32_t GetTextLength();
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
   // XPCOM Select is fine
   uint32_t GetSelectionStart(ErrorResult& aError);
   void SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError);
   uint32_t GetSelectionEnd(ErrorResult& aError);
   void SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError);
   void GetSelectionDirection(nsAString& aDirection, ErrorResult& aError);
   void SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError);
--- a/dom/html/nsIConstraintValidation.cpp
+++ b/dom/html/nsIConstraintValidation.cpp
@@ -5,20 +5,24 @@
  * 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/HTMLInputElement.h"
 #include "mozilla/dom/ValidityState.h"
 #include "nsIFormControl.h"
 #include "nsContentUtils.h"
 
+#include "nsIFormSubmitObserver.h"
+#include "nsIObserverService.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.
@@ -121,16 +125,81 @@ nsIConstraintValidation::CheckValidity(b
 {
   NS_ENSURE_ARG_POINTER(aValidity);
 
   *aValidity = CheckValidity();
 
   return NS_OK;
 }
 
+bool
+nsIConstraintValidation::ReportValidity()
+{
+  if (!IsCandidateForConstraintValidation() || IsValid()) {
+    return true;
+  }
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(this);
+  MOZ_ASSERT(content, "This class should be inherited by HTML elements only!");
+
+  bool defaultAction = true;
+  nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
+                                       NS_LITERAL_STRING("invalid"),
+                                       false, true, &defaultAction);
+  if (!defaultAction) {
+    return false;
+  }
+
+  nsCOMPtr<nsIObserverService> service =
+    mozilla::services::GetObserverService();
+  if (!service) {
+    NS_WARNING("No observer service available!");
+    return true;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> theEnum;
+  nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
+                                            getter_AddRefs(theEnum));
+
+  // Return true on error here because that's what we always did
+  NS_ENSURE_SUCCESS(rv, true);
+
+  bool hasObserver = false;
+  rv = theEnum->HasMoreElements(&hasObserver);
+
+  nsCOMPtr<nsIMutableArray> invalidElements =
+    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+  invalidElements->AppendElement(content, false);
+
+  NS_ENSURE_SUCCESS(rv, true);
+  nsCOMPtr<nsISupports> inst;
+  nsCOMPtr<nsIFormSubmitObserver> observer;
+  bool more = true;
+  while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
+    theEnum->GetNext(getter_AddRefs(inst));
+    observer = do_QueryInterface(inst);
+
+    if (observer) {
+      observer->NotifyInvalidSubmit(nullptr, invalidElements);
+    }
+  }
+
+  if (content->IsHTMLElement(nsGkAtoms::input) &&
+      nsContentUtils::IsFocusedContent(content)) {
+    HTMLInputElement* inputElement =
+    HTMLInputElement::FromContentOrNull(content);
+
+    inputElement->UpdateValidityUIBits(true);
+  }
+
+  dom::Element* element = content->AsElement();
+  element->UpdateState(true);
+  return false;
+}
+
 void
 nsIConstraintValidation::SetValidityState(ValidityStateType aState,
                                           bool aValue)
 {
   bool previousValidity = IsValid();
 
   if (aValue) {
     mValidityBitField |= aState;
--- a/dom/html/nsIConstraintValidation.h
+++ b/dom/html/nsIConstraintValidation.h
@@ -68,16 +68,17 @@ public:
                         bool aValue);
 
   // Web IDL binding methods
   bool WillValidate() const {
     return IsCandidateForConstraintValidation();
   }
   mozilla::dom::ValidityState* Validity();
   bool CheckValidity();
+  bool ReportValidity();
 
 protected:
 
   // You can't instantiate an object from that class.
   nsIConstraintValidation();
 
   nsresult GetValidity(nsIDOMValidityState** aValidity);
   nsresult CheckValidity(bool* aValidity);
--- a/dom/webidl/HTMLButtonElement.webidl
+++ b/dom/webidl/HTMLButtonElement.webidl
@@ -36,13 +36,14 @@ interface HTMLButtonElement : HTMLElemen
            attribute DOMString value;
 // Not yet implemented:
 //           attribute HTMLMenuElement? menu;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // Not yet implemented:
 //  readonly attribute NodeList labels;
 };
--- a/dom/webidl/HTMLFieldSetElement.webidl
+++ b/dom/webidl/HTMLFieldSetElement.webidl
@@ -22,11 +22,12 @@ interface HTMLFieldSetElement : HTMLElem
 
   readonly attribute HTMLCollection elements;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
 
   boolean checkValidity();
+  boolean reportValidity();
 
   void setCustomValidity(DOMString error);
 };
--- a/dom/webidl/HTMLFormElement.webidl
+++ b/dom/webidl/HTMLFormElement.webidl
@@ -42,12 +42,13 @@ interface HTMLFormElement : HTMLElement 
   getter Element (unsigned long index);
   // TODO this should be: getter (RadioNodeList or HTMLInputElement or HTMLImageElement) (DOMString name);
   getter nsISupports (DOMString name);
 
   [Throws]
   void submit();
   void reset();
   boolean checkValidity();
+  boolean reportValidity();
 
   [Pref="dom.forms.requestAutocomplete"]
   void requestAutocomplete();
 };
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -101,16 +101,17 @@ interface HTMLInputElement : HTMLElement
 
   [Pure]
   readonly attribute boolean willValidate;
   [Pure]
   readonly attribute ValidityState validity;
   [GetterThrows]
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   // Bug 850365 readonly attribute NodeList labels;
 
   void select();
 
   [Throws]
            // TODO: unsigned vs signed
--- a/dom/webidl/HTMLObjectElement.webidl
+++ b/dom/webidl/HTMLObjectElement.webidl
@@ -35,16 +35,17 @@ interface HTMLObjectElement : HTMLElemen
   readonly attribute Document? contentDocument;
   // Not pure: can trigger about:blank instantiation
   readonly attribute WindowProxy? contentWindow;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   [Throws]
   legacycaller any (any... arguments);
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#HTMLObjectElement-partial
 partial interface HTMLObjectElement {
--- a/dom/webidl/HTMLOutputElement.webidl
+++ b/dom/webidl/HTMLOutputElement.webidl
@@ -25,13 +25,14 @@ interface HTMLOutputElement : HTMLElemen
            attribute DOMString defaultValue;
   [SetterThrows, Pure]
            attribute DOMString value;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // Not yet implemented (bug 556743).
 //  readonly attribute NodeList labels;
 };
--- a/dom/webidl/HTMLSelectElement.webidl
+++ b/dom/webidl/HTMLSelectElement.webidl
@@ -45,15 +45,16 @@ interface HTMLSelectElement : HTMLElemen
            attribute long selectedIndex;
   [Pure]
            attribute DOMString value;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // NYI:  readonly attribute NodeList labels;
 
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=20720
   void remove();
 };
--- a/dom/webidl/HTMLTextAreaElement.webidl
+++ b/dom/webidl/HTMLTextAreaElement.webidl
@@ -47,16 +47,17 @@ interface HTMLTextAreaElement : HTMLElem
            attribute DOMString defaultValue;
   [TreatNullAs=EmptyString] attribute DOMString value;
   readonly attribute unsigned long textLength;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   // readonly attribute NodeList labels;
 
   void select();
   [Throws]
            attribute unsigned long selectionStart;
   [Throws]
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5294,25 +5294,25 @@ var FormAssistant = {
         break;
     }
   },
 
   notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) {
     if (!aInvalidElements.length)
       return;
 
-    // Ignore this notificaiton if the current tab doesn't contain the invalid form
+    // Ignore this notificaiton if the current tab doesn't contain the invalid element
+    let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports);
     if (BrowserApp.selectedBrowser.contentDocument !=
-        aFormElement.ownerDocument.defaultView.top.document)
+        currentElement.ownerDocument.defaultView.top.document)
       return;
 
     this._invalidSubmit = true;
 
     // Our focus listener will show the element's validation message
-    let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports);
     currentElement.focus();
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "focus": {
         let currentElement = aEvent.target;