Commit 6ded062c authored by tkent@chromium.org's avatar tkent@chromium.org

Framework to show form validation message for invalid controls

https://bugs.webkit.org/show_bug.cgi?id=31718

Reviewed by Dimitri Glazkov.

HTMLFormElement::validateInteractively() shows a validation message for
an invalid control by HTMLFormControlElement::updateVisibleValidationMessage(),
and the message is hidden when the invalid control looses focus, becomes
valid, detached, or the form is submitted again.

Introduce ValidationMessage class to manage visible form validation
message. It has no implementation to show/hide a message yet.

No new tests. New behavior is disabled by default, and is timing-dependent.

* Android.mk: Add ValidationMessage.
* CMakeLists.txt: ditto.
* GNUmakefile.am: ditto.
* WebCore.gypi: ditto.
* WebCore.pro: ditto.
* WebCore.vcproj/WebCore.vcproj: ditto.
* WebCore.xcodeproj/project.pbxproj: ditto.
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::detach): Hides a validation message.
(WebCore::HTMLFormControlElement::setNeedsWillValidateCheck):
   Hides a validation message if validation is not needed.
(WebCore::HTMLFormControlElement::updateVisibleValidationMessage):
(WebCore::HTMLFormControlElement::hideVisibleValidationMessage):
(WebCore::HTMLFormControlElement::setNeedsValidityCheck):
  Hides a validation message or updates the validation message.
(WebCore::HTMLFormControlElement::dispatchBlurEvent):
  Hides a validation message.
(WebCore::HTMLFormControlElement::visibleValidationMessage):
* html/HTMLFormControlElement.h:
* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::validateInteractively):
 - Hide existing validation messages before showing new message.
 - Show new validation message.
* html/ValidationMessage.cpp: Added.
(WebCore::ValidationMessage::ValidationMessage):
(WebCore::ValidationMessage::~ValidationMessage):
(WebCore::ValidationMessage::create):
(WebCore::ValidationMessage::setMessage):
(WebCore::ValidationMessage::hideMessage):
* html/ValidationMessage.h: Added.
(WebCore::ValidationMessage::message):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@71309 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d8eed362
......@@ -315,6 +315,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
html/TimeInputType.cpp \
html/TimeRanges.cpp \
html/URLInputType.cpp \
html/ValidationMessage.cpp \
html/ValidityState.cpp \
html/WeekInputType.cpp \
\
......
......@@ -1081,6 +1081,7 @@ SET(WebCore_SOURCES
html/TextInputType.cpp
html/TimeInputType.cpp
html/URLInputType.cpp
html/ValidationMessage.cpp
html/ValidityState.cpp
html/WeekInputType.cpp
html/canvas/CanvasGradient.cpp
......
2010-11-03 Kent Tamura <tkent@chromium.org>
Reviewed by Dimitri Glazkov.
Framework to show form validation message for invalid controls
https://bugs.webkit.org/show_bug.cgi?id=31718
HTMLFormElement::validateInteractively() shows a validation message for
an invalid control by HTMLFormControlElement::updateVisibleValidationMessage(),
and the message is hidden when the invalid control looses focus, becomes
valid, detached, or the form is submitted again.
Introduce ValidationMessage class to manage visible form validation
message. It has no implementation to show/hide a message yet.
No new tests. New behavior is disabled by default, and is timing-dependent.
* Android.mk: Add ValidationMessage.
* CMakeLists.txt: ditto.
* GNUmakefile.am: ditto.
* WebCore.gypi: ditto.
* WebCore.pro: ditto.
* WebCore.vcproj/WebCore.vcproj: ditto.
* WebCore.xcodeproj/project.pbxproj: ditto.
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::detach): Hides a validation message.
(WebCore::HTMLFormControlElement::setNeedsWillValidateCheck):
Hides a validation message if validation is not needed.
(WebCore::HTMLFormControlElement::updateVisibleValidationMessage):
(WebCore::HTMLFormControlElement::hideVisibleValidationMessage):
(WebCore::HTMLFormControlElement::setNeedsValidityCheck):
Hides a validation message or updates the validation message.
(WebCore::HTMLFormControlElement::dispatchBlurEvent):
Hides a validation message.
(WebCore::HTMLFormControlElement::visibleValidationMessage):
* html/HTMLFormControlElement.h:
* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::validateInteractively):
- Hide existing validation messages before showing new message.
- Show new validation message.
* html/ValidationMessage.cpp: Added.
(WebCore::ValidationMessage::ValidationMessage):
(WebCore::ValidationMessage::~ValidationMessage):
(WebCore::ValidationMessage::create):
(WebCore::ValidationMessage::setMessage):
(WebCore::ValidationMessage::hideMessage):
* html/ValidationMessage.h: Added.
(WebCore::ValidationMessage::message):
2010-11-03 Patrick Gansterer <paroga@webkit.org>
Reviewed by Adam Roben.
......@@ -1785,6 +1785,8 @@ webcore_sources += \
WebCore/html/TimeRanges.h \
WebCore/html/URLInputType.cpp \
WebCore/html/URLInputType.h \
WebCore/html/ValidationMessage.cpp \
WebCore/html/ValidationMessage.h \
WebCore/html/ValidityState.cpp \
WebCore/html/ValidityState.h \
WebCore/html/VoidCallback.h \
......
......@@ -1822,6 +1822,8 @@
'html/TimeRanges.h',
'html/URLInputType.cpp',
'html/URLInputType.h',
'html/ValidationMessage.cpp',
'html/ValidationMessage.h',
'html/ValidityState.cpp',
'html/ValidityState.h',
'html/VoidCallback.h',
......
......@@ -973,6 +973,7 @@ SOURCES += \
html/TextInputType.cpp \
html/TimeInputType.cpp \
html/URLInputType.cpp \
html/ValidationMessage.cpp \
html/ValidityState.cpp \
html/WeekInputType.cpp \
html/canvas/CanvasGradient.cpp \
......
......@@ -53503,6 +53503,14 @@
RelativePath="..\html\URLInputType.h"
>
</File>
<File
RelativePath="..\html\ValidationMessage.cpp"
>
</File>
<File
RelativePath="..\html\ValidationMessage.h"
>
</File>
<File
RelativePath="..\html\ValidityState.cpp"
>
......@@ -5551,6 +5551,8 @@
F55B3DE01251F12D003EF269 /* WeekInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = F55B3DAC1251F12D003EF269 /* WeekInputType.h */; };
F59C95FF1255B23F000623C0 /* BaseDateAndTimeInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F59C95FD1255B23F000623C0 /* BaseDateAndTimeInputType.cpp */; };
F59C96001255B23F000623C0 /* BaseDateAndTimeInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = F59C95FE1255B23F000623C0 /* BaseDateAndTimeInputType.h */; };
F5A154271279534D00D0B0C0 /* ValidationMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A154251279534D00D0B0C0 /* ValidationMessage.cpp */; };
F5A154281279534D00D0B0C0 /* ValidationMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = F5A154261279534D00D0B0C0 /* ValidationMessage.h */; };
F5C041DA0FFCA7CE00839D4A /* HTMLDataListElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5C041D70FFCA7CE00839D4A /* HTMLDataListElement.cpp */; };
F5C041DB0FFCA7CE00839D4A /* HTMLDataListElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C041D80FFCA7CE00839D4A /* HTMLDataListElement.h */; };
F5C041E30FFCA96D00839D4A /* DOMHTMLDataListElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C041DE0FFCA96D00839D4A /* DOMHTMLDataListElement.h */; };
......@@ -11636,6 +11638,8 @@
F587869902DE3B8601EA4122 /* DeprecatedPtrList.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = DeprecatedPtrList.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
F59C95FD1255B23F000623C0 /* BaseDateAndTimeInputType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseDateAndTimeInputType.cpp; sourceTree = "<group>"; };
F59C95FE1255B23F000623C0 /* BaseDateAndTimeInputType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseDateAndTimeInputType.h; sourceTree = "<group>"; };
F5A154251279534D00D0B0C0 /* ValidationMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ValidationMessage.cpp; sourceTree = "<group>"; };
F5A154261279534D00D0B0C0 /* ValidationMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValidationMessage.h; sourceTree = "<group>"; };
F5C041D70FFCA7CE00839D4A /* HTMLDataListElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLDataListElement.cpp; sourceTree = "<group>"; };
F5C041D80FFCA7CE00839D4A /* HTMLDataListElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDataListElement.h; sourceTree = "<group>"; };
F5C041D90FFCA7CE00839D4A /* HTMLDataListElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLDataListElement.idl; sourceTree = "<group>"; };
......@@ -14988,6 +14992,8 @@
E446139F0CD6331000FADA75 /* TimeRanges.idl */,
F55B3DA91251F12D003EF269 /* URLInputType.cpp */,
F55B3DAA1251F12D003EF269 /* URLInputType.h */,
F5A154251279534D00D0B0C0 /* ValidationMessage.cpp */,
F5A154261279534D00D0B0C0 /* ValidationMessage.h */,
15C7708B100D3C6A005BA267 /* ValidityState.cpp */,
15C7708A100D3C6A005BA267 /* ValidityState.h */,
15C77089100D3C6A005BA267 /* ValidityState.idl */,
......@@ -21122,6 +21128,7 @@
BC8BF15A1058141800A40A07 /* UserStyleSheetTypes.h in Headers */,
BCDF317C11F8D683003C5BF8 /* UserTypingGestureIndicator.h in Headers */,
2E3BBF081162DA1100B9409A /* UUID.h in Headers */,
F5A154281279534D00D0B0C0 /* ValidationMessage.h in Headers */,
15C7708D100D3C6B005BA267 /* ValidityState.h in Headers */,
CEF418CF1179678C009D112C /* ViewportArguments.h in Headers */,
93309E1E099E64920056E581 /* visible_units.h in Headers */,
......@@ -23701,6 +23708,7 @@
2542F4DA1166C25A00E89A86 /* UserGestureIndicator.cpp in Sources */,
BCDF317B11F8D683003C5BF8 /* UserTypingGestureIndicator.cpp in Sources */,
2E3BBF071162DA1100B9409A /* UUID.cpp in Sources */,
F5A154271279534D00D0B0C0 /* ValidationMessage.cpp in Sources */,
15C7708E100D3C6B005BA267 /* ValidityState.cpp in Sources */,
CEF418CE1179678C009D112C /* ViewportArguments.cpp in Sources */,
93309E1D099E64920056E581 /* visible_units.cpp in Sources */,
......@@ -45,6 +45,7 @@
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "ScriptEventListener.h"
#include "ValidationMessage.h"
#include "ValidityState.h"
#include <limits>
#include <wtf/Vector.h>
......@@ -77,6 +78,12 @@ HTMLFormControlElement::~HTMLFormControlElement()
m_form->removeFormElement(this);
}
void HTMLFormControlElement::detach()
{
hideVisibleValidationMessage();
HTMLElement::detach();
}
bool HTMLFormControlElement::formNoValidate() const
{
return !getAttribute(formnovalidateAttr).isNull();
......@@ -301,7 +308,8 @@ void HTMLFormControlElement::setNeedsWillValidateCheck()
m_willValidateInitialized = true;
m_willValidate = newWillValidate;
setNeedsStyleRecalc();
// FIXME: Show/hide a validation message.
if (!m_willValidate)
hideVisibleValidationMessage();
}
String HTMLFormControlElement::validationMessage()
......@@ -309,6 +317,42 @@ String HTMLFormControlElement::validationMessage()
return validity()->validationMessage();
}
void HTMLFormControlElement::updateVisibleValidationMessage()
{
Page* page = document()->page();
if (!page)
return;
String message;
if (renderer() && willValidate()) {
message = validationMessage().stripWhiteSpace();
// HTML5 specification doesn't ask UA to show the title attribute value
// with the validationMessage. However, this behavior is same as Opera
// and the specification describes such behavior as an example.
const AtomicString& title = getAttribute(titleAttr);
if (!message.isEmpty() && !title.isEmpty()) {
message.append('\n');
message.append(title);
}
}
if (!m_validationMessage) {
m_validationMessage = ValidationMessage::create(this);
m_validationMessage->setMessage(message);
} else if (message.isEmpty())
hideVisibleValidationMessage();
else if (m_validationMessage->message() != message)
m_validationMessage->setMessage(message);
}
void HTMLFormControlElement::hideVisibleValidationMessage()
{
m_validationMessage = 0;
}
String HTMLFormControlElement::visibleValidationMessage() const
{
return m_validationMessage ? m_validationMessage->message() : String();
}
bool HTMLFormControlElement::checkValidity(Vector<RefPtr<HTMLFormControlElement> >* unhandledInvalidControls)
{
if (!willValidate() || isValidFormControlElement())
......@@ -338,7 +382,13 @@ void HTMLFormControlElement::setNeedsValidityCheck()
setNeedsStyleRecalc();
}
m_isValid = newIsValid;
// FIXME: show/hide a validation message.
// Updates only if this control already has a validtion message.
if (!visibleValidationMessage().isEmpty()) {
// Calls updateVisibleValidationMessage() even if m_isValid is not
// changed because a validation message can be chagned.
updateVisibleValidationMessage();
}
}
void HTMLFormControlElement::setCustomValidity(const String& error)
......@@ -360,6 +410,7 @@ void HTMLFormControlElement::dispatchBlurEvent()
document()->page()->chrome()->client()->formDidBlur(this);
HTMLElement::dispatchBlurEvent();
hideVisibleValidationMessage();
}
HTMLFormElement* HTMLFormControlElement::virtualForm() const
......
......@@ -31,6 +31,7 @@ namespace WebCore {
class FormDataList;
class HTMLFormElement;
class RenderTextControl;
class ValidationMessage;
class ValidityState;
class VisibleSelection;
......@@ -84,6 +85,8 @@ public:
virtual bool willValidate() const;
String validationMessage();
void updateVisibleValidationMessage();
void hideVisibleValidationMessage();
bool checkValidity(Vector<RefPtr<HTMLFormControlElement> >* unhandledInvalidControls = 0);
// This must be called when a validation constraint or control value is changed.
void setNeedsValidityCheck();
......@@ -111,6 +114,7 @@ protected:
virtual void dispatchFocusEvent();
virtual void dispatchBlurEvent();
virtual void detach();
void removeFromForm();
......@@ -131,9 +135,11 @@ private:
virtual HTMLFormElement* virtualForm() const;
virtual bool isDefaultButtonForForm() const;
virtual bool isValidFormControlElement();
String visibleValidationMessage() const;
HTMLFormElement* m_form;
OwnPtr<ValidityState> m_validityState;
OwnPtr<ValidationMessage> m_validationMessage;
bool m_disabled : 1;
bool m_readOnly : 1;
bool m_required : 1;
......
......@@ -205,6 +205,9 @@ bool HTMLFormElement::validateInteractively(Event* event)
if (submitElement && submitElement->formNoValidate())
return true;
for (unsigned i = 0; i < m_associatedElements.size(); ++i)
m_associatedElements[i]->hideVisibleValidationMessage();
Vector<RefPtr<HTMLFormControlElement> > unhandledInvalidControls;
collectUnhandledInvalidControls(unhandledInvalidControls);
if (unhandledInvalidControls.isEmpty())
......@@ -212,7 +215,7 @@ bool HTMLFormElement::validateInteractively(Event* event)
// If the form has invalid controls, abort submission.
RefPtr<HTMLFormElement> protector(this);
// Focus on the first focusable control.
// Focus on the first focusable control and show a validation message.
for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
HTMLFormControlElement* unhandled = unhandledInvalidControls[i].get();
if (unhandled->isFocusable() && unhandled->inDocument()) {
......@@ -223,6 +226,7 @@ bool HTMLFormElement::validateInteractively(Event* event)
// moved to another document.
if (unhandled->isFocusable() && unhandled->inDocument() && originalDocument == unhandled->document()) {
unhandled->focus();
unhandled->updateVisibleValidationMessage();
break;
}
}
......
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ValidationMessage.h"
#include <wtf/PassOwnPtr.h>
namespace WebCore {
ALWAYS_INLINE ValidationMessage::ValidationMessage(HTMLFormControlElement* element)
: m_element(element)
{
}
ValidationMessage::~ValidationMessage()
{
hideMessage();
}
PassOwnPtr<ValidationMessage> ValidationMessage::create(HTMLFormControlElement* element)
{
return adoptPtr(new ValidationMessage(element));
}
void ValidationMessage::setMessage(const String& message)
{
// FIXME: Construct validation message UI if m_message is empty.
m_message = message;
m_timer.set(new Timer<ValidationMessage>(this, &ValidationMessage::hideMessage));
m_timer->startOneShot(6.0); // FIXME: should be <message length> * something.
}
void ValidationMessage::hideMessage(Timer<ValidationMessage>*)
{
// FIXME: Implement.
m_message = String();
}
} // namespace WebCore
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ValidationMessage_h
#define ValidationMessage_h
#include "Timer.h"
#include <wtf/Noncopyable.h>
#include <wtf/OwnPtr.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
class HTMLFormControlElement;
class ValidationMessage : public Noncopyable {
public:
static PassOwnPtr<ValidationMessage> create(HTMLFormControlElement*);
~ValidationMessage();
String message() const { return m_message; }
void setMessage(const String&);
private:
ValidationMessage(HTMLFormControlElement*);
void hideMessage(Timer<ValidationMessage>* = 0);
HTMLFormControlElement* m_element;
String m_message;
OwnPtr<Timer<ValidationMessage> > m_timer;
};
} // namespace WebCore
#endif // ValidationMessage_h
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment