| OLD | NEW |
| (Empty) |
| 1 /** | |
| 2 * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. | |
| 3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.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/layout/HitTestResult.h" | |
| 34 #include "core/layout/Layer.h" | |
| 35 #include "core/layout/LayoutTheme.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* eleme
nt) | |
| 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(ShadowElementNa
mes::textFieldContainer()); | |
| 57 } | |
| 58 | |
| 59 inline Element* RenderTextControlSingleLine::editingViewPortElement() const | |
| 60 { | |
| 61 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNa
mes::editingViewPort()); | |
| 62 } | |
| 63 | |
| 64 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const | |
| 65 { | |
| 66 return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(S
hadowElementNames::spinButton())); | |
| 67 } | |
| 68 | |
| 69 void RenderTextControlSingleLine::paint(const PaintInfo& paintInfo, const Layout
Point& paintOffset) | |
| 70 { | |
| 71 RenderTextControl::paint(paintInfo, paintOffset); | |
| 72 | |
| 73 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndi
cator) { | |
| 74 LayoutRect contentsRect = contentBoxRect(); | |
| 75 | |
| 76 // Center in the block progression direction. | |
| 77 if (isHorizontalWritingMode()) | |
| 78 contentsRect.setY((size().height() - contentsRect.height()) / 2); | |
| 79 else | |
| 80 contentsRect.setX((size().width() - contentsRect.width()) / 2); | |
| 81 | |
| 82 // Convert the rect into the coords used for painting the content | |
| 83 contentsRect.moveBy(paintOffset + location()); | |
| 84 LayoutTheme::theme().paintCapsLockIndicator(this, paintInfo, pixelSnappe
dIntRect(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() ? editingViewPortElem
ent()->renderBox() : 0; | |
| 110 | |
| 111 // To ensure consistency between layouts, we need to reset any conditionally
overriden height. | |
| 112 if (innerEditorRenderer && !innerEditorRenderer->style()->logicalHeight().is
Auto()) { | |
| 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() > logicalHei
ghtLimit) { | |
| 133 if (desiredLogicalHeight != innerEditorRenderer->logicalHeight()) | |
| 134 layoutScope.setNeedsLayout(this); | |
| 135 | |
| 136 m_desiredInnerEditorLogicalHeight = desiredLogicalHeight; | |
| 137 | |
| 138 innerEditorRenderer->style()->setLogicalHeight(Length(desiredLogicalHeig
ht, Fixed)); | |
| 139 layoutScope.setNeedsLayout(innerEditorRenderer); | |
| 140 if (viewPortRenderer) { | |
| 141 viewPortRenderer->style()->setLogicalHeight(Length(desiredLogicalHei
ght, 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(logicalHeightLim
it, Fixed)); | |
| 151 layoutScope.setNeedsLayout(this); | |
| 152 } else if (containerRenderer->logicalHeight() < contentLogicalHeight())
{ | |
| 153 containerRenderer->style()->setLogicalHeight(Length(contentLogicalHe
ight(), Fixed)); | |
| 154 layoutScope.setNeedsLayout(this); | |
| 155 } else | |
| 156 containerRenderer->style()->setLogicalHeight(Length(containerLogical
Height, 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 cente
ring for horizontal text fields). | |
| 164 if (!container && innerEditorRenderer && innerEditorRenderer->size().height(
) != contentLogicalHeight()) { | |
| 165 LayoutUnit logicalHeightDiff = innerEditorRenderer->logicalHeight() - co
ntentLogicalHeight(); | |
| 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->ren
derBox() : 0) { | |
| 172 LayoutSize innerEditorSize; | |
| 173 | |
| 174 if (innerEditorRenderer) | |
| 175 innerEditorSize = innerEditorRenderer->size(); | |
| 176 placeholderBox->style()->setWidth(Length(innerEditorSize.width() - place
holderBox->borderAndPaddingWidth(), Fixed)); | |
| 177 placeholderBox->style()->setHeight(Length(innerEditorSize.height() - pla
ceholderBox->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()->lo
cation()); | |
| 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 i
ts 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, Hit
TestResult& result, const HitTestLocation& locationInContainer, const LayoutPoin
t& accumulatedOffset, HitTestAction hitTestAction) | |
| 197 { | |
| 198 if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, ac
cumulatedOffset, 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.inner
Node() == node() || (container && container == result.innerNode())) { | |
| 207 LayoutPoint pointInParent = locationInContainer.point(); | |
| 208 if (container && editingViewPortElement()) { | |
| 209 if (editingViewPortElement()->renderBox()) | |
| 210 pointInParent -= toLayoutSize(editingViewPortElement()->renderBo
x()->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 Lay
outStyle* 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 (LayoutObject* viewPortRenderer = viewPort ? viewPort->renderer() : 0) { | |
| 228 viewPortRenderer->style()->setHeight(Length()); | |
| 229 viewPortRenderer->style()->setWidth(Length()); | |
| 230 } | |
| 231 Element* container = containerElement(); | |
| 232 if (LayoutObject* containerRenderer = container ? container->renderer() : 0)
{ | |
| 233 containerRenderer->style()->setHeight(Length()); | |
| 234 containerRenderer->style()->setWidth(Length()); | |
| 235 } | |
| 236 LayoutObject* innerEditorRenderer = innerEditorElement()->renderer(); | |
| 237 if (innerEditorRenderer && diff.needsFullLayout()) | |
| 238 innerEditorRenderer->setNeedsLayoutAndFullPaintInvalidation(); | |
| 239 if (HTMLElement* placeholder = inputElement()->placeholderElement()) | |
| 240 placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldB
eTruncated() ? 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(); | |
| 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& addit
ionalOffset) const | |
| 272 { | |
| 273 ASSERT(hasControlClip()); | |
| 274 LayoutRect clipRect = contentBoxRect(); | |
| 275 if (containerElement()->renderBox()) | |
| 276 clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRec
t()); | |
| 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 an
d | |
| 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 charW
idth) 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 an
d | |
| 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, spinRender
er->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 l
ineHeight, LayoutUnit nonContentHeight) const | |
| 331 { | |
| 332 return lineHeight + nonContentHeight; | |
| 333 } | |
| 334 | |
| 335 PassRefPtr<LayoutStyle> RenderTextControlSingleLine::createInnerEditorStyle(cons
t LayoutStyle& startStyle) const | |
| 336 { | |
| 337 RefPtr<LayoutStyle> textBlockStyle = LayoutStyle::create(); | |
| 338 textBlockStyle->inheritFrom(startStyle); | |
| 339 adjustInnerEditorStyle(*textBlockStyle); | |
| 340 | |
| 341 textBlockStyle->setWhiteSpace(PRE); | |
| 342 textBlockStyle->setOverflowWrap(NormalOverflowWrap); | |
| 343 textBlockStyle->setOverflowX(OHIDDEN); | |
| 344 textBlockStyle->setOverflowY(OHIDDEN); | |
| 345 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllips
is : TextOverflowClip); | |
| 346 | |
| 347 if (m_desiredInnerEditorLogicalHeight >= 0) | |
| 348 textBlockStyle->setLogicalHeight(Length(m_desiredInnerEditorLogicalHeigh
t, Fixed)); | |
| 349 // Do not allow line-height to be smaller than our default. | |
| 350 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, Horizonta
lLine, PositionOfInteriorLineBoxes)) | |
| 351 textBlockStyle->setLineHeight(LayoutStyle::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() == T
extOverflowEllipsis; | |
| 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 (RenderBox* inner = innerEditorElement() ? innerEditorElement()->renderBo
x() : 0) { | |
| 379 // Adjust scrollWidth to inculde input element horizontal paddings and | |
| 380 // decoration width | |
| 381 LayoutUnit adjustment = clientWidth() - inner->clientWidth(); | |
| 382 return innerEditorElement()->scrollWidth() + adjustment; | |
| 383 } | |
| 384 return RenderBlockFlow::scrollWidth(); | |
| 385 } | |
| 386 | |
| 387 LayoutUnit RenderTextControlSingleLine::scrollHeight() const | |
| 388 { | |
| 389 if (RenderBox* inner = innerEditorElement() ? innerEditorElement()->renderBo
x() : 0) { | |
| 390 // Adjust scrollHeight to include input element vertical paddings and | |
| 391 // decoration height | |
| 392 LayoutUnit adjustment = clientHeight() - inner->clientHeight(); | |
| 393 return innerEditorElement()->scrollHeight() + adjustment; | |
| 394 } | |
| 395 return RenderBlockFlow::scrollHeight(); | |
| 396 } | |
| 397 | |
| 398 LayoutUnit RenderTextControlSingleLine::scrollLeft() const | |
| 399 { | |
| 400 if (innerEditorElement()) | |
| 401 return innerEditorElement()->scrollLeft(); | |
| 402 return RenderBlockFlow::scrollLeft(); | |
| 403 } | |
| 404 | |
| 405 LayoutUnit RenderTextControlSingleLine::scrollTop() const | |
| 406 { | |
| 407 if (innerEditorElement()) | |
| 408 return innerEditorElement()->scrollTop(); | |
| 409 return RenderBlockFlow::scrollTop(); | |
| 410 } | |
| 411 | |
| 412 void RenderTextControlSingleLine::setScrollLeft(LayoutUnit newLeft) | |
| 413 { | |
| 414 if (innerEditorElement()) | |
| 415 innerEditorElement()->setScrollLeft(newLeft); | |
| 416 } | |
| 417 | |
| 418 void RenderTextControlSingleLine::setScrollTop(LayoutUnit newTop) | |
| 419 { | |
| 420 if (innerEditorElement()) | |
| 421 innerEditorElement()->setScrollTop(newTop); | |
| 422 } | |
| 423 | |
| 424 HTMLInputElement* RenderTextControlSingleLine::inputElement() const | |
| 425 { | |
| 426 return toHTMLInputElement(node()); | |
| 427 } | |
| 428 | |
| 429 } | |
| OLD | NEW |