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 "core/CSSValueKeywords.h" 28 #include "core/InputTypeNames.h" 29 #include "core/dom/shadow/ShadowRoot.h" 30 #include "core/editing/FrameSelection.h" 31 #include "core/frame/LocalFrame.h" 32 #include "core/html/shadow/ShadowElementNames.h" 33 #include "core/rendering/HitTestResult.h" 34 #include "core/rendering/RenderLayer.h" 35 #include "core/rendering/RenderTheme.h" 36 #include "platform/PlatformKeyboardEvent.h" 37 #include "platform/fonts/SimpleFontData.h" 38 39 namespace blink { 40 41 using namespace HTMLNames; 42 43 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element) 44 : RenderTextControl(element) 45 , m_shouldDrawCapsLockIndicator(false) 46 , m_desiredInnerEditorLogicalHeight(-1) 47 { 48 } 49 50 RenderTextControlSingleLine::~RenderTextControlSingleLine() 51 { 52 } 53 54 inline Element* RenderTextControlSingleLine::containerElement() const 55 { 56 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer()); 57 } 58 59 inline Element* RenderTextControlSingleLine::editingViewPortElement() const 60 { 61 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort()); 62 } 63 64 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const 65 { 66 return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton())); 67 } 68 69 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 70 { 71 RenderTextControl::paint(paintInfo, paintOffset); 72 73 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { 74 LayoutRect contentsRect = contentBoxRect(); 75 76 // Center in the block progression direction. 77 if (isHorizontalWritingMode()) 78 contentsRect.setY((height() - contentsRect.height()) / 2); 79 else 80 contentsRect.setX((width() - contentsRect.width()) / 2); 81 82 // Convert the rect into the coords used for painting the content 83 contentsRect.moveBy(paintOffset + location()); 84 RenderTheme::theme().paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect)); 85 } 86 } 87 88 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const 89 { 90 return containerElement() ? contentLogicalHeight() : logicalHeight(); 91 } 92 93 void RenderTextControlSingleLine::layout() 94 { 95 SubtreeLayoutScope layoutScope(*this); 96 97 // FIXME: We should remove the height-related hacks in layout() and 98 // styleDidChange(). We need them because 99 // - Center the inner elements vertically if the input height is taller than 100 // the intrinsic height of the inner elements. 101 // - Shrink the inner elment heights if the input height is samller than the 102 // intrinsic heights of the inner elements. 103 104 // We don't honor paddings and borders for textfields without decorations 105 // and type=search if the text height is taller than the contentHeight() 106 // because of compability. 107 108 RenderBox* innerEditorRenderer = innerEditorElement()->renderBox(); 109 RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0; 110 111 // To ensure consistency between layouts, we need to reset any conditionally overriden height. 112 if (innerEditorRenderer && !innerEditorRenderer->style()->logicalHeight().isAuto()) { 113 innerEditorRenderer->style()->setLogicalHeight(Length(Auto)); 114 layoutScope.setNeedsLayout(innerEditorRenderer); 115 HTMLElement* placeholderElement = inputElement()->placeholderElement(); 116 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) 117 layoutScope.setNeedsLayout(placeholderBox); 118 } 119 if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) { 120 viewPortRenderer->style()->setLogicalHeight(Length(Auto)); 121 layoutScope.setNeedsLayout(viewPortRenderer); 122 } 123 124 RenderBlockFlow::layoutBlock(false); 125 126 Element* container = containerElement(); 127 RenderBox* containerRenderer = container ? container->renderBox() : 0; 128 129 // Set the text block height 130 LayoutUnit desiredLogicalHeight = textBlockLogicalHeight(); 131 LayoutUnit logicalHeightLimit = computeLogicalHeightLimit(); 132 if (innerEditorRenderer && innerEditorRenderer->logicalHeight() > logicalHeightLimit) { 133 if (desiredLogicalHeight != innerEditorRenderer->logicalHeight()) 134 layoutScope.setNeedsLayout(this); 135 136 m_desiredInnerEditorLogicalHeight = desiredLogicalHeight; 137 138 innerEditorRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); 139 layoutScope.setNeedsLayout(innerEditorRenderer); 140 if (viewPortRenderer) { 141 viewPortRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); 142 layoutScope.setNeedsLayout(viewPortRenderer); 143 } 144 } 145 // The container might be taller because of decoration elements. 146 if (containerRenderer) { 147 containerRenderer->layoutIfNeeded(); 148 LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight(); 149 if (containerLogicalHeight > logicalHeightLimit) { 150 containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed)); 151 layoutScope.setNeedsLayout(this); 152 } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) { 153 containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed)); 154 layoutScope.setNeedsLayout(this); 155 } else 156 containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed)); 157 } 158 159 // If we need another layout pass, we have changed one of children's height so we need to relayout them. 160 if (needsLayout()) 161 RenderBlockFlow::layoutBlock(true); 162 163 // Center the child block in the block progression direction (vertical centering for horizontal text fields). 164 if (!container && innerEditorRenderer && innerEditorRenderer->height() != contentLogicalHeight()) { 165 LayoutUnit logicalHeightDiff = innerEditorRenderer->logicalHeight() - contentLogicalHeight(); 166 innerEditorRenderer->setLogicalTop(innerEditorRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2))); 167 } else 168 centerContainerIfNeeded(containerRenderer); 169 170 HTMLElement* placeholderElement = inputElement()->placeholderElement(); 171 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) { 172 LayoutSize innerEditorSize; 173 174 if (innerEditorRenderer) 175 innerEditorSize = innerEditorRenderer->size(); 176 placeholderBox->style()->setWidth(Length(innerEditorSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed)); 177 placeholderBox->style()->setHeight(Length(innerEditorSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed)); 178 bool neededLayout = placeholderBox->needsLayout(); 179 placeholderBox->layoutIfNeeded(); 180 LayoutPoint textOffset; 181 if (innerEditorRenderer) 182 textOffset = innerEditorRenderer->location(); 183 if (editingViewPortElement() && editingViewPortElement()->renderBox()) 184 textOffset += toLayoutSize(editingViewPortElement()->renderBox()->location()); 185 if (containerRenderer) 186 textOffset += toLayoutSize(containerRenderer->location()); 187 placeholderBox->setLocation(textOffset); 188 189 // The placeholder gets layout last, after the parent text control and its other children, 190 // so in order to get the correct overflow from the placeholder we need to recompute it now. 191 if (neededLayout) 192 computeOverflow(clientLogicalBottom()); 193 } 194 } 195 196 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 197 { 198 if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction)) 199 return false; 200 201 // Say that we hit the inner text element if 202 // - we hit a node inside the inner text element, 203 // - we hit the <input> element (e.g. we're over the border or padding), or 204 // - we hit regions not in any decoration buttons. 205 Element* container = containerElement(); 206 if (result.innerNode()->isDescendantOf(innerEditorElement()) || result.innerNode() == node() || (container && container == result.innerNode())) { 207 LayoutPoint pointInParent = locationInContainer.point(); 208 if (container && editingViewPortElement()) { 209 if (editingViewPortElement()->renderBox()) 210 pointInParent -= toLayoutSize(editingViewPortElement()->renderBox()->location()); 211 if (container->renderBox()) 212 pointInParent -= toLayoutSize(container->renderBox()->location()); 213 } 214 hitInnerEditorElement(result, pointInParent, accumulatedOffset); 215 } 216 return true; 217 } 218 219 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 220 { 221 m_desiredInnerEditorLogicalHeight = -1; 222 RenderTextControl::styleDidChange(diff, oldStyle); 223 224 // We may have set the width and the height in the old style in layout(). 225 // Reset them now to avoid getting a spurious layout hint. 226 Element* viewPort = editingViewPortElement(); 227 if (RenderObject* viewPortRenderer = viewPort ? viewPort->renderer() : 0) { 228 viewPortRenderer->style()->setHeight(Length()); 229 viewPortRenderer->style()->setWidth(Length()); 230 } 231 Element* container = containerElement(); 232 if (RenderObject* containerRenderer = container ? container->renderer() : 0) { 233 containerRenderer->style()->setHeight(Length()); 234 containerRenderer->style()->setWidth(Length()); 235 } 236 RenderObject* innerEditorRenderer = innerEditorElement()->renderer(); 237 if (innerEditorRenderer && diff.needsFullLayout()) 238 innerEditorRenderer->setNeedsLayoutAndFullPaintInvalidation(); 239 if (HTMLElement* placeholder = inputElement()->placeholderElement()) 240 placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip); 241 setHasOverflowClip(false); 242 } 243 244 void RenderTextControlSingleLine::capsLockStateMayHaveChanged() 245 { 246 if (!node()) 247 return; 248 249 // Only draw the caps lock indicator if these things are true: 250 // 1) The field is a password field 251 // 2) The frame is active 252 // 3) The element is focused 253 // 4) The caps lock is on 254 bool shouldDrawCapsLockIndicator = false; 255 256 if (LocalFrame* frame = document().frame()) 257 shouldDrawCapsLockIndicator = inputElement()->type() == InputTypeNames::password && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState(); 258 259 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { 260 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; 261 setShouldDoFullPaintInvalidation(true); 262 } 263 } 264 265 bool RenderTextControlSingleLine::hasControlClip() const 266 { 267 // Apply control clip for text fields with decorations. 268 return !!containerElement(); 269 } 270 271 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const 272 { 273 ASSERT(hasControlClip()); 274 LayoutRect clipRect = contentBoxRect(); 275 if (containerElement()->renderBox()) 276 clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect()); 277 clipRect.moveBy(additionalOffset); 278 return clipRect; 279 } 280 281 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family) 282 { 283 // Since Lucida Grande is the default font, we want this to match the width 284 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 285 // IE for some encodings (in IE, the default font is encoding specific). 286 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg. 287 if (family == "Lucida Grande") 288 return scaleEmToUnits(901); 289 290 return RenderTextControl::getAvgCharWidth(family); 291 } 292 293 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const 294 { 295 int factor; 296 bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor); 297 if (factor <= 0) 298 factor = 20; 299 300 LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor); 301 302 float maxCharWidth = 0.f; 303 AtomicString family = style()->font().fontDescription().family().family(); 304 // Since Lucida Grande is the default font, we want this to match the width 305 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 306 // IE for some encodings (in IE, the default font is encoding specific). 307 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg. 308 if (family == "Lucida Grande") 309 maxCharWidth = scaleEmToUnits(4027); 310 else if (hasValidAvgCharWidth(family)) 311 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth()); 312 313 // For text inputs, IE adds some extra width. 314 if (maxCharWidth > 0.f) 315 result += maxCharWidth - charWidth; 316 317 if (includesDecoration) { 318 HTMLElement* spinButton = innerSpinButtonElement(); 319 if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) { 320 result += spinRenderer->borderAndPaddingLogicalWidth(); 321 // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0. 322 // So computedStyle()->logicalWidth() is used instead. 323 result += spinButton->computedStyle()->logicalWidth().value(); 324 } 325 } 326 327 return result; 328 } 329 330 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const 331 { 332 return lineHeight + nonContentHeight; 333 } 334 335 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerEditorStyle(const RenderStyle* startStyle) const 336 { 337 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create(); 338 textBlockStyle->inheritFrom(startStyle); 339 adjustInnerEditorStyle(textBlockStyle.get()); 340 341 textBlockStyle->setWhiteSpace(PRE); 342 textBlockStyle->setOverflowWrap(NormalOverflowWrap); 343 textBlockStyle->setOverflowX(OHIDDEN); 344 textBlockStyle->setOverflowY(OHIDDEN); 345 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip); 346 347 if (m_desiredInnerEditorLogicalHeight >= 0) 348 textBlockStyle->setLogicalHeight(Length(m_desiredInnerEditorLogicalHeight, Fixed)); 349 // Do not allow line-height to be smaller than our default. 350 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)) 351 textBlockStyle->setLineHeight(RenderStyle::initialLineHeight()); 352 353 textBlockStyle->setDisplay(BLOCK); 354 textBlockStyle->setUnique(); 355 356 if (inputElement()->shouldRevealPassword()) 357 textBlockStyle->setTextSecurity(TSNONE); 358 359 return textBlockStyle.release(); 360 } 361 362 bool RenderTextControlSingleLine::textShouldBeTruncated() const 363 { 364 return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis; 365 } 366 367 void RenderTextControlSingleLine::autoscroll(const IntPoint& position) 368 { 369 RenderBox* renderer = innerEditorElement()->renderBox(); 370 if (!renderer) 371 return; 372 373 renderer->autoscroll(position); 374 } 375 376 LayoutUnit RenderTextControlSingleLine::scrollWidth() const 377 { 378 if (innerEditorElement()) 379 return innerEditorElement()->scrollWidth(); 380 return RenderBlockFlow::scrollWidth(); 381 } 382 383 LayoutUnit RenderTextControlSingleLine::scrollHeight() const 384 { 385 if (innerEditorElement()) 386 return innerEditorElement()->scrollHeight(); 387 return RenderBlockFlow::scrollHeight(); 388 } 389 390 LayoutUnit RenderTextControlSingleLine::scrollLeft() const 391 { 392 if (innerEditorElement()) 393 return innerEditorElement()->scrollLeft(); 394 return RenderBlockFlow::scrollLeft(); 395 } 396 397 LayoutUnit RenderTextControlSingleLine::scrollTop() const 398 { 399 if (innerEditorElement()) 400 return innerEditorElement()->scrollTop(); 401 return RenderBlockFlow::scrollTop(); 402 } 403 404 void RenderTextControlSingleLine::setScrollLeft(LayoutUnit newLeft) 405 { 406 if (innerEditorElement()) 407 innerEditorElement()->setScrollLeft(newLeft); 408 } 409 410 void RenderTextControlSingleLine::setScrollTop(LayoutUnit newTop) 411 { 412 if (innerEditorElement()) 413 innerEditorElement()->setScrollTop(newTop); 414 } 415 416 HTMLInputElement* RenderTextControlSingleLine::inputElement() const 417 { 418 return toHTMLInputElement(node()); 419 } 420 421 } 422