1/**
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23#include "RenderTextControl.h"
24
25#include "AXObjectCache.h"
26#include "Editor.h"
27#include "Event.h"
28#include "EventNames.h"
29#include "Frame.h"
30#include "HTMLBRElement.h"
31#include "HTMLFormControlElement.h"
32#include "HTMLInputElement.h"
33#include "HTMLNames.h"
34#include "HitTestResult.h"
35#include "Position.h"
36#include "RenderLayer.h"
37#include "RenderText.h"
38#include "ScrollbarTheme.h"
39#include "SelectionController.h"
40#include "Text.h"
41#include "TextControlInnerElements.h"
42#include "TextIterator.h"
43#include "TextRun.h"
44#include <wtf/unicode/CharacterNames.h>
45
46using namespace std;
47
48namespace WebCore {
49
50using namespace HTMLNames;
51
52// Value chosen by observation.  This can be tweaked.
53static const int minColorContrastValue = 1300;
54
55static Color disabledTextColor(const Color& textColor, const Color& backgroundColor)
56{
57    // The explicit check for black is an optimization for the 99% case (black on white).
58    // This also means that black on black will turn into grey on black when disabled.
59    Color disabledColor;
60    if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
61        disabledColor = textColor.light();
62    else
63        disabledColor = textColor.dark();
64
65    // If there's not very much contrast between the disabled color and the background color,
66    // just leave the text color alone.  We don't want to change a good contrast color scheme so that it has really bad contrast.
67    // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
68    if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
69        return textColor;
70
71    return disabledColor;
72}
73
74RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible)
75    : RenderBlock(node)
76    , m_placeholderVisible(placeholderVisible)
77    , m_lastChangeWasUserEdit(false)
78{
79}
80
81RenderTextControl::~RenderTextControl()
82{
83    // The children renderers have already been destroyed by destroyLeftoverChildren
84    if (m_innerText)
85        m_innerText->detach();
86}
87
88void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
89{
90    RenderBlock::styleDidChange(diff, oldStyle);
91
92    if (m_innerText) {
93        RenderBlock* textBlockRenderer = toRenderBlock(m_innerText->renderer());
94        RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style());
95        // We may have set the width and the height in the old style in layout().
96        // Reset them now to avoid getting a spurious layout hint.
97        textBlockRenderer->style()->setHeight(Length());
98        textBlockRenderer->style()->setWidth(Length());
99        setInnerTextStyle(textBlockStyle);
100    }
101}
102
103void RenderTextControl::setInnerTextStyle(PassRefPtr<RenderStyle> style)
104{
105    if (m_innerText) {
106        RefPtr<RenderStyle> textStyle = style;
107        m_innerText->renderer()->setStyle(textStyle);
108        for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
109            if (n->renderer())
110                n->renderer()->setStyle(textStyle);
111        }
112    }
113}
114
115static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
116{
117    bool isEnabled = true;
118    bool isReadOnlyControl = false;
119
120    if (node->isElementNode()) {
121        Element* element = static_cast<Element*>(node);
122        isEnabled = element->isEnabledFormControl();
123        isReadOnlyControl = element->isReadOnlyFormControl();
124    }
125
126    style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
127    return !isEnabled;
128}
129
130void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
131{
132    // The inner block, if present, always has its direction set to LTR,
133    // so we need to inherit the direction from the element.
134    textBlockStyle->setDirection(style()->direction());
135
136    bool disabled = updateUserModifyProperty(node(), textBlockStyle);
137    if (disabled)
138        textBlockStyle->setColor(disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
139}
140
141void RenderTextControl::createSubtreeIfNeeded(TextControlInnerElement* innerBlock)
142{
143    if (!m_innerText) {
144        // Create the text block element
145        // For non-search fields, there is no intermediate innerBlock as the shadow node.
146        // m_innerText will be the shadow node in that case.
147        RenderStyle* parentStyle = innerBlock ? innerBlock->renderer()->style() : style();
148        m_innerText = TextControlInnerTextElement::create(document(), innerBlock ? 0 : toHTMLElement(node()));
149        m_innerText->attachInnerElement(innerBlock ? innerBlock : node(), createInnerTextStyle(parentStyle), renderArena());
150    }
151}
152
153int RenderTextControl::textBlockHeight() const
154{
155    return height() - borderAndPaddingHeight();
156}
157
158int RenderTextControl::textBlockWidth() const
159{
160    return width() - borderAndPaddingWidth() - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
161}
162
163void RenderTextControl::updateFromElement()
164{
165    updateUserModifyProperty(node(), m_innerText->renderer()->style());
166}
167
168void RenderTextControl::setInnerTextValue(const String& innerTextValue)
169{
170    String value = innerTextValue;
171    if (value != text() || !m_innerText->hasChildNodes()) {
172        if (value != text()) {
173            if (Frame* frame = this->frame()) {
174                frame->editor()->clearUndoRedoOperations();
175
176                if (AXObjectCache::accessibilityEnabled())
177                    document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, false);
178            }
179        }
180
181        ExceptionCode ec = 0;
182        m_innerText->setInnerText(value, ec);
183        ASSERT(!ec);
184
185        if (value.endsWith("\n") || value.endsWith("\r")) {
186            m_innerText->appendChild(HTMLBRElement::create(document()), ec);
187            ASSERT(!ec);
188        }
189
190        // We set m_lastChangeWasUserEdit to false since this change was not explicitly made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>.
191        m_lastChangeWasUserEdit = false;
192    }
193
194    static_cast<Element*>(node())->setFormControlValueMatchesRenderer(true);
195}
196
197void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
198{
199    m_lastChangeWasUserEdit = lastChangeWasUserEdit;
200    document()->setIgnoreAutofocus(lastChangeWasUserEdit);
201}
202
203int RenderTextControl::selectionStart() const
204{
205    Frame* frame = this->frame();
206    if (!frame)
207        return 0;
208
209    HTMLElement* innerText = innerTextElement();
210    // Do not call innerTextElement() in the function arguments as creating a VisiblePosition
211    // from frame->selection->start() can blow us from underneath. Also, function ordering is
212    // usually dependent on the compiler.
213    return RenderTextControl::indexForVisiblePosition(innerText, frame->selection()->start());
214}
215
216int RenderTextControl::selectionEnd() const
217{
218    Frame* frame = this->frame();
219    if (!frame)
220        return 0;
221
222    HTMLElement* innerText = innerTextElement();
223    // Do not call innerTextElement() in the function arguments as creating a VisiblePosition
224    // from frame->selection->end() can blow us from underneath. Also, function ordering is
225    // usually dependent on the compiler.
226    return RenderTextControl::indexForVisiblePosition(innerText, frame->selection()->end());
227}
228
229bool RenderTextControl::hasVisibleTextArea() const
230{
231    return style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height();
232}
233
234void setSelectionRange(Node* node, int start, int end)
235{
236    ASSERT(node);
237    node->document()->updateLayoutIgnorePendingStylesheets();
238
239    if (!node->renderer() || !node->renderer()->isTextControl())
240        return;
241
242    end = max(end, 0);
243    start = min(max(start, 0), end);
244
245    RenderTextControl* control = toRenderTextControl(node->renderer());
246
247    if (control->hasVisibleTextArea()) {
248        control->cacheSelection(start, end);
249        return;
250    }
251    VisiblePosition startPosition = control->visiblePositionForIndex(start);
252    VisiblePosition endPosition;
253    if (start == end)
254        endPosition = startPosition;
255    else
256        endPosition = control->visiblePositionForIndex(end);
257
258    // startPosition and endPosition can be null position for example when
259    // "-webkit-user-select: none" style attribute is specified.
260    if (startPosition.isNotNull() && endPosition.isNotNull()) {
261        ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == node && endPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == node);
262    }
263    VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);
264
265    if (Frame* frame = node->document()->frame())
266        frame->selection()->setSelection(newSelection);
267}
268
269bool RenderTextControl::isSelectableElement(HTMLElement* innerText, Node* node)
270{
271    if (!node || !innerText)
272        return false;
273
274    if (node->rootEditableElement() == innerText)
275        return true;
276
277    if (!innerText->contains(node))
278        return false;
279
280    Node* shadowAncestor = node->shadowAncestorNode();
281    return shadowAncestor && (shadowAncestor->hasTagName(textareaTag)
282        || (shadowAncestor->hasTagName(inputTag) && static_cast<HTMLInputElement*>(shadowAncestor)->isTextField()));
283}
284
285static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
286{
287    if (node->isTextNode()) {
288        containerNode = node;
289        offsetInContainer = offset;
290    } else {
291        containerNode = node->parentNode();
292        offsetInContainer = node->nodeIndex() + offset;
293    }
294}
295
296PassRefPtr<Range> RenderTextControl::selection(int start, int end) const
297{
298    ASSERT(start <= end);
299    if (!m_innerText)
300        return 0;
301
302    if (!m_innerText->firstChild())
303        return Range::create(document(), m_innerText, 0, m_innerText, 0);
304
305    int offset = 0;
306    Node* startNode = 0;
307    Node* endNode = 0;
308    for (Node* node = m_innerText->firstChild(); node; node = node->traverseNextNode(m_innerText.get())) {
309        ASSERT(!node->firstChild());
310        ASSERT(node->isTextNode() || node->hasTagName(brTag));
311        int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
312
313        if (offset <= start && start <= offset + length)
314            setContainerAndOffsetForRange(node, start - offset, startNode, start);
315
316        if (offset <= end && end <= offset + length) {
317            setContainerAndOffsetForRange(node, end - offset, endNode, end);
318            break;
319        }
320
321        offset += length;
322    }
323
324    if (!startNode || !endNode)
325        return 0;
326
327    return Range::create(document(), startNode, start, endNode, end);
328}
329
330VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
331{
332    if (index <= 0)
333        return VisiblePosition(Position(m_innerText.get(), 0, Position::PositionIsOffsetInAnchor), DOWNSTREAM);
334    ExceptionCode ec = 0;
335    RefPtr<Range> range = Range::create(document());
336    range->selectNodeContents(m_innerText.get(), ec);
337    ASSERT(!ec);
338    CharacterIterator it(range.get());
339    it.advance(index - 1);
340    Node* endContainer = it.range()->endContainer(ec);
341    ASSERT(!ec);
342    int endOffset = it.range()->endOffset(ec);
343    ASSERT(!ec);
344    return VisiblePosition(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), UPSTREAM);
345}
346
347int RenderTextControl::indexForVisiblePosition(HTMLElement* innerTextElement, const VisiblePosition& pos)
348{
349    Position indexPosition = pos.deepEquivalent();
350    if (!RenderTextControl::isSelectableElement(innerTextElement, indexPosition.deprecatedNode()))
351        return 0;
352    ExceptionCode ec = 0;
353    RefPtr<Range> range = Range::create(indexPosition.document());
354    range->setStart(innerTextElement, 0, ec);
355    ASSERT(!ec);
356    range->setEnd(indexPosition.deprecatedNode(), indexPosition.deprecatedEditingOffset(), ec);
357    ASSERT(!ec);
358    return TextIterator::rangeLength(range.get());
359}
360
361void RenderTextControl::subtreeHasChanged()
362{
363    m_lastChangeWasUserEdit = true;
364}
365
366String RenderTextControl::finishText(Vector<UChar>& result) const
367{
368    // Remove one trailing newline; there's always one that's collapsed out by rendering.
369    size_t size = result.size();
370    if (size && result[size - 1] == '\n')
371        result.shrink(--size);
372
373    return String::adopt(result);
374}
375
376String RenderTextControl::text()
377{
378    if (!m_innerText)
379        return "";
380
381    Vector<UChar> result;
382
383    for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
384        if (n->hasTagName(brTag))
385            result.append(&newlineCharacter, 1);
386        else if (n->isTextNode()) {
387            String data = static_cast<Text*>(n)->data();
388            result.append(data.characters(), data.length());
389        }
390    }
391
392    return finishText(result);
393}
394
395static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
396{
397    RootInlineBox* next;
398    for (; line; line = next) {
399        next = line->nextRootBox();
400        if (next && !line->endsWithBreak()) {
401            ASSERT(line->lineBreakObj());
402            breakNode = line->lineBreakObj()->node();
403            breakOffset = line->lineBreakPos();
404            line = next;
405            return;
406        }
407    }
408    breakNode = 0;
409    breakOffset = 0;
410}
411
412String RenderTextControl::textWithHardLineBreaks()
413{
414    if (!m_innerText)
415        return "";
416
417    RenderBlock* renderer = toRenderBlock(m_innerText->renderer());
418    if (!renderer)
419        return "";
420
421    Node* breakNode;
422    unsigned breakOffset;
423    RootInlineBox* line = renderer->firstRootBox();
424    if (!line)
425        return "";
426
427    getNextSoftBreak(line, breakNode, breakOffset);
428
429    Vector<UChar> result;
430
431    for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
432        if (n->hasTagName(brTag))
433            result.append(&newlineCharacter, 1);
434        else if (n->isTextNode()) {
435            Text* text = static_cast<Text*>(n);
436            String data = text->data();
437            unsigned length = data.length();
438            unsigned position = 0;
439            while (breakNode == n && breakOffset <= length) {
440                if (breakOffset > position) {
441                    result.append(data.characters() + position, breakOffset - position);
442                    position = breakOffset;
443                    result.append(&newlineCharacter, 1);
444                }
445                getNextSoftBreak(line, breakNode, breakOffset);
446            }
447            result.append(data.characters() + position, length - position);
448        }
449        while (breakNode == n)
450            getNextSoftBreak(line, breakNode, breakOffset);
451    }
452
453    return finishText(result);
454}
455
456int RenderTextControl::scrollbarThickness() const
457{
458    // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
459    return ScrollbarTheme::nativeTheme()->scrollbarThickness();
460}
461
462void RenderTextControl::computeLogicalHeight()
463{
464    setHeight(m_innerText->renderBox()->borderTop() + m_innerText->renderBox()->borderBottom() +
465              m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() +
466              m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom());
467
468    adjustControlHeightBasedOnLineHeight(m_innerText->renderBox()->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes));
469    setHeight(height() + borderAndPaddingHeight());
470
471    // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
472    if (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
473        setHeight(height() + scrollbarThickness());
474
475    RenderBlock::computeLogicalHeight();
476}
477
478void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty)
479{
480    result.setInnerNode(m_innerText.get());
481    result.setInnerNonSharedNode(m_innerText.get());
482    result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(),
483                                  yPos - ty - y() - m_innerText->renderBox()->y()));
484}
485
486void RenderTextControl::forwardEvent(Event* event)
487{
488    if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
489        return;
490    m_innerText->defaultEventHandler(event);
491}
492
493static const char* fontFamiliesWithInvalidCharWidth[] = {
494    "American Typewriter",
495    "Arial Hebrew",
496    "Chalkboard",
497    "Cochin",
498    "Corsiva Hebrew",
499    "Courier",
500    "Euphemia UCAS",
501    "Geneva",
502    "Gill Sans",
503    "Hei",
504    "Helvetica",
505    "Hoefler Text",
506    "InaiMathi",
507    "Kai",
508    "Lucida Grande",
509    "Marker Felt",
510    "Monaco",
511    "Mshtakan",
512    "New Peninim MT",
513    "Osaka",
514    "Raanana",
515    "STHeiti",
516    "Symbol",
517    "Times",
518    "Apple Braille",
519    "Apple LiGothic",
520    "Apple LiSung",
521    "Apple Symbols",
522    "AppleGothic",
523    "AppleMyungjo",
524    "#GungSeo",
525    "#HeadLineA",
526    "#PCMyungjo",
527    "#PilGi",
528};
529
530// For font families where any of the fonts don't have a valid entry in the OS/2 table
531// for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
532// from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
533// but, in order to get similar rendering across platforms, we do this check for
534// all platforms.
535bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
536{
537    static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
538
539    if (!fontFamiliesWithInvalidCharWidthMap) {
540        fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
541
542        for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
543            fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
544    }
545
546    return !fontFamiliesWithInvalidCharWidthMap->contains(family);
547}
548
549float RenderTextControl::getAvgCharWidth(AtomicString family)
550{
551    if (hasValidAvgCharWidth(family))
552        return roundf(style()->font().primaryFont()->avgCharWidth());
553
554    const UChar ch = '0';
555    return style()->font().width(TextRun(&ch, 1, false, 0, 0, TextRun::AllowTrailingExpansion, false));
556}
557
558float RenderTextControl::scaleEmToUnits(int x) const
559{
560    // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
561    float unitsPerEm = 2048.0f;
562    return roundf(style()->font().size() * x / unitsPerEm);
563}
564
565void RenderTextControl::computePreferredLogicalWidths()
566{
567    ASSERT(preferredLogicalWidthsDirty());
568
569    m_minPreferredLogicalWidth = 0;
570    m_maxPreferredLogicalWidth = 0;
571
572    if (style()->width().isFixed() && style()->width().value() > 0)
573        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
574    else {
575        // Use average character width. Matches IE.
576        AtomicString family = style()->font().family().family();
577        m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + m_innerText->renderBox()->paddingLeft() + m_innerText->renderBox()->paddingRight();
578    }
579
580    if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
581        m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
582        m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
583    } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
584        m_minPreferredLogicalWidth = 0;
585    else
586        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
587
588    if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
589        m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
590        m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
591    }
592
593    int toAdd = borderAndPaddingWidth();
594
595    m_minPreferredLogicalWidth += toAdd;
596    m_maxPreferredLogicalWidth += toAdd;
597
598    setPreferredLogicalWidthsDirty(false);
599}
600
601void RenderTextControl::selectionChanged(bool userTriggered)
602{
603    cacheSelection(selectionStart(), selectionEnd());
604
605    if (Frame* frame = this->frame()) {
606        if (frame->selection()->isRange() && userTriggered)
607            node()->dispatchEvent(Event::create(eventNames().selectEvent, true, false));
608    }
609}
610
611void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
612{
613    if (width() && height())
614        rects.append(IntRect(tx, ty, width(), height()));
615}
616
617HTMLElement* RenderTextControl::innerTextElement() const
618{
619    return m_innerText.get();
620}
621
622void RenderTextControl::updatePlaceholderVisibility(bool placeholderShouldBeVisible, bool placeholderValueChanged)
623{
624    bool oldPlaceholderVisible = m_placeholderVisible;
625    m_placeholderVisible = placeholderShouldBeVisible;
626    if (oldPlaceholderVisible != m_placeholderVisible || placeholderValueChanged)
627        repaint();
628}
629
630void RenderTextControl::paintPlaceholder(PaintInfo& paintInfo, int tx, int ty)
631{
632    if (style()->visibility() != VISIBLE)
633        return;
634
635    IntRect clipRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop());
636    if (clipRect.isEmpty())
637        return;
638
639    paintInfo.context->save();
640
641    paintInfo.context->clip(clipRect);
642
643    RefPtr<RenderStyle> placeholderStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER);
644    if (!placeholderStyle)
645        placeholderStyle = style();
646
647    paintInfo.context->setFillColor(placeholderStyle->visitedDependentColor(CSSPropertyColor), placeholderStyle->colorSpace());
648
649    String placeholderText = static_cast<HTMLTextFormControlElement*>(node())->strippedPlaceholder();
650    TextRun textRun(placeholderText.characters(), placeholderText.length(), false, 0, 0, TextRun::AllowTrailingExpansion, !placeholderStyle->isLeftToRightDirection(), placeholderStyle->unicodeBidi() == Override);
651
652    RenderBox* textRenderer = innerTextElement() ? innerTextElement()->renderBox() : 0;
653    if (textRenderer) {
654        IntPoint textPoint;
655        textPoint.setY(ty + textBlockInsetTop() + placeholderStyle->fontMetrics().ascent());
656        if (placeholderStyle->isLeftToRightDirection())
657            textPoint.setX(tx + textBlockInsetLeft());
658        else
659            textPoint.setX(tx + width() - textBlockInsetRight() - style()->font().width(textRun));
660
661        paintInfo.context->drawBidiText(placeholderStyle->font(), textRun, textPoint);
662    }
663    paintInfo.context->restore();
664}
665
666void RenderTextControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
667{
668    if (m_placeholderVisible && paintInfo.phase == PaintPhaseForeground)
669        paintPlaceholder(paintInfo, tx, ty);
670
671    RenderBlock::paintObject(paintInfo, tx, ty);
672}
673
674} // namespace WebCore
675