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