• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
disabledTextColor(const Color & textColor,const Color & backgroundColor)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 
RenderTextControl(Node * node,bool placeholderVisible)74 RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible)
75     : RenderBlock(node)
76     , m_placeholderVisible(placeholderVisible)
77     , m_lastChangeWasUserEdit(false)
78 {
79 }
80 
~RenderTextControl()81 RenderTextControl::~RenderTextControl()
82 {
83     // The children renderers have already been destroyed by destroyLeftoverChildren
84     if (m_innerText)
85         m_innerText->detach();
86 }
87 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)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 
setInnerTextStyle(PassRefPtr<RenderStyle> style)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 
updateUserModifyProperty(Node * node,RenderStyle * style)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 
adjustInnerTextStyle(const RenderStyle * startStyle,RenderStyle * textBlockStyle) const130 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 
createSubtreeIfNeeded(TextControlInnerElement * innerBlock)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 
textBlockHeight() const153 int RenderTextControl::textBlockHeight() const
154 {
155     return height() - borderAndPaddingHeight();
156 }
157 
textBlockWidth() const158 int RenderTextControl::textBlockWidth() const
159 {
160     return width() - borderAndPaddingWidth() - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
161 }
162 
updateFromElement()163 void RenderTextControl::updateFromElement()
164 {
165     updateUserModifyProperty(node(), m_innerText->renderer()->style());
166 }
167 
setInnerTextValue(const String & innerTextValue)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 
setLastChangeWasUserEdit(bool lastChangeWasUserEdit)197 void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
198 {
199     m_lastChangeWasUserEdit = lastChangeWasUserEdit;
200     document()->setIgnoreAutofocus(lastChangeWasUserEdit);
201 }
202 
selectionStart() const203 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 
selectionEnd() const216 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 
hasVisibleTextArea() const229 bool RenderTextControl::hasVisibleTextArea() const
230 {
231     return style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height();
232 }
233 
setSelectionRange(Node * node,int start,int end)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 
isSelectableElement(HTMLElement * innerText,Node * node)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 
setContainerAndOffsetForRange(Node * node,int offset,Node * & containerNode,int & offsetInContainer)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 
selection(int start,int end) const296 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 
visiblePositionForIndex(int index) const330 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 
indexForVisiblePosition(HTMLElement * innerTextElement,const VisiblePosition & pos)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 
subtreeHasChanged()361 void RenderTextControl::subtreeHasChanged()
362 {
363     m_lastChangeWasUserEdit = true;
364 }
365 
finishText(Vector<UChar> & result) const366 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 
text()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 
getNextSoftBreak(RootInlineBox * & line,Node * & breakNode,unsigned & breakOffset)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 
textWithHardLineBreaks()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 
scrollbarThickness() const456 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 
computeLogicalHeight()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 
hitInnerTextElement(HitTestResult & result,int xPos,int yPos,int tx,int ty)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 
forwardEvent(Event * event)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.
hasValidAvgCharWidth(AtomicString family)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 
getAvgCharWidth(AtomicString family)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 
scaleEmToUnits(int x) const558 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 
computePreferredLogicalWidths()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 
selectionChanged(bool userTriggered)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 
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)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 
innerTextElement() const617 HTMLElement* RenderTextControl::innerTextElement() const
618 {
619     return m_innerText.get();
620 }
621 
updatePlaceholderVisibility(bool placeholderShouldBeVisible,bool placeholderValueChanged)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 
paintPlaceholder(PaintInfo & paintInfo,int tx,int ty)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 
paintObject(PaintInfo & paintInfo,int tx,int ty)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