Bug 21248

Summary: Support placeholder on textarea
Product: WebKit Reporter: Andy Lyttle <webkit>
Component: FormsAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Enhancement CC: adele, arv, contact, darin, emacemac7, eric, michelangelo, mike, ojan, simon.fraser, tkent
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
URL: http://phroggy.com/placeholder.html
Bug Depends on:    
Bug Blocks: 19264    
Attachments:
Description Flags
Proposed patch
none
Proposed patch (rev.2)
none
Proposed patch (rev.3)
darin: review-
Proposed patch (rev.4)
eric: review+, eric: commit-queue-
Proposed patch (rev.5)
none
Proposed patch (rev.6) none

Description Andy Lyttle 2008-09-30 12:01:55 PDT
There's no reason why <textarea> shouldn't support the placeholder property, just like other text input fields do.
Comment 1 Ojan Vafai 2008-10-13 15:24:52 PDT
I'd add also that contentEditable elements and designMode documents should support it as well. Really any text-input should support it.
Comment 2 Adele Peterson 2009-03-30 11:51:56 PDT
This has now been added in the HTML5 draft.
Comment 4 Kent Tamura 2009-06-25 04:35:28 PDT
*** Bug 26507 has been marked as a duplicate of this bug. ***
Comment 5 Kent Tamura 2009-06-25 04:36:19 PDT
I'm making a patch for this.
Comment 6 Kent Tamura 2009-06-25 22:30:04 PDT
Created attachment 31906 [details]
Proposed patch
Comment 7 Eric Seidel (no email) 2009-06-26 01:23:08 PDT
Comment on attachment 31906 [details]
Proposed patch

It's sad we can't share more code here.  Adele should really comment here.
Comment 8 Kent Tamura 2009-06-26 01:48:25 PDT
Created attachment 31919 [details]
Proposed patch (rev.2)

Some small changes.
Comment 9 Kent Tamura 2009-06-30 23:32:12 PDT
Created attachment 32114 [details]
Proposed patch (rev.3)

Update for the rename of css/html4.css.
Comment 10 Darin Adler 2009-08-07 08:02:42 PDT
Comment on attachment 32114 [details]
Proposed patch (rev.3)

Looks really good. Just a few comments.

> +String HTMLTextAreaElement::placeholder() const
> +{
> +    return getAttribute(placeholderAttr).string();
> +}
> +
> +void HTMLTextAreaElement::setPlaceholder(const String& value)
> +{
> +    setAttribute(placeholderAttr, value);
> +}

These are not great. There's no need to convert to a plain string here, and in fact it's bad for performance. The right way to do this nowadays is to just put [Reflect] in the IDL file and not define a setPlaceholder function in the .h and .cpp file at all, so that should be done here.

Since there are a couple call sites that do want placeholder, it would be OK to keep it but it could return const AtomicString& instead of String to avoid a bit of reference count churn and you probably would only have to use the string() function in one place.

> +    if ((oldPlaceholderShouldBeVisible != m_placeholderShouldBeVisible || placeholderValueChanged)
> +        && renderer())
> +        static_cast<RenderTextControlMultiLine*>(renderer())->updatePlaceholderVisibility();

Indenting here is awkward.

> +    bool placeholderShouldBeVisible() const { return m_placeholderShouldBeVisible; }

I think it would be a better design for the HTMLTextAreaElement to tell the 

> +    virtual void dispatchFocusEvent();
> +    virtual void dispatchBlurEvent();

These overrides should be private, not public, because we generally want all functions to be as private as possible.

> +    void setPlaceholderShouldBeVisible(bool visible) { m_placeholderShouldBeVisible = visible; }

I don't think you need this setter function. It doesn't seem to add any abstraction.

> +                 attribute  DOMString            placeholder;

Just adding the [Reflect] attribute here will do the trick.

> +void RenderTextControl::updatePlaceholderVisibility()
> +{
> +    RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle());
> +    HTMLElement* innerText = innerTextElement();
> +    innerText->renderer()->setStyle(textBlockStyle);
> +
> +    for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) {
> +        if (RenderObject* renderer = n->renderer())
> +            renderer->setStyle(textBlockStyle);
> +    }
> +
> +    updateFromElement();
> +}

I know you must moved this from one class to another, but this function needs some comments. It's unclear why updatePlaceholderVisibility needs to update the style of all the renderers and then call updateFromElement. A comment could explain the relationship. Also, we could consider moving code to set the text block style into a separate function, since the name of that function would help document it.

> -    if (result.innerNode() == node() || result.innerNode() == innerTextElement())
> +    HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(node());
> +    if (result.innerNode() == node()
> +        || !textArea->placeholderShouldBeVisible() && result.innerNode() == innerTextElement()
> +        || textArea->placeholderShouldBeVisible() && result.innerNode()->isDescendantOf(innerTextElement()))
>          hitInnerTextElement(result, x, y, tx, ty);

The way the formatting makes the clauses of the if statement line up with its body makes things like this hard to read.

I think you could consider fixing that by using a boolean local variable; the name of the boolean could even help document a bit. I think the code also needs a comment. The rule here is slightly unclear unless the reader really aware of how placeholders should behave.

I'm not so sure that it's great to move the m_placeholderVisible data member into the RenderTextControl base class. While it's true that both derived classes use it, the base class does not.

> +RenderStyle* RenderTextControlMultiLine::parentStyle()

I don't understand the use of "parent" in this name. This isn't really the style of the renderer's parent, because if so it wouldn't need to be a virtual function. I know the local variable was named that, but a local variable has less scope than a member function, and naming is slightly less critical when the name is that tightly scoped. But could we give this function a name that's more precise? Sorry, I don't have a specific name to suggest.

> +protected:
> +    virtual RenderStyle* parentStyle();

This can and should be private instead of protected. You can override a function with a private function, and we should make things as private as possible. If we ever did want to derive from this class we could make it protected at that time.

> +protected:
> +    virtual RenderStyle* parentStyle();

Ditto.

review- because at least some of the changes above should be made
Comment 11 Kent Tamura 2009-08-10 19:03:34 PDT
Thank you for the comments!

(In reply to comment #10)
> (From update of attachment 32114 [details])
> Looks really good. Just a few comments.
> 
> > +String HTMLTextAreaElement::placeholder() const
> > +{
> > +    return getAttribute(placeholderAttr).string();
> > +}
> > +
> > +void HTMLTextAreaElement::setPlaceholder(const String& value)
> > +{
> > +    setAttribute(placeholderAttr, value);
> > +}
> 
> These are not great. There's no need to convert to a plain string here, and in
> fact it's bad for performance. The right way to do this nowadays is to just put
> [Reflect] in the IDL file and not define a setPlaceholder function in the .h
> and .cpp file at all, so that should be done here.

Ok, I changed this to [Reflect].

> Indenting here is awkward.

Fixed.

> > +    bool placeholderShouldBeVisible() const { return m_placeholderShouldBeVisible; }
> 
> I think it would be a better design for the HTMLTextAreaElement to tell the 

I agree.  When I looked at the existing placeholder code first time, I had an impression that the code was complex.  I think the reason is that both of a DOM node and a renderer have flags for placeholder visibility.
I have removed the flag of InputElemntData, and updated many place to tell the visibility from a DOM node to a renderer.

> > +    virtual void dispatchFocusEvent();
> > +    virtual void dispatchBlurEvent();
> 
> These overrides should be private, not public, because we generally want all
> functions to be as private as possible.

Fixed.

> > +    void setPlaceholderShouldBeVisible(bool visible) { m_placeholderShouldBeVisible = visible; }
> 
> I don't think you need this setter function. It doesn't seem to add any
> abstraction.

Removed.

> > +void RenderTextControl::updatePlaceholderVisibility()
> > +{
> > +    RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle());
> > +    HTMLElement* innerText = innerTextElement();
> > +    innerText->renderer()->setStyle(textBlockStyle);
> > +
> > +    for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) {
> > +        if (RenderObject* renderer = n->renderer())
> > +            renderer->setStyle(textBlockStyle);
> > +    }
> > +
> > +    updateFromElement();
> > +}
> 
> I know you must moved this from one class to another, but this function needs
> some comments. It's unclear why updatePlaceholderVisibility needs to update the
> style of all the renderers and then call updateFromElement. A comment could
> explain the relationship. Also, we could consider moving code to set the text
> block style into a separate function, since the name of that function would
> help document it.

I have addded a new method, setInnerTextStyle().

> > -    if (result.innerNode() == node() || result.innerNode() == innerTextElement())
> > +    HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(node());
> > +    if (result.innerNode() == node()
> > +        || !textArea->placeholderShouldBeVisible() && result.innerNode() == innerTextElement()
> > +        || textArea->placeholderShouldBeVisible() && result.innerNode()->isDescendantOf(innerTextElement()))
> >          hitInnerTextElement(result, x, y, tx, ty);
> 
> The way the formatting makes the clauses of the if statement line up with its
> body makes things like this hard to read.
> 
> I think you could consider fixing that by using a boolean local variable; the
> name of the boolean could even help document a bit. I think the code also needs
> a comment. The rule here is slightly unclear unless the reader really aware of
> how placeholders should behave.

Done.

> I'm not so sure that it's great to move the m_placeholderVisible data member
> into the RenderTextControl base class. While it's true that both derived
> classes use it, the base class does not.

In the next patch, RenderTextControl refers m_placeholderVisible.

> > +RenderStyle* RenderTextControlMultiLine::parentStyle()

Renamed it to textBaseStyle().

> > +protected:
> > +    virtual RenderStyle* parentStyle();
> 
> This can and should be private instead of protected. You can override a
> function with a private function, and we should make things as private as
> possible. If we ever did want to derive from this class we could make it
> protected at that time.

Fixed.
Comment 12 Kent Tamura 2009-08-10 19:11:12 PDT
Created attachment 34534 [details]
Proposed patch (rev.4)

Sorry for the large patch.
* The most of changes in HTMLInputElment, InputElment, WMLInputElement are trivial. (removal of unused parameters)
* We have a bug about maxlength.  The patch accidentally fixes it because of RenderTextControlSingleLine::updateFromElement() change.  input-text-maxlength-expected.txt had wrong expected data.
Comment 13 Eric Seidel (no email) 2009-08-21 09:47:05 PDT
Comment on attachment 34534 [details]
Proposed patch (rev.4)

This looks great.  I'm just sad we can's share implementations:

+    static void dispatchFocusEvent(InputElement*, Element*);
+    static void dispatchBlurEvent(InputElement*, Element*);
+    static bool placeholderShouldBeVisible(const InputElement*, const Element*);
+    static void updatePlaceholderVisibility(InputElement*, Element*, bool placeholderValueChanged = false);

We could put those on HTMLFormControlElement... or on a new HTMLTextControlElement subclass.  Or on some class which both of these used (since "having" placeholder text does not necessarily mean you need to "be" a classs which can have placehodler text.  We could use 'has a' instead of 'is a' to share code.)

I'm going to approve this as is.  I would like you to consider a follow-up patch to share more of this code.
Comment 14 Eric Seidel (no email) 2009-08-21 12:07:12 PDT
Comment on attachment 34534 [details]
Proposed patch (rev.4)

Rejecting patch 34534 from commit-queue.  This patch will require manual commit.

['WebKitTools/Scripts/run-webkit-tests', '--no-launch-safari', '--quiet', '--exit-after-n-failures=1'] failed with exit code 1
Comment 15 Eric Seidel (no email) 2009-08-21 12:07:40 PDT
fast/forms/input-text-maxlength.html -> failed
Comment 16 Kent Tamura 2009-08-23 22:40:35 PDT
Created attachment 38467 [details]
Proposed patch (rev.5)

Updated test cases.
Comment 17 Kent Tamura 2009-08-23 22:46:24 PDT
Created attachment 38468 [details]
Proposed patch (rev.6)

Oops, I found LayoutTests/ChangeLog in the rev.5 was incomplete.  Corrected it.
Comment 18 Kent Tamura 2009-08-23 22:49:18 PDT
(In reply to comment #13)
> I'm going to approve this as is.  I would like you to consider a follow-up
> patch to share more of this code.

Sure.  I'll try it.
Comment 19 Eric Seidel (no email) 2009-08-24 01:15:44 PDT
Comment on attachment 38468 [details]
Proposed patch (rev.6)

LGTM.
Comment 20 Eric Seidel (no email) 2009-08-24 01:28:58 PDT
Comment on attachment 38468 [details]
Proposed patch (rev.6)

Clearing flags on attachment: 38468

Committed r47702: <http://trac.webkit.org/changeset/47702>
Comment 21 Eric Seidel (no email) 2009-08-24 01:29:01 PDT
All reviewed patches have been landed.  Closing bug.
Comment 22 Simon Fraser (smfr) 2009-08-24 11:11:01 PDT
4 textarea tests are still failing after this change.
Comment 23 Eric Seidel (no email) 2009-08-24 11:40:28 PDT
(In reply to comment #22)
> 4 textarea tests are still failing after this change.

On what platform?  I assume windows since mac is green.
Comment 24 Eric Seidel (no email) 2009-08-24 11:41:54 PDT
http://build.webkit.org/results/Windows%20Release%20(Tests)/r47719%20(3707)/results.html

Looks like windows will need new baselines or these tests will need to be converted not to be render tree dumps. tkent could do the test conversion, or we can just land the windows results from the bots.
Comment 25 Eric Seidel (no email) 2009-08-24 11:46:21 PDT
Will land windows results from the bot, shortly.
Comment 26 Eric Seidel (no email) 2009-08-24 12:10:24 PDT
Committed r47721: <http://trac.webkit.org/changeset/47721>