Bug 556013 - Implement novalidate attribute for form elements. r=smaug a2.0=blocking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Sat, 11 Sep 2010 06:11:58 +0200
changeset 52448 8235bdf7c65eb9d84da729439f00df39fc05671c
parent 52447 e3f194205a3bd556d471bb69c2621a9457945c76
child 52449 5bf4e798d24f7cfe3e8c8f4456c7542b16d18850
push id15645
push usermlamouri@mozilla.com
push dateSat, 11 Sep 2010 04:22:15 +0000
treeherdermozilla-central@2779c55431a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs556013
milestone2.0b6pre
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 556013 - Implement novalidate attribute for form elements. r=smaug a2.0=blocking
content/base/src/nsGkAtomList.h
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLFormElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/test_bug556013.html
dom/interfaces/html/nsIDOMHTMLFormElement.idl
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -596,16 +596,17 @@ GK_ATOM(noembed, "noembed")
 GK_ATOM(noframes, "noframes")
 GK_ATOM(nohref, "nohref")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
 GK_ATOM(normal, "normal")
 GK_ATOM(normalizeSpace, "normalize-space")
 GK_ATOM(noscript, "noscript")
 GK_ATOM(noshade, "noshade")
+GK_ATOM(novalidate, "novalidate")
 GK_ATOM(_not, "not")
 GK_ATOM(nowrap, "nowrap")
 GK_ATOM(number, "number")
 GK_ATOM(null, "null")
 GK_ATOM(object, "object")
 GK_ATOM(objectType, "object-type")
 GK_ATOM(observer, "observer")
 GK_ATOM(observes, "observes")
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -502,16 +502,17 @@ nsHTMLButtonElement::PostHandleEvent(nsE
           aVisitor.mPresContext->GetPresShell();
         // If |nsIPresShell::Destroy| has been called due to
         // handling the event, the pres context will return
         // a null pres shell.  See bug 125624.
         //
         // Using presShell to dispatch the event. It makes sure that
         // event is not handled if the window is being destroyed.
         if (presShell && (event.message != NS_FORM_SUBMIT ||
+                          mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) ||
                           mForm->CheckValidFormSubmission())) {
           // TODO: removing this code and have the submit event sent by the form
           // see bug 592124.
           // Hold a strong ref while dispatching
           nsRefPtr<nsHTMLFormElement> form(mForm);
           presShell->HandleDOMEventWithTarget(mForm, &event, &status);
         }
       }
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -364,16 +364,17 @@ nsHTMLFormElement::SetAttr(PRInt32 aName
 }
 
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, AcceptCharset, acceptcharset)
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Action, action)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLFormElement, Enctype, enctype,
                                 kFormDefaultEnctype->tag)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLFormElement, Method, method,
                                 kFormDefaultMethod->tag)
+NS_IMPL_BOOL_ATTR(nsHTMLFormElement, NoValidate, novalidate)
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Name, name)
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Target, target)
 
 NS_IMETHODIMP
 nsHTMLFormElement::GetMozActionUri(nsAString& aValue)
 {
   GetAttr(kNameSpaceID_None, nsGkAtoms::action, aValue);
   if (aValue.IsEmpty()) {
@@ -1657,16 +1658,19 @@ nsHTMLFormElement::CheckValidFormSubmiss
    * NS_INVALIDFORMSUBMIT_SUBJECT so it will prevent blocking form submission
    * if the browser does not have implemented a UI yet.
    *
    * TODO: the check for observer should be removed later when HTML5 Forms will
    * be spread enough and authors will assume forms can't be submitted when
    * invalid. See bug 587671.
    */
 
+  NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate),
+               "We shouldn't be there if novalidate is set!");
+
   // When .submit() is called aEvent = nsnull so we can rely on that to know if
   // we have to check the validity of the form.
   nsCOMPtr<nsIObserverService> service =
     mozilla::services::GetObserverService();
   if (!service) {
     NS_WARNING("No observer service available!");
     return true;
   }
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -273,16 +273,17 @@ public:
   PRBool GetValidity() const { return !mInvalidElementsCount; }
 
   /**
    * This method check the form validity and make invalid form elements send
    * invalid event if needed.
    *
    * @return Whether the form is valid.
    *
+   * @note Do not call this method if novalidate/formnovalidate is used.
    * @note This method might disappear with bug 592124, hopefuly.
    */
   bool CheckValidFormSubmission();
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   class RemoveElementRunnable;
   friend class RemoveElementRunnable;
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1635,17 +1635,19 @@ nsHTMLInputElement::MaybeSubmitForm(nsPr
   if (submitControl) {
     nsCOMPtr<nsIContent> submitContent(do_QueryInterface(submitControl));
     NS_ASSERTION(submitContent, "Form control not implementing nsIContent?!");
     // Fire the button's onclick handler and let the button handle
     // submitting the form.
     nsMouseEvent event(PR_TRUE, NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
     nsEventStatus status = nsEventStatus_eIgnore;
     shell->HandleDOMEventWithTarget(submitContent, &event, &status);
-  } else if (mForm->HasSingleTextControl() && mForm->CheckValidFormSubmission()) {
+  } else if (mForm->HasSingleTextControl() &&
+             (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) ||
+              mForm->CheckValidFormSubmission())) {
     // TODO: removing this code and have the submit event sent by the form,
     // bug 592124.
     // If there's only one text control, just submit the form
     // Hold strong ref across the event
     nsRefPtr<nsHTMLFormElement> form(mForm);
     nsFormEvent event(PR_TRUE, NS_FORM_SUBMIT);
     nsEventStatus status  = nsEventStatus_eIgnore;
     shell->HandleDOMEventWithTarget(mForm, &event, &status);
@@ -2428,16 +2430,17 @@ nsHTMLInputElement::PostHandleEvent(nsEv
               aVisitor.mPresContext->GetPresShell();
 
             // If |nsIPresShell::Destroy| has been called due to
             // handling the event the pres context will return a null
             // pres shell.  See bug 125624.
             // TODO: removing this code and have the submit event sent by the
             // form, see bug 592124.
             if (presShell && (event.message != NS_FORM_SUBMIT ||
+                              mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) ||
                               mForm->CheckValidFormSubmission())) {
               // Hold a strong ref while dispatching
               nsRefPtr<nsHTMLFormElement> form(mForm);
               presShell->HandleDOMEventWithTarget(mForm, &event, &status);
             }
           }
           break;
 
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -214,12 +214,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug593689.html \
 		test_bug583288-1.html \
 		test_bug583288-2.html \
 		test_bug583288-3.html \
 		583288_submit_server.sjs \
 		583288_redirect_server.sjs \
 		test_bug555840.html \
 		test_bug561636.html \
+		test_bug556013.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug556013.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=556013
+-->
+<head>
+  <title>Test for Bug 556013</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=556013">Mozilla Bug 556013</a>
+<p id="display"></p>
+<iframe style='width:50px; height: 50px;' name='t'></iframe>
+<div id="content">
+  <form target='t' action='data:text/html,' novalidate>
+    <input id='av' required>
+    <input id='a' type='submit'>
+  </form>
+  <form target='t' action='data:text/html,' novalidate>
+    <input id='bv' type='checkbox' required>
+    <button id='b' type='submit'></button>
+  </form>
+  <form target='t' action='data:text/html,' novalidate>
+    <input id='c' required>
+  </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 556013 **/
+
+var form = document.createElement('form');
+
+function checkNoValidateAttribute(aForm)
+{
+  ok("noValidate" in form, "noValidate should be a form element IDL attribute");
+
+  ok(!aForm.noValidate, "aForm novalidate attribute should be disabled");
+  is(aForm.getAttribute('novalidate'), null,
+    "aForm novalidate attribute should be disabled");
+
+  aForm.noValidate = true;
+  ok(aForm.noValidate, "aForm novalidate attribute should be enabled");
+  isnot(aForm.getAttribute('novalidate'), null,
+    "aForm novalidate attribute should be enabled");
+
+  aForm.removeAttribute('novalidate');
+  aForm.setAttribute('novalidate', '');
+  ok(aForm.noValidate, "aForm novalidate attribute should be enabled");
+  isnot(aForm.getAttribute('novalidate'), null,
+    "aForm novalidate attribute should be enabled");
+
+  aForm.removeAttribute('novalidate');
+  ok(!aForm.noValidate, "aForm novalidate attribute should be disabled");
+  is(aForm.getAttribute('novalidate'), null,
+    "aForm novalidate attribute should be disabled");
+}
+
+checkNoValidateAttribute(form);
+
+/**
+ * novalidate should prevent form validation, thus not blocking form submission.
+ *
+ * NOTE: if there is no invalidformsubmit observer, the form submission will
+ * never be blocked and this test might be a false-positive but that should not
+ * be a problem.
+ */
+document.forms[0].addEventListener("submit", function(aEvent) {
+  aEvent.target.removeEventListener("submit", arguments.callee, false);
+  ok(true, "novalidate has been correctly used for first form");
+  document.getElementById('b').click();
+}, false);
+
+document.forms[1].addEventListener("submit", function(aEvent) {
+  aEvent.target.removeEventListener("submit", arguments.callee, false);
+  ok(true, "novalidate has been correctly used for second form");
+  var c = document.getElementById('c');
+  c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
+}, false);
+
+document.forms[2].addEventListener("submit", function(aEvent) {
+  aEvent.target.removeEventListener("submit", arguments.callee, false);
+  ok(true, "novalidate has been correctly used for third form");
+  SimpleTest.executeSoon(SimpleTest.finish);
+}, false);
+
+/**
+ * We have to be sure invalid events are not send too.
+ * They should be sent before the submit event so we can just create a test
+ * failure if we got one. All of them should be catched if sent.
+ * At worst, we got random green which isn't harmful.
+ */
+function invalidHandling(aEvent)
+{
+  aEvent.target.removeEventListener("invalid", invalidHandling, false);
+  ok(false, "invalid event should not be sent");
+}
+
+document.getElementById('av').addEventListener("invalid", invalidHandling, false);
+document.getElementById('bv').addEventListener("invalid", invalidHandling, false);
+document.getElementById('c').addEventListener("invalid", invalidHandling, false);
+
+SimpleTest.waitForExplicitFinish();
+
+// This is going to call all the tests (with a chain reaction).
+SimpleTest.waitForFocus(function() {
+  document.getElementById('a').click();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/html/nsIDOMHTMLFormElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLFormElement.idl
@@ -42,25 +42,28 @@
 /**
  * The nsIDOMHTMLFormElement interface is the interface to a [X]HTML
  * form element.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-HTML/
  */
 
-[scriptable, uuid(083d2e20-a996-11df-94e2-0800200c9a66)]
+[scriptable, uuid(48db9517-3a85-4f6d-af5f-106ce6aadd7d)]
 interface nsIDOMHTMLFormElement : nsIDOMHTMLElement
 {
-  readonly attribute nsIDOMHTMLCollection elements;
-  readonly attribute long                 length;
            attribute DOMString            name;
            attribute DOMString            acceptCharset;
            attribute DOMString            action;
            attribute DOMString            enctype;
            attribute DOMString            method;
            attribute DOMString            target;
+           attribute boolean              noValidate;
+
+  readonly attribute nsIDOMHTMLCollection elements;
+  readonly attribute long                 length;
+
   void                      submit();
   void                      reset();
 
   // This property returns the resolved action URI.
   readonly attribute DOMString            mozActionUri;
 };