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 "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"
41 using namespace HTMLNames;
43 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
44 : RenderTextControl(element)
45 , m_shouldDrawCapsLockIndicator(false)
46 , m_desiredInnerEditorLogicalHeight(-1)
50 RenderTextControlSingleLine::~RenderTextControlSingleLine()
54 inline Element* RenderTextControlSingleLine::containerElement() const
56 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
59 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
61 return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
64 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
66 return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
69 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
71 RenderTextControl::paint(paintInfo, paintOffset);
73 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
74 LayoutRect contentsRect = contentBoxRect();
76 // Center in the block progression direction.
77 if (isHorizontalWritingMode())
78 contentsRect.setY((height() - contentsRect.height()) / 2);
80 contentsRect.setX((width() - contentsRect.width()) / 2);
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));
88 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
90 return containerElement() ? contentLogicalHeight() : logicalHeight();
93 void RenderTextControlSingleLine::layout()
95 SubtreeLayoutScope layoutScope(*this);
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.
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.
108 RenderBox* innerEditorRenderer = innerEditorElement()->renderBox();
109 RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
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);
119 if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
120 viewPortRenderer->style()->setLogicalHeight(Length(Auto));
121 layoutScope.setNeedsLayout(viewPortRenderer);
124 RenderBlockFlow::layoutBlock(false);
126 Element* container = containerElement();
127 RenderBox* containerRenderer = container ? container->renderBox() : 0;
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);
136 m_desiredInnerEditorLogicalHeight = desiredLogicalHeight;
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);
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);
156 containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
159 // If we need another layout pass, we have changed one of children's height so we need to relayout them.
161 RenderBlockFlow::layoutBlock(true);
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)));
168 centerContainerIfNeeded(containerRenderer);
170 HTMLElement* placeholderElement = inputElement()->placeholderElement();
171 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
172 LayoutSize innerEditorSize;
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);
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.
192 computeOverflow(clientLogicalBottom());
196 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
198 if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
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());
214 hitInnerEditorElement(result, pointInParent, accumulatedOffset);
219 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
221 m_desiredInnerEditorLogicalHeight = -1;
222 RenderTextControl::styleDidChange(diff, oldStyle);
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());
231 Element* container = containerElement();
232 if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
233 containerRenderer->style()->setHeight(Length());
234 containerRenderer->style()->setWidth(Length());
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);
244 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
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;
256 if (LocalFrame* frame = document().frame())
257 shouldDrawCapsLockIndicator = inputElement()->type() == InputTypeNames::password && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
259 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
260 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
261 setShouldDoFullPaintInvalidation();
265 bool RenderTextControlSingleLine::hasControlClip() const
267 // Apply control clip for text fields with decorations.
268 return !!containerElement();
271 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
273 ASSERT(hasControlClip());
274 LayoutRect clipRect = contentBoxRect();
275 if (containerElement()->renderBox())
276 clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
277 clipRect.moveBy(additionalOffset);
281 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
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);
290 return RenderTextControl::getAvgCharWidth(family);
293 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
296 bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
300 LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
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());
313 // For text inputs, IE adds some extra width.
314 if (maxCharWidth > 0.f)
315 result += maxCharWidth - charWidth;
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();
330 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
332 return lineHeight + nonContentHeight;
335 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerEditorStyle(const RenderStyle* startStyle) const
337 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
338 textBlockStyle->inheritFrom(startStyle);
339 adjustInnerEditorStyle(textBlockStyle.get());
341 textBlockStyle->setWhiteSpace(PRE);
342 textBlockStyle->setOverflowWrap(NormalOverflowWrap);
343 textBlockStyle->setOverflowX(OHIDDEN);
344 textBlockStyle->setOverflowY(OHIDDEN);
345 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
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());
353 textBlockStyle->setDisplay(BLOCK);
354 textBlockStyle->setUnique();
356 if (inputElement()->shouldRevealPassword())
357 textBlockStyle->setTextSecurity(TSNONE);
359 return textBlockStyle.release();
362 bool RenderTextControlSingleLine::textShouldBeTruncated() const
364 return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
367 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
369 RenderBox* renderer = innerEditorElement()->renderBox();
373 renderer->autoscroll(position);
376 LayoutUnit RenderTextControlSingleLine::scrollWidth() const
378 if (innerEditorElement())
379 return innerEditorElement()->scrollWidth();
380 return RenderBlockFlow::scrollWidth();
383 LayoutUnit RenderTextControlSingleLine::scrollHeight() const
385 if (innerEditorElement())
386 return innerEditorElement()->scrollHeight();
387 return RenderBlockFlow::scrollHeight();
390 LayoutUnit RenderTextControlSingleLine::scrollLeft() const
392 if (innerEditorElement())
393 return innerEditorElement()->scrollLeft();
394 return RenderBlockFlow::scrollLeft();
397 LayoutUnit RenderTextControlSingleLine::scrollTop() const
399 if (innerEditorElement())
400 return innerEditorElement()->scrollTop();
401 return RenderBlockFlow::scrollTop();
404 void RenderTextControlSingleLine::setScrollLeft(LayoutUnit newLeft)
406 if (innerEditorElement())
407 innerEditorElement()->setScrollLeft(newLeft);
410 void RenderTextControlSingleLine::setScrollTop(LayoutUnit newTop)
412 if (innerEditorElement())
413 innerEditorElement()->setScrollTop(newTop);
416 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
418 return toHTMLInputElement(node());