0a9749229637ebff12d423479a5b3017d425b1ec
[WebKit-https.git] / Source / WebCore / rendering / RenderTextControl.cpp
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 "FrameSelection.h"
31 #include "HTMLBRElement.h"
32 #include "HTMLFormControlElement.h"
33 #include "HTMLInputElement.h"
34 #include "HTMLNames.h"
35 #include "HitTestResult.h"
36 #include "Position.h"
37 #include "RenderLayer.h"
38 #include "RenderText.h"
39 #include "ScrollbarTheme.h"
40 #include "Text.h"
41 #include "TextControlInnerElements.h"
42 #include "TextIterator.h"
43 #include "TextRun.h"
44 #include <wtf/unicode/CharacterNames.h>
45
46 using namespace std;
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 // Value chosen by observation.  This can be tweaked.
53 static const int minColorContrastValue = 1300;
54
55 static 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
74 RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible)
75     : RenderBlock(node)
76     , m_placeholderVisible(placeholderVisible)
77     , m_lastChangeWasUserEdit(false)
78 {
79 }
80
81 RenderTextControl::~RenderTextControl()
82 {
83     // The children renderers have already been destroyed by destroyLeftoverChildren
84     if (m_innerText)
85         m_innerText->detach();
86 }
87
88 void 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
103 void 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
115 static 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
130 void 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
141 void 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
153 int RenderTextControl::textBlockHeight() const
154 {
155     return height() - borderAndPaddingHeight();
156 }
157
158 int RenderTextControl::textBlockWidth() const
159 {
160     return width() - borderAndPaddingWidth() - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
161 }
162
163 void RenderTextControl::updateFromElement()
164 {
165     updateUserModifyProperty(node(), m_innerText->renderer()->style());
166 }
167
168 void 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
197 void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
198 {
199     m_lastChangeWasUserEdit = lastChangeWasUserEdit;
200     document()->setIgnoreAutofocus(lastChangeWasUserEdit);
201 }
202
203 int RenderTextControl::selectionStart() const
204 {
205     Frame* frame = this->frame();
206     if (!frame)
207         return 0;
208     return indexForVisiblePosition(frame->selection()->start());
209 }
210
211 int RenderTextControl::selectionEnd() const
212 {
213     Frame* frame = this->frame();
214     if (!frame)
215         return 0;
216     return indexForVisiblePosition(frame->selection()->end());
217 }
218
219 bool RenderTextControl::hasVisibleTextArea() const
220 {
221     return style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height();
222 }
223
224 void setSelectionRange(Node* node, int start, int end)
225 {
226     ASSERT(node);
227     node->document()->updateLayoutIgnorePendingStylesheets();
228
229     if (!node->renderer() || !node->renderer()->isTextControl())
230         return;
231
232     end = max(end, 0);
233     start = min(max(start, 0), end);
234
235     RenderTextControl* control = toRenderTextControl(node->renderer());
236
237     if (control->hasVisibleTextArea()) {
238         control->cacheSelection(start, end);
239         return;
240     }
241     VisiblePosition startPosition = control->visiblePositionForIndex(start);
242     VisiblePosition endPosition;
243     if (start == end)
244         endPosition = startPosition;
245     else
246         endPosition = control->visiblePositionForIndex(end);
247
248     // startPosition and endPosition can be null position for example when
249     // "-webkit-user-select: none" style attribute is specified.
250     if (startPosition.isNotNull() && endPosition.isNotNull()) {
251         ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == node && endPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == node);
252     }
253     VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);
254
255     if (Frame* frame = node->document()->frame())
256         frame->selection()->setSelection(newSelection);
257 }
258
259 bool RenderTextControl::isSelectableElement(Node* node) const
260 {
261     if (!node || !m_innerText)
262         return false;
263     
264     if (node->rootEditableElement() == m_innerText)
265         return true;
266     
267     if (!m_innerText->contains(node))
268         return false;
269     
270     Node* shadowAncestor = node->shadowAncestorNode();
271     return shadowAncestor && (shadowAncestor->hasTagName(textareaTag)
272         || (shadowAncestor->hasTagName(inputTag) && static_cast<HTMLInputElement*>(shadowAncestor)->isTextField()));
273 }
274
275 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
276 {
277     if (node->isTextNode()) {
278         containerNode = node;
279         offsetInContainer = offset;
280     } else {
281         containerNode = node->parentNode();
282         offsetInContainer = node->nodeIndex() + offset;
283     }
284 }
285
286 PassRefPtr<Range> RenderTextControl::selection(int start, int end) const
287 {
288     ASSERT(start <= end);
289     if (!m_innerText)
290         return 0;
291
292     if (!m_innerText->firstChild())
293         return Range::create(document(), m_innerText, 0, m_innerText, 0);
294
295     int offset = 0;
296     Node* startNode = 0;
297     Node* endNode = 0;
298     for (Node* node = m_innerText->firstChild(); node; node = node->traverseNextNode(m_innerText.get())) {
299         ASSERT(!node->firstChild());
300         ASSERT(node->isTextNode() || node->hasTagName(brTag));
301         int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
302     
303         if (offset <= start && start <= offset + length)
304             setContainerAndOffsetForRange(node, start - offset, startNode, start);
305
306         if (offset <= end && end <= offset + length) {
307             setContainerAndOffsetForRange(node, end - offset, endNode, end);
308             break;
309         }
310
311         offset += length;
312     }
313     
314     if (!startNode || !endNode)
315         return 0;
316
317     return Range::create(document(), startNode, start, endNode, end);
318 }
319
320 VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
321 {
322     if (index <= 0)
323         return VisiblePosition(Position(m_innerText.get(), 0, Position::PositionIsOffsetInAnchor), DOWNSTREAM);
324     ExceptionCode ec = 0;
325     RefPtr<Range> range = Range::create(document());
326     range->selectNodeContents(m_innerText.get(), ec);
327     ASSERT(!ec);
328     CharacterIterator it(range.get());
329     it.advance(index - 1);
330     Node* endContainer = it.range()->endContainer(ec);
331     ASSERT(!ec);
332     int endOffset = it.range()->endOffset(ec);
333     ASSERT(!ec);
334     return VisiblePosition(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), UPSTREAM);
335 }
336
337 int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) const
338 {
339     Position indexPosition = pos.deepEquivalent();
340     if (!isSelectableElement(indexPosition.deprecatedNode()))
341         return 0;
342     ExceptionCode ec = 0;
343     RefPtr<Range> range = Range::create(document());
344     range->setStart(m_innerText.get(), 0, ec);
345     ASSERT(!ec);
346     range->setEnd(indexPosition.deprecatedNode(), indexPosition.deprecatedEditingOffset(), ec);
347     ASSERT(!ec);
348     return TextIterator::rangeLength(range.get());
349 }
350
351 void RenderTextControl::subtreeHasChanged()
352 {
353     m_lastChangeWasUserEdit = true;
354 }
355
356 String RenderTextControl::finishText(Vector<UChar>& result) const
357 {
358     // Remove one trailing newline; there's always one that's collapsed out by rendering.
359     size_t size = result.size();
360     if (size && result[size - 1] == '\n')
361         result.shrink(--size);
362
363     return String::adopt(result);
364 }
365
366 String RenderTextControl::text()
367 {
368     if (!m_innerText)
369         return "";
370  
371     Vector<UChar> result;
372
373     for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
374         if (n->hasTagName(brTag))
375             result.append(&newlineCharacter, 1);
376         else if (n->isTextNode()) {
377             String data = static_cast<Text*>(n)->data();
378             result.append(data.characters(), data.length());
379         }
380     }
381
382     return finishText(result);
383 }
384
385 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
386 {
387     RootInlineBox* next;
388     for (; line; line = next) {
389         next = line->nextRootBox();
390         if (next && !line->endsWithBreak()) {
391             ASSERT(line->lineBreakObj());
392             breakNode = line->lineBreakObj()->node();
393             breakOffset = line->lineBreakPos();
394             line = next;
395             return;
396         }
397     }
398     breakNode = 0;
399     breakOffset = 0;
400 }
401
402 String RenderTextControl::textWithHardLineBreaks()
403 {
404     if (!m_innerText)
405         return "";
406
407     RenderBlock* renderer = toRenderBlock(m_innerText->renderer());
408     if (!renderer)
409         return "";
410
411     Node* breakNode;
412     unsigned breakOffset;
413     RootInlineBox* line = renderer->firstRootBox();
414     if (!line)
415         return "";
416
417     getNextSoftBreak(line, breakNode, breakOffset);
418
419     Vector<UChar> result;
420
421     for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
422         if (n->hasTagName(brTag))
423             result.append(&newlineCharacter, 1);
424         else if (n->isTextNode()) {
425             Text* text = static_cast<Text*>(n);
426             String data = text->data();
427             unsigned length = data.length();
428             unsigned position = 0;
429             while (breakNode == n && breakOffset <= length) {
430                 if (breakOffset > position) {
431                     result.append(data.characters() + position, breakOffset - position);
432                     position = breakOffset;
433                     result.append(&newlineCharacter, 1);
434                 }
435                 getNextSoftBreak(line, breakNode, breakOffset);
436             }
437             result.append(data.characters() + position, length - position);
438         }
439         while (breakNode == n)
440             getNextSoftBreak(line, breakNode, breakOffset);
441     }
442
443     return finishText(result);
444 }
445
446 int RenderTextControl::scrollbarThickness() const
447 {
448     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
449     return ScrollbarTheme::nativeTheme()->scrollbarThickness();
450 }
451
452 void RenderTextControl::computeLogicalHeight()
453 {
454     setHeight(m_innerText->renderBox()->borderTop() + m_innerText->renderBox()->borderBottom() +
455               m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() +
456               m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom());
457
458     adjustControlHeightBasedOnLineHeight(m_innerText->renderBox()->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes));
459     setHeight(height() + borderAndPaddingHeight());
460
461     // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
462     if (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
463         setHeight(height() + scrollbarThickness());
464
465     RenderBlock::computeLogicalHeight();
466 }
467
468 void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty)
469 {
470     result.setInnerNode(m_innerText.get());
471     result.setInnerNonSharedNode(m_innerText.get());
472     result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(),
473                                   yPos - ty - y() - m_innerText->renderBox()->y()));
474 }
475
476 void RenderTextControl::forwardEvent(Event* event)
477 {
478     if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
479         return;
480     m_innerText->defaultEventHandler(event);
481 }
482
483 static const char* fontFamiliesWithInvalidCharWidth[] = {
484     "American Typewriter",
485     "Arial Hebrew",
486     "Chalkboard",
487     "Cochin",
488     "Corsiva Hebrew",
489     "Courier",
490     "Euphemia UCAS",
491     "Geneva",
492     "Gill Sans",
493     "Hei",
494     "Helvetica",
495     "Hoefler Text",
496     "InaiMathi",
497     "Kai",
498     "Lucida Grande",
499     "Marker Felt",
500     "Monaco",
501     "Mshtakan",
502     "New Peninim MT",
503     "Osaka",
504     "Raanana",
505     "STHeiti",
506     "Symbol",
507     "Times",
508     "Apple Braille",
509     "Apple LiGothic",
510     "Apple LiSung",
511     "Apple Symbols",
512     "AppleGothic",
513     "AppleMyungjo",
514     "#GungSeo",
515     "#HeadLineA",
516     "#PCMyungjo",
517     "#PilGi",
518 };
519
520 // For font families where any of the fonts don't have a valid entry in the OS/2 table
521 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
522 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
523 // but, in order to get similar rendering across platforms, we do this check for
524 // all platforms.
525 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
526 {
527     static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
528
529     if (!fontFamiliesWithInvalidCharWidthMap) {
530         fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
531
532         for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
533             fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
534     }
535
536     return !fontFamiliesWithInvalidCharWidthMap->contains(family);
537 }
538
539 float RenderTextControl::getAvgCharWidth(AtomicString family)
540 {
541     if (hasValidAvgCharWidth(family))
542         return roundf(style()->font().primaryFont()->avgCharWidth());
543
544     const UChar ch = '0';
545     return style()->font().width(constructTextRunAllowTrailingExpansion(String(&ch, 1), style()));
546 }
547
548 float RenderTextControl::scaleEmToUnits(int x) const
549 {
550     // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
551     float unitsPerEm = 2048.0f;
552     return roundf(style()->font().size() * x / unitsPerEm);
553 }
554
555 void RenderTextControl::computePreferredLogicalWidths()
556 {
557     ASSERT(preferredLogicalWidthsDirty());
558
559     m_minPreferredLogicalWidth = 0;
560     m_maxPreferredLogicalWidth = 0;
561
562     if (style()->width().isFixed() && style()->width().value() > 0)
563         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
564     else {
565         // Use average character width. Matches IE.
566         AtomicString family = style()->font().family().family();
567         m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + m_innerText->renderBox()->paddingLeft() + m_innerText->renderBox()->paddingRight();
568     }
569
570     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
571         m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
572         m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
573     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
574         m_minPreferredLogicalWidth = 0;
575     else
576         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
577
578     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
579         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
580         m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
581     }
582
583     int toAdd = borderAndPaddingWidth();
584
585     m_minPreferredLogicalWidth += toAdd;
586     m_maxPreferredLogicalWidth += toAdd;
587
588     setPreferredLogicalWidthsDirty(false);
589 }
590
591 void RenderTextControl::selectionChanged(bool userTriggered)
592 {
593     cacheSelection(selectionStart(), selectionEnd());
594
595     if (Frame* frame = this->frame()) {
596         if (frame->selection()->isRange() && userTriggered)
597             node()->dispatchEvent(Event::create(eventNames().selectEvent, true, false));
598     }
599 }
600
601 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
602 {
603     if (width() && height())
604         rects.append(IntRect(tx, ty, width(), height()));
605 }
606
607 HTMLElement* RenderTextControl::innerTextElement() const
608 {
609     return m_innerText.get();
610 }
611
612 void RenderTextControl::updatePlaceholderVisibility(bool placeholderShouldBeVisible, bool placeholderValueChanged)
613 {
614     bool oldPlaceholderVisible = m_placeholderVisible;
615     m_placeholderVisible = placeholderShouldBeVisible;
616     if (oldPlaceholderVisible != m_placeholderVisible || placeholderValueChanged)
617         repaint();
618 }
619
620 void RenderTextControl::paintPlaceholder(PaintInfo& paintInfo, int tx, int ty)
621 {
622     if (style()->visibility() != VISIBLE)
623         return;
624     
625     IntRect clipRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop());
626     if (clipRect.isEmpty())
627         return;
628     
629     GraphicsContextStateSaver stateSaver(*paintInfo.context);
630     paintInfo.context->clip(clipRect);
631     
632     RefPtr<RenderStyle> placeholderStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER);
633     if (!placeholderStyle)
634         placeholderStyle = style();
635     
636     paintInfo.context->setFillColor(placeholderStyle->visitedDependentColor(CSSPropertyColor), placeholderStyle->colorSpace());
637     
638     String placeholderText = static_cast<HTMLTextFormControlElement*>(node())->strippedPlaceholder();
639     TextRun textRun(placeholderText.characters(), placeholderText.length(), false, 0, 0, TextRun::AllowTrailingExpansion, placeholderStyle->direction(), placeholderStyle->unicodeBidi() == Override);
640     
641     RenderBox* textRenderer = innerTextElement() ? innerTextElement()->renderBox() : 0;
642     if (textRenderer) {
643         IntPoint textPoint;
644         textPoint.setY(ty + textBlockInsetTop() + placeholderStyle->fontMetrics().ascent());
645         int styleTextIndent = placeholderStyle->textIndent().isFixed() ? placeholderStyle->textIndent().calcMinValue(0) : 0;
646         if (placeholderStyle->isLeftToRightDirection())
647             textPoint.setX(tx + styleTextIndent + textBlockInsetLeft());
648         else
649             textPoint.setX(tx + width() - textBlockInsetRight() - styleTextIndent - style()->font().width(textRun));
650         
651         paintInfo.context->drawBidiText(placeholderStyle->font(), textRun, textPoint);
652     }
653 }
654
655 void RenderTextControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
656 {    
657     if (m_placeholderVisible && paintInfo.phase == PaintPhaseForeground)
658         paintPlaceholder(paintInfo, tx, ty);
659     
660     RenderBlock::paintObject(paintInfo, tx, ty);
661 }
662
663 } // namespace WebCore