Bug 31718

Summary: Framework to show form validation message for invalid controls
Product: WebKit Reporter: Kent Tamura <tkent>
Component: FormsAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: adele, aroben, darin, dglazkov, eric, gustavo, michelangelo, sam, webkit-ews, webkit.review.bot, xan.lopez
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Bug Depends on: 34924    
Bug Blocks: 28649, 48980    
Attachments:
Description Flags
Proposed patch
none
Screenshot of this implementation on Safari/Windows
none
Proposed patch (rev.2)
none
Proposed patch (rev.3)
none
Proposed patch (rev.4)
none
Patch (rev.5; remove Windows impl.)
none
Patch (rev.6; build fix)
none
Patch (rev.7; build fix 2)
none
Patch (rev.8; build fix 3)
none
Patch (rev.9)
none
Patch rev.10
none
Patch rev.11 (rebased)
none
Patch 12 (rebased and simplified)
none
Patch 13 (more simplification. No Chrome changes) none

Description Kent Tamura 2009-11-20 04:16:24 PST
- Adds methods to show/hide validation messages to ChromeClient
- A form control calls them if it has focus and it is invalid.
Comment 1 Kent Tamura 2010-02-04 08:11:22 PST
Created attachment 48145 [details]
Proposed patch
Comment 2 Darin Adler 2010-02-04 11:31:17 PST
Comment on attachment 48145 [details]
Proposed patch

> +    void mayShowValidationMessage();
> +    void mayHideValidationMessage();

A single update function would be better than these two.

> +    // A validation message for this element is shown.
> +    bool m_validationMessageShown : 1;

For boolean data members we normally use a name that fits the sentence "form control element <xxx>".

But I think what you may need here is a copy of the message that was last sent to the client, rather than just a boolean.

> +        virtual void showFormValidationMessage(const Node*, const IntRect&, FrameView*, const String&) { };
> +        virtual void hideFormValidationMessage(const Node*) { };

Should be HTMLFormControlElement* instead of Node*. Also no good reason to use const Node* since these objects are in a tree and const pointers are not all that useful.

No semicolons after "{ }".

I don’t understand the value of the IntRect and FrameView arguments. What rectangle? What guarantees that a form element is rectangular, given that it can be transformed arbitrarily? Why can’t the caller get things like the rectangle and view from the form control element using the usual APIs instead of having them explicitly passed? Maybe this is best for the current usage in Chromium but I don’t think it’s quite right. I would leave all that complexity out of the chrome client hookup.

Further, if you pass a rectangle and frame view, then who is responsible for calling with a new rectangle if the page changes? It's clearly wrong to send a rectangle for one moment in time without providing for tracking over time, and it's not useful to supply the FrameView since it can easily be derived.

If the validation interface is going to be shown overlaying the web page, I’d like to see more support for it in WebKit itself. It’s unpleasant to have to redo this feature for each platform separately. It’s good to have flexibility but ideally we would share as much common code as necessary.

I’m not sure it makes sense to have separate hide/show calls for this -- this should instead just be a single set function. All the logic would end up simpler and we can use either a null or empty string to mean "hide".

Is the client guaranteed to receive a call for a form element if it’s removed from the document? What about when a document moves out of the view or back into the view during navigation? I think that sending the call at destructor time is clearly bad, because these are reference-counted times and the timing of the call to the destructor is unpredictable. More thought needs to go into this aspect of the design.

How can we test this? Can you hook this up to DumpRenderTree? I don’t think we should land the ChromeClient level alone without the upper level hookup that allows testing.

What prevents the same validation message from being repeatedly sent to the client?

There's a lot to think about here.
Comment 3 Kent Tamura 2010-02-08 23:34:12 PST
(In reply to comment #2)
> (From update of attachment 48145 [details])
> > +    void mayShowValidationMessage();
> > +    void mayHideValidationMessage();
> 
> A single update function would be better than these two.

ok, I'll merge them into updateValidationMessage(const String&).

> > +    // A validation message for this element is shown.
> > +    bool m_validationMessageShown : 1;
> 
> For boolean data members we normally use a name that fits the sentence "form
> control element <xxx>".
> 
> But I think what you may need here is a copy of the message that was last sent
> to the client, rather than just a boolean.
> 
> > +        virtual void showFormValidationMessage(const Node*, const IntRect&, FrameView*, const String&) { };
> > +        virtual void hideFormValidationMessage(const Node*) { };
> 
> Should be HTMLFormControlElement* instead of Node*. Also no good reason to use
> const Node* since these objects are in a tree and const pointers are not all
> that useful.
> 
> No semicolons after "{ }".
> 
> I don’t understand the value of the IntRect and FrameView arguments. What
> rectangle? What guarantees that a form element is rectangular, given that it
> can be transformed arbitrarily? Why can’t the caller get things like the
> rectangle and view from the form control element using the usual APIs instead
> of having them explicitly passed? Maybe this is best for the current usage in
> Chromium but I don’t think it’s quite right. I would leave all that complexity
> out of the chrome client hookup.

Ah, I didn't take care of transformation. You're right. We shouldn't provide a rectangle here.

> 
> Further, if you pass a rectangle and frame view, then who is responsible for
> calling with a new rectangle if the page changes? It's clearly wrong to send a
> rectangle for one moment in time without providing for tracking over time, and
> it's not useful to supply the FrameView since it can easily be derived.
> 
> If the validation interface is going to be shown overlaying the web page, I’d
> like to see more support for it in WebKit itself. It’s unpleasant to have to
> redo this feature for each platform separately. It’s good to have flexibility
> but ideally we would share as much common code as necessary.

I see. However I don't have any idea how to support.
I thought validation messages were similar to tooltips or history completions. They don't have common support code.

> 
> I’m not sure it makes sense to have separate hide/show calls for this -- this
> should instead just be a single set function. All the logic would end up
> simpler and we can use either a null or empty string to mean "hide".
> 
> Is the client guaranteed to receive a call for a form element if it’s removed
> from the document? What about when a document moves out of the view or back
> into the view during navigation? I think that sending the call at destructor
> time is clearly bad, because these are reference-counted times and the timing
> of the call to the destructor is unpredictable. More thought needs to go into
> this aspect of the design.

I assumed ChromeClient implementations were responsible for them.  That is to say, the implementations may hide validation messages even if hideFormValidationMessage() is not called.


> How can we test this? Can you hook this up to DumpRenderTree? I don’t think we
> should land the ChromeClient level alone without the upper level hookup that
> allows testing.

Hmm, how about introducing layoutTestController.visibleValidationMessage(element) ?

> What prevents the same validation message from being repeatedly sent to the
> client?

It never happens because of the code in HTMLFormControlElement.cpp.  If HTMLFormControlElement has a visible message string as you advised, we can double-check it.

BTW, I suppose a validation message is shown as "Balloon Tooltip" in Windows, and couldn't find such UI control for Mac. Do you have any idea on Mac UI?
http://msdn.microsoft.com/en-us/library/bb760252(VS.85).aspx


I'll update the patch later (a few days or more).
Comment 4 Darin Adler 2010-02-09 10:40:27 PST
(In reply to comment #3)
> > Further, if you pass a rectangle and frame view, then who is responsible for
> > calling with a new rectangle if the page changes? It's clearly wrong to send a
> > rectangle for one moment in time without providing for tracking over time, and
> > it's not useful to supply the FrameView since it can easily be derived.
> > 
> > If the validation interface is going to be shown overlaying the web page, I’d
> > like to see more support for it in WebKit itself. It’s unpleasant to have to
> > redo this feature for each platform separately. It’s good to have flexibility
> > but ideally we would share as much common code as necessary.
> 
> I see. However I don't have any idea how to support.
> I thought validation messages were similar to tooltips or history completions.
> They don't have common support code.

Tooltips are indeed updated as elements move on the page. I’m not sure about completion, but I believe they also update.

Neither has an API that involves passing a rectangle to the ChromeClient, so I don’t think these are similar enough.

Imagine a page where there is a zip code field that appears only if the country is U.S. You enter some invalid data below and then the user changes the country to non-U.S.. So the invalid field moves up on the page and the rectangle that was passed to the ChromeClient is now invalid. How would you suggest this work?

> > I’m not sure it makes sense to have separate hide/show calls for this -- this
> > should instead just be a single set function. All the logic would end up
> > simpler and we can use either a null or empty string to mean "hide".
> > 
> > Is the client guaranteed to receive a call for a form element if it’s removed
> > from the document? What about when a document moves out of the view or back
> > into the view during navigation? I think that sending the call at destructor
> > time is clearly bad, because these are reference-counted times and the timing
> > of the call to the destructor is unpredictable. More thought needs to go into
> > this aspect of the design.
> 
> I assumed ChromeClient implementations were responsible for them.  That is to
> say, the implementations may hide validation messages even if
> hideFormValidationMessage() is not called.

I think we want most of the logic to be in the engine. The more code that client applications need, the more we’ll have bugs and an unequal playing field between clients. We want WebKit to be as easy to use as possible. The policy comes from the client, but as much of the sensible behavior as possible should be built into WebKit.

Specifically, simple cases like elements going away should not require extra explicit code in every client.

> > How can we test this? Can you hook this up to DumpRenderTree? I don’t think we
> > should land the ChromeClient level alone without the upper level hookup that
> > allows testing.
> 
> Hmm, how about introducing
> layoutTestController.visibleValidationMessage(element) ?

> BTW, I suppose a validation message is shown as "Balloon Tooltip" in Windows,
> and couldn't find such UI control for Mac. Do you have any idea on Mac UI?

The very notion of a one size fits all “validation failed” UI seems a bit quaint and naive to me.

But I suggest looking for interface in Mac OS X UI that implement this type of thing. Take a Mac and poke around at the system until you can find something. I believe there is an example like this when you install Mac OS X and set up your initial account name and password. Tap some other Mac OS X experts to find some examples and then we can make an informed decision.
Comment 5 Kent Tamura 2010-02-18 08:32:05 PST
I had some investigation.

* Tooltips for title= attributes
 ChromeClient::setTooltip() doesn't pass location information. It just shows the specified string as a tooltip, and disappears a few seconds later. A tooltip appears at the mouse pointer position initially, and never moves.

* History completion popup in Chromium
 A history completion popup appears on the focused element, and doesn't move even if the focused element moves.  It is implemented at outside of WebCore.

* When should we hide a validation message?
 Node::detach().  We should not show validation messages for a node with no renderer node.

* Keep track of the target element position
 To hook layout() or paint() seems costly. I think polling the position periodically is better.
Comment 6 Kent Tamura 2010-02-25 07:51:55 PST
The new patch is ready. I'll upload it after Bug#34924 is landed.
Comment 7 Kent Tamura 2010-03-25 17:23:45 PDT
Created attachment 51697 [details]
Screenshot of this implementation on Safari/Windows
Comment 8 Kent Tamura 2010-03-25 17:54:10 PDT
Created attachment 51702 [details]
Proposed patch (rev.2)
Comment 9 Kent Tamura 2010-03-25 22:27:50 PDT
(In reply to comment #8)
> Created an attachment (id=51702) [details]
> Proposed patch (rev.2)

Notable differences from the first patch are:
 - Introduce layoutTestController.visibleValidationMessageForElementById()

 - WebCore::Chrome manages timing of showing / hiding validation messages
  A message is hidden 5 seconds later, etc.

 - Has a Windows implementation
  No Mac implementation.  It would be another patch.
Comment 10 Sam Weinig 2010-03-28 19:15:05 PDT
Comment on attachment 51702 [details]
Proposed patch (rev.2)

> +    // Returns the top-left, top-right, bottom-left, bottom-right corner
> +    // locations with transformations. This returns an empty vector if the
> +    // element has no renderer.
> +    // Suppose that 'points' is a result of getTransformedCorners(), you can
> +    // access each of corners with symbolic indexes:
> +    //   points[TopLeftCorner]
> +    //   points[TopRightCorner]
> +    //   points[BottomLeftCorner]
> +    //   points[bottomRightCorner]
> +    virtual Vector<FloatPoint> getTransformedCorners() const;
> +    enum { // Indexes for the result of getTransformedCorners().
> +        TopLeftCorner = 0,
> +        TopRightCorner,
> +        BottomLeftCorner,
> +        BottomRightCorner
> +    };

Why doesn't this just a FloatQuad?
Comment 11 Sam Weinig 2010-03-28 19:20:51 PDT
 Why doesn't this just *use* a FloatQuad?
Comment 12 Kent Tamura 2010-03-28 19:26:21 PDT
(In reply to comment #11)
>  Why doesn't this just *use* a FloatQuad?

Oh, I haven't known FloatQuad.  We should use it.  I'll update the patch.
Comment 13 Kent Tamura 2010-04-01 04:50:56 PDT
Created attachment 52284 [details]
Proposed patch (rev.3)
Comment 14 Kent Tamura 2010-04-01 04:52:54 PDT
(In reply to comment #13)
> Created an attachment (id=52284) [details]
> Proposed patch (rev.3)

- Use FloatQuad
- Replace unnecessary PassRefPtr<HTMLFormControlElement> with HTMLFormControlElement*
Comment 15 Kent Tamura 2010-04-01 09:07:54 PDT
Created attachment 52302 [details]
Proposed patch (rev.4)
Comment 16 Kent Tamura 2010-04-01 09:08:48 PDT
(In reply to comment #15)
> Created an attachment (id=52302) [details]
> Proposed patch (rev.4)

This improved comments and ChangeLog.
Comment 17 Kent Tamura 2010-05-03 09:03:25 PDT
The latest patch makes some conflicts with the current WebKit source. I'll resolve them when I update the patch or I commit it.
Comment 18 Kent Tamura 2010-05-24 09:41:04 PDT
Could anyone review the patch please?
If the patch is too large, I'll split it into some pieces.
Comment 19 Kent Tamura 2010-06-09 08:44:17 PDT
Created attachment 58248 [details]
Patch (rev.5; remove Windows impl.)
Comment 20 Early Warning System Bot 2010-06-09 08:51:27 PDT
Attachment 58248 [details] did not build on qt:
Build output: http://webkit-commit-queue.appspot.com/results/3193098
Comment 21 Eric Seidel (no email) 2010-06-09 08:52:30 PDT
Attachment 58248 [details] did not build on mac:
Build output: http://webkit-commit-queue.appspot.com/results/3195088
Comment 22 WebKit Review Bot 2010-06-09 08:56:42 PDT
Attachment 58248 [details] did not build on gtk:
Build output: http://webkit-commit-queue.appspot.com/results/3246089
Comment 23 WebKit Review Bot 2010-06-09 09:05:38 PDT
Attachment 58248 [details] did not build on chromium:
Build output: http://webkit-commit-queue.appspot.com/results/3235106
Comment 24 Kent Tamura 2010-06-09 09:14:36 PDT
Created attachment 58252 [details]
Patch (rev.6; build fix)
Comment 25 Early Warning System Bot 2010-06-09 09:26:18 PDT
Attachment 58252 [details] did not build on qt:
Build output: http://webkit-commit-queue.appspot.com/results/3234074
Comment 26 WebKit Review Bot 2010-06-09 10:02:07 PDT
Attachment 58252 [details] did not build on chromium:
Build output: http://webkit-commit-queue.appspot.com/results/3212079
Comment 27 Kent Tamura 2010-06-09 10:08:38 PDT
Created attachment 58254 [details]
Patch (rev.7; build fix 2)
Comment 28 Kent Tamura 2010-06-09 10:20:07 PDT
I removed Windows balloon implementation and ContainerNode::getTransformedCorners() from the patch to minimize the size.
Comment 29 WebKit Review Bot 2010-06-09 11:42:41 PDT
Attachment 58254 [details] did not build on win:
Build output: http://webkit-commit-queue.appspot.com/results/3189092
Comment 30 Kent Tamura 2010-06-09 17:19:47 PDT
Created attachment 58312 [details]
Patch (rev.8; build fix 3)
Comment 31 Kent Tamura 2010-06-10 08:52:15 PDT
Created attachment 58375 [details]
Patch (rev.9)
Comment 32 Kent Tamura 2010-07-22 20:57:25 PDT
Reviewers, could you review the patch please?
Comment 33 Kent Tamura 2010-09-21 18:02:27 PDT
FYI: Mozilla completed the implementation of this feature for Firefox.
https://bugzilla.mozilla.org/show_bug.cgi?id=561636
Comment 34 Adam Barth 2010-10-10 11:21:10 PDT
Comment on attachment 58375 [details]
Patch (rev.9)

View in context: https://bugs.webkit.org/attachment.cgi?id=58375&action=review

r- for crash.

> WebCore/html/HTMLFormControlElement.cpp:360
> +    page->chrome()->updateFormValidationMessage(this, message);

Doesn't this crash if page is null?
Comment 35 Kent Tamura 2010-10-13 02:52:26 PDT
Thank you for reviewing an ancient patch.

(In reply to comment #34)
> > WebCore/html/HTMLFormControlElement.cpp:360
> > +    page->chrome()->updateFormValidationMessage(this, message);
> 
> Doesn't this crash if page is null?

It can crash though I don't know when page() returns NULL.
I'll update the patch.
Comment 36 Kent Tamura 2010-10-13 02:54:48 PDT
Created attachment 70593 [details]
Patch rev.10

Fix null-dereference of page()
Comment 37 Kent Tamura 2010-10-13 02:57:13 PDT
(In reply to comment #36)
> Created an attachment (id=70593) [details]
> Patch rev.10
> 
> Fix null-dereference of page()

I removed the ChromeClient change which the prior patches had because I'm going to implement the message bubble with shadow DOM.
Comment 38 Kent Tamura 2010-10-13 03:32:54 PDT
Created attachment 70595 [details]
Patch rev.11 (rebased)
Comment 39 Kent Tamura 2010-10-25 03:41:46 PDT
Created attachment 71726 [details]
Patch 12 (rebased and simplified)
Comment 40 Dimitri Glazkov (Google) 2010-10-26 14:04:52 PDT
(In reply to comment #39)
> Created an attachment (id=71726) [details]
> Patch 12 (rebased and simplified)

Story for stepping into the middle of the story here. I thought you mentioned using shadow DOM in one of the previous comments, but this patch still uses the same page->chrome->foo machinery as the others.

Also, why does Chrome.cpp code is doing so much work? Shouldn't it be basically just a shim to shuttle information between browser and renderer?
Comment 41 Kent Tamura 2010-10-27 03:34:20 PDT
(In reply to comment #40)
> (In reply to comment #39)
> > Created an attachment (id=71726) [details] [details]
> > Patch 12 (rebased and simplified)
> 
> Story for stepping into the middle of the story here. I thought you mentioned using shadow DOM in one of the previous comments, but this patch still uses the same page->chrome->foo machinery as the others.
> 
> Also, why does Chrome.cpp code is doing so much work? Shouldn't it be basically just a shim to shuttle information between browser and renderer?

The original intention was
 1) Showing a validation message by a native way.  It means Chromium needs IPC messagings.
 2) Showing just one validation message in one page even if the page consists of multiple frames

As you know, 1 is invalid for now because I have a shadow DOM implementation.

As for 2, I have changed my mind :-)   Showing multiple validation messages for multiple forms would be acceptable.  It happens only if a page has multiple forms and a user tries to submit them in short time.

We can simplify the patch.  Simpler is better.
Comment 42 Kent Tamura 2010-10-28 01:22:48 PDT
Created attachment 72157 [details]
Patch 13 (more simplification. No Chrome changes)
Comment 43 Dimitri Glazkov (Google) 2010-11-03 21:00:46 PDT
Comment on attachment 72157 [details]
Patch 13 (more simplification. No Chrome changes)

View in context: https://bugs.webkit.org/attachment.cgi?id=72157&action=review

ok.

> WebCore/html/ValidationMessage.cpp:55
> +    if (m_message.isEmpty()) { } // FIXME: Construct validation message UI.

Probably should just remove this, leaving just a FIXME.
Comment 44 Dimitri Glazkov (Google) 2010-11-03 21:23:02 PDT
Comment on attachment 72157 [details]
Patch 13 (more simplification. No Chrome changes)

View in context: https://bugs.webkit.org/attachment.cgi?id=72157&action=review

> WebCore/html/ValidationMessage.cpp:45
> +    hideMessage();

Cancel timer here?
Comment 45 Kent Tamura 2010-11-03 23:41:15 PDT
Comment on attachment 72157 [details]
Patch 13 (more simplification. No Chrome changes)

View in context: https://bugs.webkit.org/attachment.cgi?id=72157&action=review

Thank you for reviewing!!!

>> WebCore/html/ValidationMessage.cpp:45
>> +    hideMessage();
> 
> Cancel timer here?

We don't need to cancel explicitly.  It is canceled by m_timer destruction.

>> WebCore/html/ValidationMessage.cpp:55
>> +    if (m_message.isEmpty()) { } // FIXME: Construct validation message UI.
> 
> Probably should just remove this, leaving just a FIXME.

ok.
Comment 46 Kent Tamura 2010-11-04 00:02:39 PDT
Landed as http://trac.webkit.org/changeset/71309