1 /** 2 * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. 3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 4 * Copyright (C) 2010 Google Inc. All rights reserved. 5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #include "config.h" 25 #include "core/rendering/RenderTextControlSingleLine.h" 26 27 #include "CSSValueKeywords.h" 28 #include "core/editing/FrameSelection.h" 29 #include "core/page/Frame.h" 30 #include "core/platform/PlatformKeyboardEvent.h" 31 #include "core/platform/graphics/SimpleFontData.h" 32 #include "core/rendering/HitTestResult.h" 33 #include "core/rendering/RenderLayer.h" 34 #include "core/rendering/RenderTheme.h" 35 36 using namespace std; 37 38 namespace WebCore { 39 40 using namespace HTMLNames; 41 42 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element) 43 : RenderTextControl(element) 44 , m_shouldDrawCapsLockIndicator(false) 45 , m_desiredInnerTextLogicalHeight(-1) 46 { 47 ASSERT(element->hasTagName(inputTag)); 48 } 49 50 RenderTextControlSingleLine::~RenderTextControlSingleLine() 51 { 52 } 53 54 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const 55 { 56 return inputElement()->innerSpinButtonElement(); 57 } 58 59 RenderStyle* RenderTextControlSingleLine::textBaseStyle() const 60 { 61 HTMLElement* innerBlock = innerBlockElement(); 62 return innerBlock ? innerBlock->renderer()->style() : style(); 63 } 64 65 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 66 { 67 RenderTextControl::paint(paintInfo, paintOffset); 68 69 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { 70 LayoutRect contentsRect = contentBoxRect(); 71 72 // Center in the block progression direction. 73 if (isHorizontalWritingMode()) 74 contentsRect.setY((height() - contentsRect.height()) / 2); 75 else 76 contentsRect.setX((width() - contentsRect.width()) / 2); 77 78 // Convert the rect into the coords used for painting the content 79 contentsRect.moveBy(paintOffset + location()); 80 theme()->paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect)); 81 } 82 } 83 84 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const 85 { 86 return containerElement() ? contentLogicalHeight() : logicalHeight(); 87 } 88 89 // 'end' must be an ancestor of 'start.' 90 static void setNeedsLayoutInRange(RenderObject* start, RenderObject* end) 91 { 92 ASSERT(start); 93 ASSERT(start != end); 94 for (RenderObject* renderer = start; renderer != end; renderer = renderer->parent()) 95 renderer->setNeedsLayout(MarkOnlyThis); 96 } 97 98 void RenderTextControlSingleLine::layout() 99 { 100 StackStats::LayoutCheckPoint layoutCheckPoint; 101 102 // FIXME: We should remove the height-related hacks in layout() and 103 // styleDidChange(). We need them because 104 // - Center the inner elements vertically if the input height is taller than 105 // the intrinsic height of the inner elements. 106 // - Shrink the inner elment heights if the input height is samller than the 107 // intrinsic heights of the inner elements. 108 109 // We don't honor paddings and borders for textfields without decorations 110 // and type=search if the text height is taller than the contentHeight() 111 // because of compability. 112 113 RenderBox* innerTextRenderer = innerTextElement()->renderBox(); 114 RenderBox* innerBlockRenderer = innerBlockElement() ? innerBlockElement()->renderBox() : 0; 115 116 // To ensure consistency between layouts, we need to reset any conditionally overriden height. 117 if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) { 118 innerTextRenderer->style()->setLogicalHeight(Length(Auto)); 119 setNeedsLayoutInRange(innerTextRenderer, this); 120 } 121 if (innerBlockRenderer && !innerBlockRenderer->style()->logicalHeight().isAuto()) { 122 innerBlockRenderer->style()->setLogicalHeight(Length(Auto)); 123 setNeedsLayoutInRange(innerBlockRenderer, this); 124 } 125 126 RenderBlock::layoutBlock(false); 127 128 HTMLElement* container = containerElement(); 129 RenderBox* containerRenderer = container ? container->renderBox() : 0; 130 131 // Set the text block height 132 LayoutUnit desiredLogicalHeight = textBlockLogicalHeight(); 133 LayoutUnit logicalHeightLimit = computeLogicalHeightLimit(); 134 if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) { 135 if (desiredLogicalHeight != innerTextRenderer->logicalHeight()) 136 setNeedsLayout(MarkOnlyThis); 137 138 m_desiredInnerTextLogicalHeight = desiredLogicalHeight; 139 140 innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); 141 innerTextRenderer->setNeedsLayout(MarkOnlyThis); 142 if (innerBlockRenderer) { 143 innerBlockRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); 144 innerBlockRenderer->setNeedsLayout(MarkOnlyThis); 145 } 146 } 147 // The container might be taller because of decoration elements. 148 if (containerRenderer) { 149 containerRenderer->layoutIfNeeded(); 150 LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight(); 151 if (containerLogicalHeight > logicalHeightLimit) { 152 containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed)); 153 setNeedsLayout(MarkOnlyThis); 154 } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) { 155 containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed)); 156 setNeedsLayout(MarkOnlyThis); 157 } else 158 containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed)); 159 } 160 161 // If we need another layout pass, we have changed one of children's height so we need to relayout them. 162 if (needsLayout()) 163 RenderBlock::layoutBlock(true); 164 165 // Center the child block in the block progression direction (vertical centering for horizontal text fields). 166 if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) { 167 LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight(); 168 innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2))); 169 } else 170 centerContainerIfNeeded(containerRenderer); 171 172 // Ignores the paddings for the inner spin button. 173 if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) { 174 RenderBox* parentBox = innerSpinBox->parentBox(); 175 if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection()) 176 innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore())); 177 else 178 innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore())); 179 innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter()); 180 } 181 182 HTMLElement* placeholderElement = inputElement()->placeholderElement(); 183 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) { 184 LayoutSize innerTextSize; 185 if (innerTextRenderer) 186 innerTextSize = innerTextRenderer->size(); 187 placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed)); 188 placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed)); 189 bool neededLayout = placeholderBox->needsLayout(); 190 bool placeholderBoxHadLayout = placeholderBox->everHadLayout(); 191 placeholderBox->layoutIfNeeded(); 192 LayoutPoint textOffset; 193 if (innerTextRenderer) 194 textOffset = innerTextRenderer->location(); 195 if (innerBlockElement() && innerBlockElement()->renderBox()) 196 textOffset += toLayoutSize(innerBlockElement()->renderBox()->location()); 197 if (containerRenderer) 198 textOffset += toLayoutSize(containerRenderer->location()); 199 placeholderBox->setLocation(textOffset); 200 201 if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) { 202 // This assumes a shadow tree without floats. If floats are added, the 203 // logic should be shared with RenderBlock::layoutBlockChild. 204 placeholderBox->repaint(); 205 } 206 // The placeholder gets layout last, after the parent text control and its other children, 207 // so in order to get the correct overflow from the placeholder we need to recompute it now. 208 if (neededLayout) 209 computeOverflow(clientLogicalBottom()); 210 } 211 } 212 213 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 214 { 215 if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction)) 216 return false; 217 218 // Say that we hit the inner text element if 219 // - we hit a node inside the inner text element, 220 // - we hit the <input> element (e.g. we're over the border or padding), or 221 // - we hit regions not in any decoration buttons. 222 HTMLElement* container = containerElement(); 223 if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) { 224 LayoutPoint pointInParent = locationInContainer.point(); 225 if (container && innerBlockElement()) { 226 if (innerBlockElement()->renderBox()) 227 pointInParent -= toLayoutSize(innerBlockElement()->renderBox()->location()); 228 if (container->renderBox()) 229 pointInParent -= toLayoutSize(container->renderBox()->location()); 230 } 231 hitInnerTextElement(result, pointInParent, accumulatedOffset); 232 } 233 return true; 234 } 235 236 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 237 { 238 m_desiredInnerTextLogicalHeight = -1; 239 RenderTextControl::styleDidChange(diff, oldStyle); 240 241 // We may have set the width and the height in the old style in layout(). 242 // Reset them now to avoid getting a spurious layout hint. 243 HTMLElement* innerBlock = innerBlockElement(); 244 if (RenderObject* innerBlockRenderer = innerBlock ? innerBlock->renderer() : 0) { 245 innerBlockRenderer->style()->setHeight(Length()); 246 innerBlockRenderer->style()->setWidth(Length()); 247 } 248 HTMLElement* container = containerElement(); 249 if (RenderObject* containerRenderer = container ? container->renderer() : 0) { 250 containerRenderer->style()->setHeight(Length()); 251 containerRenderer->style()->setWidth(Length()); 252 } 253 RenderObject* innerTextRenderer = innerTextElement()->renderer(); 254 if (innerTextRenderer && diff == StyleDifferenceLayout) 255 innerTextRenderer->setNeedsLayout(MarkContainingBlockChain); 256 if (HTMLElement* placeholder = inputElement()->placeholderElement()) 257 placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip); 258 setHasOverflowClip(false); 259 } 260 261 void RenderTextControlSingleLine::capsLockStateMayHaveChanged() 262 { 263 if (!node() || !document()) 264 return; 265 266 // Only draw the caps lock indicator if these things are true: 267 // 1) The field is a password field 268 // 2) The frame is active 269 // 3) The element is focused 270 // 4) The caps lock is on 271 bool shouldDrawCapsLockIndicator = false; 272 273 if (Frame* frame = document()->frame()) 274 shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection()->isFocusedAndActive() && document()->focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState(); 275 276 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { 277 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; 278 repaint(); 279 } 280 } 281 282 bool RenderTextControlSingleLine::hasControlClip() const 283 { 284 // Apply control clip for text fields with decorations. 285 return !!containerElement(); 286 } 287 288 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const 289 { 290 ASSERT(hasControlClip()); 291 LayoutRect clipRect = contentBoxRect(); 292 if (containerElement()->renderBox()) 293 clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect()); 294 clipRect.moveBy(additionalOffset); 295 return clipRect; 296 } 297 298 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family) 299 { 300 // Since Lucida Grande is the default font, we want this to match the width 301 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 302 // IE for some encodings (in IE, the default font is encoding specific). 303 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg. 304 if (family == "Lucida Grande") 305 return scaleEmToUnits(901); 306 307 return RenderTextControl::getAvgCharWidth(family); 308 } 309 310 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const 311 { 312 int factor; 313 bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor); 314 if (factor <= 0) 315 factor = 20; 316 317 LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor); 318 319 float maxCharWidth = 0.f; 320 AtomicString family = style()->font().family().family(); 321 // Since Lucida Grande is the default font, we want this to match the width 322 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 323 // IE for some encodings (in IE, the default font is encoding specific). 324 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg. 325 if (family == "Lucida Grande") 326 maxCharWidth = scaleEmToUnits(4027); 327 else if (hasValidAvgCharWidth(family)) 328 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth()); 329 330 // For text inputs, IE adds some extra width. 331 if (maxCharWidth > 0.f) 332 result += maxCharWidth - charWidth; 333 334 if (includesDecoration) { 335 HTMLElement* spinButton = innerSpinButtonElement(); 336 if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) { 337 result += spinRenderer->borderAndPaddingLogicalWidth(); 338 // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0. 339 // So computedStyle()->logicalWidth() is used instead. 340 result += spinButton->computedStyle()->logicalWidth().value(); 341 } 342 } 343 344 return result; 345 } 346 347 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const 348 { 349 return lineHeight + nonContentHeight; 350 } 351 352 void RenderTextControlSingleLine::updateFromElement() 353 { 354 RenderTextControl::updateFromElement(); 355 } 356 357 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const 358 { 359 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create(); 360 textBlockStyle->inheritFrom(startStyle); 361 adjustInnerTextStyle(textBlockStyle.get()); 362 363 textBlockStyle->setWhiteSpace(PRE); 364 textBlockStyle->setOverflowWrap(NormalOverflowWrap); 365 textBlockStyle->setOverflowX(OHIDDEN); 366 textBlockStyle->setOverflowY(OHIDDEN); 367 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip); 368 369 if (m_desiredInnerTextLogicalHeight >= 0) 370 textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed)); 371 // Do not allow line-height to be smaller than our default. 372 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)) 373 textBlockStyle->setLineHeight(RenderStyle::initialLineHeight()); 374 375 textBlockStyle->setDisplay(BLOCK); 376 377 return textBlockStyle.release(); 378 } 379 380 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const 381 { 382 RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create(); 383 innerBlockStyle->inheritFrom(startStyle); 384 385 innerBlockStyle->setFlexGrow(1); 386 // min-width: 0; is needed for correct shrinking. 387 // FIXME: Remove this line when https://bugs.webkit.org/show_bug.cgi?id=111790 is fixed. 388 innerBlockStyle->setMinWidth(Length(0, Fixed)); 389 innerBlockStyle->setDisplay(BLOCK); 390 innerBlockStyle->setDirection(LTR); 391 392 // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable. 393 innerBlockStyle->setUserModify(READ_ONLY); 394 395 return innerBlockStyle.release(); 396 } 397 398 bool RenderTextControlSingleLine::textShouldBeTruncated() const 399 { 400 return document()->focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis; 401 } 402 403 void RenderTextControlSingleLine::autoscroll(const IntPoint& position) 404 { 405 RenderBox* renderer = innerTextElement()->renderBox(); 406 if (!renderer) 407 return; 408 RenderLayer* layer = renderer->layer(); 409 if (layer) 410 layer->autoscroll(position); 411 } 412 413 int RenderTextControlSingleLine::scrollWidth() const 414 { 415 if (innerTextElement()) 416 return innerTextElement()->scrollWidth(); 417 return RenderBlock::scrollWidth(); 418 } 419 420 int RenderTextControlSingleLine::scrollHeight() const 421 { 422 if (innerTextElement()) 423 return innerTextElement()->scrollHeight(); 424 return RenderBlock::scrollHeight(); 425 } 426 427 int RenderTextControlSingleLine::scrollLeft() const 428 { 429 if (innerTextElement()) 430 return innerTextElement()->scrollLeft(); 431 return RenderBlock::scrollLeft(); 432 } 433 434 int RenderTextControlSingleLine::scrollTop() const 435 { 436 if (innerTextElement()) 437 return innerTextElement()->scrollTop(); 438 return RenderBlock::scrollTop(); 439 } 440 441 void RenderTextControlSingleLine::setScrollLeft(int newLeft) 442 { 443 if (innerTextElement()) 444 innerTextElement()->setScrollLeft(newLeft); 445 } 446 447 void RenderTextControlSingleLine::setScrollTop(int newTop) 448 { 449 if (innerTextElement()) 450 innerTextElement()->setScrollTop(newTop); 451 } 452 453 bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 454 { 455 RenderBox* renderer = innerTextElement()->renderBox(); 456 if (!renderer) 457 return false; 458 RenderLayer* layer = renderer->layer(); 459 if (layer && layer->scroll(direction, granularity, multiplier)) 460 return true; 461 return RenderBlock::scroll(direction, granularity, multiplier, stopNode); 462 } 463 464 bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 465 { 466 RenderLayer* layer = innerTextElement()->renderBox()->layer(); 467 if (layer && layer->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) 468 return true; 469 return RenderBlock::logicalScroll(direction, granularity, multiplier, stopNode); 470 } 471 472 HTMLInputElement* RenderTextControlSingleLine::inputElement() const 473 { 474 return toHTMLInputElement(node()); 475 } 476 477 } 478