Home | History | Annotate | Download | only in rendering
      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 
     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 
    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 
    216 int 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 
    229 bool RenderTextControl::hasVisibleTextArea() const
    230 {
    231     return style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height();
    232 }
    233 
    234 void 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 
    269 bool 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 
    285 static 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 
    296 PassRefPtr<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 
    330 VisiblePosition 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 
    347 int 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 
    361 void RenderTextControl::subtreeHasChanged()
    362 {
    363     m_lastChangeWasUserEdit = true;
    364 }
    365 
    366 String 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 
    376 String 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 
    395 static 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 
    412 String 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 
    456 int 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 
    462 void 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 
    478 void 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 
    486 void RenderTextControl::forwardEvent(Event* event)
    487 {
    488     if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
    489         return;
    490     m_innerText->defaultEventHandler(event);
    491 }
    492 
    493 static 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.
    535 bool 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 
    549 float 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 
    558 float 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 
    565 void 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 
    601 void 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 
    611 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
    612 {
    613     if (width() && height())
    614         rects.append(IntRect(tx, ty, width(), height()));
    615 }
    616 
    617 HTMLElement* RenderTextControl::innerTextElement() const
    618 {
    619     return m_innerText.get();
    620 }
    621 
    622 void 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 
    630 void 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 
    666 void 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