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 338165 403d9c0855448083a772f4ea52e089675fd4f2ce
parent 338164 3ff28843a8f8cbe2da800035d140f1fbe468d385
child 338166 f757f585e618b16f25e9b38274ca0e5f0c9da07d
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1088761
milestone49.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 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;