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