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).
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.
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.
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.
25 #include "core/rendering/RenderTextControlSingleLine.h"
27 #include "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/LayoutRectRecorder.h"
34 #include "core/rendering/RenderLayer.h"
35 #include "core/rendering/RenderTheme.h"
36 #include "platform/PlatformKeyboardEvent.h"
37 #include "platform/fonts/SimpleFontData.h"
43 using namespace HTMLNames;
45 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
46 : RenderTextControl(element)
47 , m_shouldDrawCapsLockIndicator(false)
48 , m_desiredInnerTextLogicalHeight(-1)
52 RenderTextControlSingleLine::~RenderTextControlSingleLine()
56 inline Element* RenderTextControlSingleLine::containerElement() const
58 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
61 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
63 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
66 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
68 return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
71 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
73 RenderTextControl::paint(paintInfo, paintOffset);
75 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
76 LayoutRect contentsRect = contentBoxRect();
78 // Center in the block progression direction.
79 if (isHorizontalWritingMode())
80 contentsRect.setY((height() - contentsRect.height()) / 2);
82 contentsRect.setX((width() - contentsRect.width()) / 2);
84 // Convert the rect into the coords used for painting the content
85 contentsRect.moveBy(paintOffset + location());
86 RenderTheme::theme().paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect));
90 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
92 return containerElement() ? contentLogicalHeight() : logicalHeight();
95 void RenderTextControlSingleLine::layout()
97 LayoutRectRecorder recorder(*this);
98 SubtreeLayoutScope layoutScope(this);
100 // FIXME: We should remove the height-related hacks in layout() and
101 // styleDidChange(). We need them because
102 // - Center the inner elements vertically if the input height is taller than
103 // the intrinsic height of the inner elements.
104 // - Shrink the inner elment heights if the input height is samller than the
105 // intrinsic heights of the inner elements.
107 // We don't honor paddings and borders for textfields without decorations
108 // and type=search if the text height is taller than the contentHeight()
109 // because of compability.
111 RenderBox* innerTextRenderer = innerTextElement()->renderBox();
112 RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
114 // To ensure consistency between layouts, we need to reset any conditionally overriden height.
115 if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) {
116 innerTextRenderer->style()->setLogicalHeight(Length(Auto));
117 layoutScope.setNeedsLayout(innerTextRenderer);
118 HTMLElement* placeholderElement = inputElement()->placeholderElement();
119 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0)
120 layoutScope.setNeedsLayout(placeholderBox);
122 if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
123 viewPortRenderer->style()->setLogicalHeight(Length(Auto));
124 layoutScope.setNeedsLayout(viewPortRenderer);
127 RenderBlockFlow::layoutBlock(false);
129 Element* container = containerElement();
130 RenderBox* containerRenderer = container ? container->renderBox() : 0;
132 // Set the text block height
133 LayoutUnit desiredLogicalHeight = textBlockLogicalHeight();
134 LayoutUnit logicalHeightLimit = computeLogicalHeightLimit();
135 if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) {
136 if (desiredLogicalHeight != innerTextRenderer->logicalHeight())
137 layoutScope.setNeedsLayout(this);
139 m_desiredInnerTextLogicalHeight = desiredLogicalHeight;
141 innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
142 layoutScope.setNeedsLayout(innerTextRenderer);
143 if (viewPortRenderer) {
144 viewPortRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
145 layoutScope.setNeedsLayout(viewPortRenderer);
148 // The container might be taller because of decoration elements.
149 if (containerRenderer) {
150 containerRenderer->layoutIfNeeded();
151 LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight();
152 if (containerLogicalHeight > logicalHeightLimit) {
153 containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed));
154 layoutScope.setNeedsLayout(this);
155 } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) {
156 containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed));
157 layoutScope.setNeedsLayout(this);
159 containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
162 // If we need another layout pass, we have changed one of children's height so we need to relayout them.
164 RenderBlockFlow::layoutBlock(true);
166 // Center the child block in the block progression direction (vertical centering for horizontal text fields).
167 if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) {
168 LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight();
169 innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2)));
171 centerContainerIfNeeded(containerRenderer);
173 // Ignores the paddings for the inner spin button.
174 if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) {
175 RenderBox* parentBox = innerSpinBox->parentBox();
176 if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection())
177 innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore()));
179 innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore()));
180 innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter());
183 HTMLElement* placeholderElement = inputElement()->placeholderElement();
184 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
185 LayoutSize innerTextSize;
186 LayoutRectRecorder placeholderBoxRecorder(*placeholderBox);
188 if (innerTextRenderer)
189 innerTextSize = innerTextRenderer->size();
190 placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed));
191 placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed));
192 bool neededLayout = placeholderBox->needsLayout();
193 bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
194 placeholderBox->layoutIfNeeded();
195 LayoutPoint textOffset;
196 if (innerTextRenderer)
197 textOffset = innerTextRenderer->location();
198 if (editingViewPortElement() && editingViewPortElement()->renderBox())
199 textOffset += toLayoutSize(editingViewPortElement()->renderBox()->location());
200 if (containerRenderer)
201 textOffset += toLayoutSize(containerRenderer->location());
202 placeholderBox->setLocation(textOffset);
204 if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) {
205 // This assumes a shadow tree without floats. If floats are added, the
206 // logic should be shared with RenderBlockFlow::layoutBlockChild.
207 placeholderBox->repaint();
209 // The placeholder gets layout last, after the parent text control and its other children,
210 // so in order to get the correct overflow from the placeholder we need to recompute it now.
212 computeOverflow(clientLogicalBottom());
216 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
218 if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
221 // Say that we hit the inner text element if
222 // - we hit a node inside the inner text element,
223 // - we hit the <input> element (e.g. we're over the border or padding), or
224 // - we hit regions not in any decoration buttons.
225 Element* container = containerElement();
226 if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) {
227 LayoutPoint pointInParent = locationInContainer.point();
228 if (container && editingViewPortElement()) {
229 if (editingViewPortElement()->renderBox())
230 pointInParent -= toLayoutSize(editingViewPortElement()->renderBox()->location());
231 if (container->renderBox())
232 pointInParent -= toLayoutSize(container->renderBox()->location());
234 hitInnerTextElement(result, pointInParent, accumulatedOffset);
239 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
241 m_desiredInnerTextLogicalHeight = -1;
242 RenderTextControl::styleDidChange(diff, oldStyle);
244 // We may have set the width and the height in the old style in layout().
245 // Reset them now to avoid getting a spurious layout hint.
246 Element* viewPort = editingViewPortElement();
247 if (RenderObject* viewPortRenderer = viewPort ? viewPort->renderer() : 0) {
248 viewPortRenderer->style()->setHeight(Length());
249 viewPortRenderer->style()->setWidth(Length());
251 Element* container = containerElement();
252 if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
253 containerRenderer->style()->setHeight(Length());
254 containerRenderer->style()->setWidth(Length());
256 RenderObject* innerTextRenderer = innerTextElement()->renderer();
257 if (innerTextRenderer && diff == StyleDifferenceLayout)
258 innerTextRenderer->setNeedsLayout();
259 if (HTMLElement* placeholder = inputElement()->placeholderElement())
260 placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip);
261 setHasOverflowClip(false);
264 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
269 // Only draw the caps lock indicator if these things are true:
270 // 1) The field is a password field
271 // 2) The frame is active
272 // 3) The element is focused
273 // 4) The caps lock is on
274 bool shouldDrawCapsLockIndicator = false;
276 if (LocalFrame* frame = document().frame())
277 shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
279 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
280 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
285 bool RenderTextControlSingleLine::hasControlClip() const
287 // Apply control clip for text fields with decorations.
288 return !!containerElement();
291 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
293 ASSERT(hasControlClip());
294 LayoutRect clipRect = contentBoxRect();
295 if (containerElement()->renderBox())
296 clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
297 clipRect.moveBy(additionalOffset);
301 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
303 // Since Lucida Grande is the default font, we want this to match the width
304 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
305 // IE for some encodings (in IE, the default font is encoding specific).
306 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
307 if (family == "Lucida Grande")
308 return scaleEmToUnits(901);
310 return RenderTextControl::getAvgCharWidth(family);
313 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
316 bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
320 LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
322 float maxCharWidth = 0.f;
323 AtomicString family = style()->font().fontDescription().family().family();
324 // Since Lucida Grande is the default font, we want this to match the width
325 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
326 // IE for some encodings (in IE, the default font is encoding specific).
327 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
328 if (family == "Lucida Grande")
329 maxCharWidth = scaleEmToUnits(4027);
330 else if (hasValidAvgCharWidth(family))
331 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
333 // For text inputs, IE adds some extra width.
334 if (maxCharWidth > 0.f)
335 result += maxCharWidth - charWidth;
337 if (includesDecoration) {
338 HTMLElement* spinButton = innerSpinButtonElement();
339 if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) {
340 result += spinRenderer->borderAndPaddingLogicalWidth();
341 // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0.
342 // So computedStyle()->logicalWidth() is used instead.
343 result += spinButton->computedStyle()->logicalWidth().value();
350 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
352 return lineHeight + nonContentHeight;
355 void RenderTextControlSingleLine::updateFromElement()
357 RenderTextControl::updateFromElement();
360 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
362 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
363 textBlockStyle->inheritFrom(startStyle);
364 adjustInnerTextStyle(textBlockStyle.get());
366 textBlockStyle->setWhiteSpace(PRE);
367 textBlockStyle->setOverflowWrap(NormalOverflowWrap);
368 textBlockStyle->setOverflowX(OHIDDEN);
369 textBlockStyle->setOverflowY(OHIDDEN);
370 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
372 if (m_desiredInnerTextLogicalHeight >= 0)
373 textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed));
374 // Do not allow line-height to be smaller than our default.
375 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
376 textBlockStyle->setLineHeight(RenderStyle::initialLineHeight());
378 textBlockStyle->setDisplay(BLOCK);
379 textBlockStyle->setUnique();
381 if (inputElement()->shouldRevealPassword())
382 textBlockStyle->setTextSecurity(TSNONE);
384 return textBlockStyle.release();
387 bool RenderTextControlSingleLine::textShouldBeTruncated() const
389 return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
392 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
394 RenderBox* renderer = innerTextElement()->renderBox();
398 renderer->autoscroll(position);
401 int RenderTextControlSingleLine::scrollWidth() const
403 if (innerTextElement())
404 return innerTextElement()->scrollWidth();
405 return RenderBlockFlow::scrollWidth();
408 int RenderTextControlSingleLine::scrollHeight() const
410 if (innerTextElement())
411 return innerTextElement()->scrollHeight();
412 return RenderBlockFlow::scrollHeight();
415 int RenderTextControlSingleLine::scrollLeft() const
417 if (innerTextElement())
418 return innerTextElement()->scrollLeft();
419 return RenderBlockFlow::scrollLeft();
422 int RenderTextControlSingleLine::scrollTop() const
424 if (innerTextElement())
425 return innerTextElement()->scrollTop();
426 return RenderBlockFlow::scrollTop();
429 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
431 if (innerTextElement())
432 innerTextElement()->setScrollLeft(newLeft);
435 void RenderTextControlSingleLine::setScrollTop(int newTop)
437 if (innerTextElement())
438 innerTextElement()->setScrollTop(newTop);
441 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
443 return toHTMLInputElement(node());