LayoutTests/ChangeLog

 12010-04-06 Kent Tamura <tkent@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Implement interactive validation for forms.
 6 https://bugs.webkit.org/show_bug.cgi?id=34930
 7
 8 Add tests for "invalid" event and interactive validation.
 9
 10 * fast/forms/checkValidity-cancel-expected.txt: Added.
 11 * fast/forms/checkValidity-cancel.html: Added.
 12 * fast/forms/interactive-validation-cancel-expected.txt: Added.
 13 * fast/forms/interactive-validation-cancel.html: Added.
 14 * fast/forms/interactive-validation-formnovalidate-expected.txt: Added.
 15 * fast/forms/interactive-validation-formnovalidate.html: Added.
 16 * fast/forms/interactive-validation-novalidate-expected.txt: Added.
 17 * fast/forms/interactive-validation-novalidate.html: Added.
 18 * fast/forms/interactive-validation-prevented-expected.txt: Added.
 19 * fast/forms/interactive-validation-prevented.html: Added.
 20 * fast/forms/interactive-validation-remove-node-in-handler-expected.txt: Added.
 21 * fast/forms/interactive-validation-remove-node-in-handler.html: Added.
 22 * fast/forms/script-tests/checkValidity-cancel.js: Added.
 23
1242010-04-06 Pavel Feldman <pfeldman@chromium.org>
225
326 Not reviewed: rebaselining Chromium layout test expectations.

LayoutTests/fast/forms/checkValidity-cancel-expected.txt

 1Tests for checkValidity() with invalid evnet canceling
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6"invalid" event is not canceled.
 7PASS input.addEventListener("invalid", nothingListener, false); !input.checkValidity() && invalidFired is true
 8PASS invalidFired = false; !form.checkValidity() && invalidFired is true
 9
 10"invalid" event is canceled.
 11PASS input.addEventListener("invalid", cancelListener, false); !input.checkValidity() && invalidFired is true
 12PASS invalidFired = false; form.checkValidity() && invalidFired is true
 13PASS successfullyParsed is true
 14
 15TEST COMPLETE
 16

LayoutTests/fast/forms/checkValidity-cancel.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<script src="script-tests/checkValidity-cancel.js"></script>
 11<script src="../../fast/js/resources/js-test-post.js"></script>
 12</body>
 13</html>

LayoutTests/fast/forms/interactive-validation-cancel-expected.txt

 1Test if the form is submitted when an "invalid" evnet for a control is canceled.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS The form should be submitted.
 7PASS location.search.indexOf("i0=") != -1 is true
 8TEST COMPLETE
 9

LayoutTests/fast/forms/interactive-validation-cancel.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<form id=f action="interactive-validation-cancel.html">
 11<input type=hidden name=submitted value="true">
 12<input name=i0 required id="i0">
 13<input type=submit id="s">
 14</form>
 15<script>
 16description('Test if the form is submitted when an "invalid" evnet for a control is canceled.');
 17
 18function cancel(event) {
 19 event.preventDefault();
 20}
 21
 22function startOrVerify() {
 23 var query = window.location.search;
 24 if (query.indexOf('submitted=true') != -1) {
 25 testPassed('The form should be submitted.');
 26 shouldBeTrue('location.search.indexOf("i0=") != -1');
 27 debug('TEST COMPLETE');
 28 if (window.layoutTestController)
 29 layoutTestController.notifyDone();
 30 } else {
 31 document.getElementById('i0').addEventListener('invalid', cancel, false);
 32 // HTMLFormElement::submit() skips validation. Use the submit button.
 33 document.getElementById('s').click();
 34 testFailed('The form was not submitted.');
 35 }
 36}
 37
 38if (window.layoutTestController)
 39 layoutTestController.waitUntilDone();
 40window.onload = startOrVerify;
 41</script>
 42</body>
 43</html>

LayoutTests/fast/forms/interactive-validation-formnovalidate-expected.txt

 1Test if the form is submitted with a submit button with formnovalidate.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS The form should not be submitted.
 7PASS location.search.indexOf("i0=") != -1 is true
 8TEST COMPLETE
 9

LayoutTests/fast/forms/interactive-validation-formnovalidate.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<form id=f action="interactive-validation-formnovalidate.html">
 11<input type=hidden name=submitted value="true">
 12<input name=i0 required id="i0">
 13<input type=submit id="s" formnovalidate>
 14</form>
 15<script>
 16description('Test if the form is submitted with a submit button with formnovalidate.');
 17
 18function startOrVerify() {
 19 var query = window.location.search;
 20 if (query.indexOf('submitted=true') != -1) {
 21 testPassed('The form should not be submitted.');
 22 shouldBeTrue('location.search.indexOf("i0=") != -1');
 23 debug('TEST COMPLETE');
 24 if (window.layoutTestController)
 25 layoutTestController.notifyDone();
 26 } else {
 27 // HTMLFormElement::submit() skips validation. Use the submit button.
 28 document.getElementById('s').click();
 29 testFailed('The form was not submitted.');
 30 }
 31}
 32
 33if (window.layoutTestController)
 34 layoutTestController.waitUntilDone();
 35window.onload = startOrVerify;
 36</script>
 37</body>
 38</html>

LayoutTests/fast/forms/interactive-validation-novalidate-expected.txt

 1Test if the form with novalidate is submitted.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS The form should not be submitted.
 7PASS location.search.indexOf("i0=") != -1 is true
 8TEST COMPLETE
 9

LayoutTests/fast/forms/interactive-validation-novalidate.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<form id=f action="interactive-validation-novalidate.html" novalidate>
 11<input type=hidden name=submitted value="true">
 12<input name=i0 required id="i0">
 13<input type=submit id="s">
 14</form>
 15<script>
 16description('Test if the form with novalidate is submitted.');
 17
 18function startOrVerify() {
 19 var query = window.location.search;
 20 if (query.indexOf('submitted=true') != -1) {
 21 testPassed('The form should not be submitted.');
 22 shouldBeTrue('location.search.indexOf("i0=") != -1');
 23 debug('TEST COMPLETE');
 24 if (window.layoutTestController)
 25 layoutTestController.notifyDone();
 26 } else {
 27 // HTMLFormElement::submit() skips validation. Use the submit button.
 28 document.getElementById('s').click();
 29 testFailed('The form was not submitted.');
 30 }
 31}
 32
 33if (window.layoutTestController)
 34 layoutTestController.waitUntilDone();
 35window.onload = startOrVerify;
 36</script>
 37</body>
 38</html>

LayoutTests/fast/forms/interactive-validation-prevented-expected.txt

 1Test if an invalid control prevents interactive form submission, and the first invalid focusable control gets focus.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS The form was not submitted
 7PASS document.activeElement is document.getElementById("i2")
 8TEST COMPLETE
 9

LayoutTests/fast/forms/interactive-validation-prevented.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<form id=f action="interactive-validation-prevented.html">
 11<input type=hidden name=submitted value=true>
 12<input name=i0 required id=i0 value="abc">
 13<input name=i1 required id=i1 style="display:none">
 14<input name=i2 required id=i2>
 15<input type=submit id=s>
 16</form>
 17<script>
 18description('Test if an invalid control prevents interactive form submission, and the first invalid focusable control gets focus.');
 19
 20function startOrVerify() {
 21 var query = window.location.search;
 22 if (query.indexOf('submitted=true') != -1) {
 23 testFailed('The form should not be submitted.');
 24 } else {
 25 // Force to render the form.
 26 document.getElementById("f").offsetWidth;
 27 // HTMLFormElement::submit() skips validation. Use the submit button.
 28 document.getElementById('s').click();
 29 testPassed('The form was not submitted');
 30 shouldBe('document.activeElement', 'document.getElementById("i2")');
 31 debug('TEST COMPLETE');
 32 }
 33
 34 if (window.layoutTestController)
 35 layoutTestController.notifyDone();
 36}
 37
 38if (window.layoutTestController)
 39 layoutTestController.waitUntilDone();
 40window.onload = startOrVerify;
 41</script>
 42</body>
 43</html>

LayoutTests/fast/forms/interactive-validation-remove-node-in-handler-expected.txt

 1Should not crash or have an assertion failure if a node was removed during an "invalid" event dispatching for the node.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6

LayoutTests/fast/forms/interactive-validation-remove-node-in-handler.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
 5<script src="../../fast/js/resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<form id=f action="interactive-validation-remove-node-in-handler.html">
 11<input type=hidden name=submitted value="true">
 12<input name=i0 required id="i0">
 13<input type=submit id="s">
 14</form>
 15<script>
 16description('Should not crash or have an assertion failure if a node was removed during an "invalid" event dispatching for the node.');
 17
 18function gc() {
 19 if (window.GCController)
 20 return GCController.collect();
 21 for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
 22 var s = new String("");
 23 }
 24}
 25
 26function handler(event) {
 27 var node = event.target;
 28 node.parentNode.removeChild(node);
 29 gc();
 30}
 31
 32function startOrVerify() {
 33 document.getElementById('i0').addEventListener('invalid', handler, false);
 34 var query = window.location.search;
 35 if (query.indexOf('submitted=true') != -1) {
 36 testPassed('The form was submitted.');
 37 debug('TEST COMPLETE');
 38 if (window.layoutTestController)
 39 layoutTestController.notifyDone();
 40 } else {
 41 // HTMLFormElement::submit() skips validation. Use the submit button.
 42 document.getElementById('s').click();
 43 // Should have no assertion failures.
 44 testFailed('The form should be submitted.');
 45 }
 46}
 47
 48if (window.layoutTestController)
 49 layoutTestController.waitUntilDone();
 50window.onload = startOrVerify;
 51</script>
 52</body>
 53</html>

LayoutTests/fast/forms/script-tests/checkValidity-cancel.js

 1description('Tests for checkValidity() with invalid evnet canceling');
 2
 3var parent = document.createElement('div');
 4document.body.appendChild(parent);
 5parent.innerHTML = '<form><input name=i required></form>';
 6var form = parent.firstChild;
 7var input = form.firstChild;
 8
 9debug('"invalid" event is not canceled.');
 10var invalidFired = false;
 11var nothingListener = {};
 12nothingListener.handleEvent = function(event) {
 13 invalidFired = true;
 14};
 15shouldBeTrue('input.addEventListener("invalid", nothingListener, false); !input.checkValidity() && invalidFired');
 16shouldBeTrue('invalidFired = false; !form.checkValidity() && invalidFired');
 17input.removeEventListener('invalid', nothingListener, false);
 18
 19debug('');
 20debug('"invalid" event is canceled.');
 21invalidFired = false;
 22var cancelListener = {};
 23cancelListener.handleEvent = function(event) {
 24 invalidFired = true;
 25 event.preventDefault();
 26};
 27// Even if 'invalid' is canceled, the input.checkValidity() result is still false.
 28shouldBeTrue('input.addEventListener("invalid", cancelListener, false); !input.checkValidity() && invalidFired');
 29// form.checkValidity() should be true.
 30shouldBeTrue('invalidFired = false; form.checkValidity() && invalidFired');
 31
 32var successfullyParsed = true;

WebCore/ChangeLog

 12010-04-06 Kent Tamura <tkent@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Implement interactive validation for forms.
 6 https://bugs.webkit.org/show_bug.cgi?id=34930
 7
 8 - checkValidity() supports unhandled invalid control list
 9 - prepareSubmit() prevents the submission if neither noValidate
 10 nor formNoValidate is specified, and focuses on the first invalid
 11 control of which "invalid" event is not canceled.
 12
 13 Tests: fast/forms/checkValidity-cancel.html
 14 fast/forms/interactive-validation-cancel.html
 15 fast/forms/interactive-validation-formnovalidate.html
 16 fast/forms/interactive-validation-novalidate.html
 17 fast/forms/interactive-validation-prevented.html
 18 fast/forms/interactive-validation-remove-node-in-handler.html
 19
 20 * html/HTMLFormControlElement.cpp:
 21 (WebCore::HTMLFormControlElement::checkValidity):
 22 If the control is invalid and the "invalid" is not canceled,
 23 push the control to the specified vector.
 24 * html/HTMLFormControlElement.h:
 25 * html/HTMLFormElement.cpp:
 26 (WebCore::HTMLFormElement::prepareSubmit):
 27 (WebCore::HTMLFormElement::checkValidity):
 28 * html/HTMLFormElement.h:
 29
1302010-04-06 Mattias Nissler <mnissler@chromium.org>
231
332 Reviewed by Pavel Feldman.

WebCore/html/HTMLFormControlElement.cpp

@@String HTMLFormControlElement::validationMessage()
331331 return validity()->validationMessage();
332332}
333333
334 bool HTMLFormControlElement::checkValidity()
 334bool HTMLFormControlElement::checkValidity(Vector<RefPtr<HTMLFormControlElement> >* unhandledInvalidControls)
335335{
336  if (willValidate() && !isValidFormControlElement()) {
337  dispatchEvent(Event::create(eventNames().invalidEvent, false, true));
338  return false;
339  }
340 
341  return true;
 336 if (!willValidate() || isValidFormControlElement())
 337 return true;
 338 // An event handler can deref this object.
 339 RefPtr<HTMLFormControlElement> protector(this);
 340 bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true));
 341 if (needsDefaultAction && unhandledInvalidControls && inDocument())
 342 unhandledInvalidControls->append(this);
 343 return false;
342344}
343345
344346bool HTMLFormControlElement::isValidFormControlElement()

WebCore/html/HTMLFormControlElement.h

@@public:
108108
109109 bool willValidate() const;
110110 String validationMessage();
111  bool checkValidity();
 111 bool checkValidity(Vector<RefPtr<HTMLFormControlElement> >* unhandledInvalidControls = 0);
112112 // This must be called when a validation constraint or control value is changed.
113113 void setNeedsValidityCheck();
114114 void setCustomValidity(const String&);

WebCore/html/HTMLFormElement.cpp

2727
2828#include "CSSHelper.h"
2929#include "DOMFormData.h"
 30#include "DOMWindow.h"
3031#include "Document.h"
3132#include "Event.h"
3233#include "EventNames.h"

@@bool HTMLFormElement::prepareSubmit(Event* event)
228229 m_insubmit = true;
229230 m_doingsubmit = false;
230231
 232 // Interactive validation must be done before dispatching the submit event.
 233 HTMLFormControlElement* submitElement = 0;
 234 Node* targetNode = event->target()->toNode();
 235 if (targetNode && targetNode->isElementNode()) {
 236 Element* targetElement = static_cast<Element*>(targetNode);
 237 if (targetElement->isFormControlElement())
 238 submitElement = static_cast<HTMLFormControlElement*>(targetElement);
 239 }
 240 if (!noValidate() && (!submitElement || !submitElement->formNoValidate())) {
 241 Vector<RefPtr<HTMLFormControlElement> > unhandledInvalidControls;
 242 // If the form has invalid controls, abort submission.
 243 if (!checkValidity(unhandledInvalidControls)) {
 244 RefPtr<HTMLFormElement> protector(this);
 245 // Focus on the first focusable control.
 246 for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
 247 HTMLFormControlElement* unhandled = unhandledInvalidControls[i].get();
 248 ASSERT(unhandled->hasOneRef());
 249 if (unhandled->isFocusable()) {
 250 unhandled->scrollIntoViewIfNeeded(false);
 251 // scrollIntoViewIfNeeded() dispatches events, so the state
 252 // of 'unhandled' might be changed.
 253 if (unhandled->isFocusable()) {
 254 unhandled->focus();
 255 break;
 256 }
 257 }
 258 }
 259 // Warn about all of unforcusable controls.
 260 Frame* frame = document()->frame();
 261 for (unsigned i = 0; frame && i < unhandledInvalidControls.size(); ++i) {
 262 HTMLFormControlElement* unhandled = unhandledInvalidControls[i].get();
 263 if (unhandled->isFocusable())
 264 continue;
 265 String message("An invalid form control with name='%name' is not focusable.");
 266 message.replace("%name", unhandled->name());
 267 frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, ErrorMessageLevel, message, 0, document()->url().string());
 268 }
 269 m_insubmit = false;
 270 return false;
 271 }
 272 }
 273
231274 if (dispatchEvent(Event::create(eventNames().submitEvent, true, true)) && !m_doingsubmit)
232275 m_doingsubmit = true;
233276

@@HTMLFormControlElement* HTMLFormElement::defaultButton() const
541584
542585bool HTMLFormElement::checkValidity()
543586{
544  // TODO: Check for unhandled invalid controls, see #27452 for tips.
 587 Vector<RefPtr<HTMLFormControlElement> > unhandled;
 588 return checkValidity(unhandled);
 589}
545590
546  bool hasOnlyValidControls = true;
 591bool HTMLFormElement::checkValidity(Vector<RefPtr<HTMLFormControlElement> >& unhandledInvalidControls)
 592{
 593 RefPtr<HTMLFormElement> protector(this);
547594 for (unsigned i = 0; i < formElements.size(); ++i) {
548595 HTMLFormControlElement* control = formElements[i];
549  if (!control->checkValidity())
550  hasOnlyValidControls = false;
 596 control->checkValidity(&unhandledInvalidControls);
551597 }
552 
553  return hasOnlyValidControls;
 598 return unhandledInvalidControls.isEmpty();
554599}
555600
556601PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias)

WebCore/html/HTMLFormElement.h

@@private:
137137 TextEncoding dataEncoding() const;
138138 PassRefPtr<FormData> createFormData();
139139 unsigned formElementIndex(HTMLFormControlElement*);
 140 bool checkValidity(Vector<RefPtr<HTMLFormControlElement> >&);
140141
141142 friend class HTMLFormCollection;
142143