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