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 "RenderTextControlSingleLine.h"
27 #include "CSSFontSelector.h"
28 #include "CSSValueKeywords.h"
31 #include "FrameSelection.h"
32 #include "FrameView.h"
33 #include "HTMLNames.h"
34 #include "HitTestResult.h"
35 #include "LocalizedStrings.h"
37 #include "PlatformKeyboardEvent.h"
38 #include "RenderLayer.h"
39 #include "RenderScrollbar.h"
40 #include "RenderTheme.h"
42 #include "SimpleFontData.h"
43 #include "StyleResolver.h"
44 #include "TextControlInnerElements.h"
50 using namespace HTMLNames;
52 VisiblePosition RenderTextControlInnerBlock::positionForPoint(const LayoutPoint& point)
54 LayoutPoint contentsPoint(point);
56 // Multiline text controls have the scroll on shadowHost, so we need to take
57 // that into account here.
59 RenderTextControl* renderer = toRenderTextControl(node()->shadowHost()->renderer());
60 if (renderer->hasOverflowClip())
61 contentsPoint += renderer->scrolledContentOffset();
64 return RenderBlock::positionForPoint(contentsPoint);
67 // ----------------------------
69 RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node)
70 : RenderTextControl(node)
71 , m_shouldDrawCapsLockIndicator(false)
72 , m_desiredInnerTextHeight(-1)
74 ASSERT(node->isHTMLElement());
75 ASSERT(node->toInputElement());
78 RenderTextControlSingleLine::~RenderTextControlSingleLine()
82 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
84 return inputElement()->innerSpinButtonElement();
87 RenderStyle* RenderTextControlSingleLine::textBaseStyle() const
89 HTMLElement* innerBlock = innerBlockElement();
90 return innerBlock ? innerBlock->renderer()->style() : style();
93 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
95 RenderTextControl::paint(paintInfo, paintOffset);
97 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
98 LayoutRect contentsRect = contentBoxRect();
100 // Center vertically like the text.
101 contentsRect.setY((height() - contentsRect.height()) / 2);
103 // Convert the rect into the coords used for painting the content
104 contentsRect.moveBy(paintOffset + location());
105 theme()->paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect));
109 LayoutUnit RenderTextControlSingleLine::computeHeightLimit() const
111 return containerElement() ? contentHeight() : height();
114 void RenderTextControlSingleLine::layout()
116 // FIXME: We should remove the height-related hacks in layout() and
117 // styleDidChange(). We need them because
118 // - Center the inner elements vertically if the input height is taller than
119 // the intrinsic height of the inner elements.
120 // - Shrink the inner elment heights if the input height is samller than the
121 // intrinsic heights of the inner elements.
123 // We don't honor paddings and borders for textfields without decorations
124 // and type=search if the text height is taller than the contentHeight()
125 // because of compability.
127 RenderBox* innerTextRenderer = innerTextElement()->renderBox();
128 ASSERT(innerTextRenderer);
129 RenderBox* innerBlockRenderer = innerBlockElement() ? innerBlockElement()->renderBox() : 0;
131 // To ensure consistency between layouts, we need to reset any conditionally overriden height.
132 innerTextRenderer->style()->setHeight(Length(Auto));
133 if (innerBlockRenderer)
134 innerBlockRenderer->style()->setHeight(Length(Auto));
136 RenderBlock::layoutBlock(false);
138 HTMLElement* container = containerElement();
139 RenderBox* containerRenderer = container ? container->renderBox() : 0;
141 // Set the text block height
142 LayoutUnit desiredHeight = textBlockHeight();
143 LayoutUnit currentHeight = innerTextRenderer->height();
145 LayoutUnit heightLimit = computeHeightLimit();
146 if (currentHeight > heightLimit) {
147 if (desiredHeight != currentHeight)
148 setNeedsLayout(true, MarkOnlyThis);
150 innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed));
151 m_desiredInnerTextHeight = desiredHeight;
152 if (innerBlockRenderer)
153 innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed));
155 // The container might be taller because of decoration elements.
156 if (containerRenderer) {
157 containerRenderer->layoutIfNeeded();
158 LayoutUnit containerHeight = containerRenderer->height();
159 if (containerHeight > heightLimit) {
160 containerRenderer->style()->setHeight(Length(heightLimit, Fixed));
161 setNeedsLayout(true, MarkOnlyThis);
162 } else if (containerRenderer->height() < contentHeight()) {
163 containerRenderer->style()->setHeight(Length(contentHeight(), Fixed));
164 setNeedsLayout(true, MarkOnlyThis);
166 containerRenderer->style()->setHeight(Length(containerHeight, Fixed));
169 // If we need another layout pass, we have changed one of children's height so we need to relayout them.
171 RenderBlock::layoutBlock(true);
173 // Center the child block vertically
174 currentHeight = innerTextRenderer->height();
175 if (!container && currentHeight != contentHeight()) {
176 LayoutUnit heightDiff = currentHeight - contentHeight();
177 innerTextRenderer->setY(innerTextRenderer->y() - (heightDiff / 2 + layoutMod(heightDiff, 2)));
179 centerContainerIfNeeded(containerRenderer);
181 // Ignores the paddings for the inner spin button.
182 if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) {
183 RenderBox* parentBox = innerSpinBox->parentBox();
184 if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection())
185 innerSpinBox->setLocation(LayoutPoint(-paddingLeft(), -paddingTop()));
187 innerSpinBox->setLocation(LayoutPoint(parentBox->width() - innerSpinBox->width() + paddingRight(), -paddingTop()));
188 innerSpinBox->setHeight(height() - borderTop() - borderBottom());
191 HTMLElement* placeholderElement = inputElement()->placeholderElement();
192 if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
193 placeholderBox->style()->setWidth(Length(innerTextRenderer->width() - placeholderBox->borderAndPaddingWidth(), Fixed));
194 placeholderBox->style()->setHeight(Length(innerTextRenderer->height() - placeholderBox->borderAndPaddingHeight(), Fixed));
195 bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
196 placeholderBox->layoutIfNeeded();
197 LayoutPoint textOffset = innerTextRenderer->location();
198 if (innerBlockElement() && innerBlockElement()->renderBox())
199 textOffset += toLayoutSize(innerBlockElement()->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 RenderBlock::layoutBlockChild.
207 placeholderBox->repaint();
212 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
214 if (!RenderTextControl::nodeAtPoint(request, result, pointInContainer, accumulatedOffset, hitTestAction))
217 // Say that we hit the inner text element if
218 // - we hit a node inside the inner text element,
219 // - we hit the <input> element (e.g. we're over the border or padding), or
220 // - we hit regions not in any decoration buttons.
221 HTMLElement* container = containerElement();
222 if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) {
223 LayoutPoint pointInParent = pointInContainer.point();
224 if (container && innerBlockElement()) {
225 if (innerBlockElement()->renderBox())
226 pointInParent -= toLayoutSize(innerBlockElement()->renderBox()->location());
227 if (container->renderBox())
228 pointInParent -= toLayoutSize(container->renderBox()->location());
230 hitInnerTextElement(result, pointInParent, accumulatedOffset);
235 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
237 m_desiredInnerTextHeight = -1;
238 RenderTextControl::styleDidChange(diff, oldStyle);
240 // We may have set the width and the height in the old style in layout().
241 // Reset them now to avoid getting a spurious layout hint.
242 HTMLElement* innerBlock = innerBlockElement();
243 if (RenderObject* innerBlockRenderer = innerBlock ? innerBlock->renderer() : 0) {
244 innerBlockRenderer->style()->setHeight(Length());
245 innerBlockRenderer->style()->setWidth(Length());
247 HTMLElement* container = containerElement();
248 if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
249 containerRenderer->style()->setHeight(Length());
250 containerRenderer->style()->setWidth(Length());
252 if (HTMLElement* placeholder = inputElement()->placeholderElement())
253 placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip);
254 setHasOverflowClip(false);
257 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
259 if (!node() || !document())
262 // Only draw the caps lock indicator if these things are true:
263 // 1) The field is a password field
264 // 2) The frame is active
265 // 3) The element is focused
266 // 4) The caps lock is on
267 bool shouldDrawCapsLockIndicator = false;
269 if (Frame* frame = document()->frame())
270 shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
271 && frame->selection()->isFocusedAndActive()
272 && document()->focusedNode() == node()
273 && PlatformKeyboardEvent::currentCapsLockState();
275 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
276 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
281 bool RenderTextControlSingleLine::hasControlClip() const
283 // Apply control clip for text fields with decorations.
284 return !!containerElement();
287 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
289 ASSERT(hasControlClip());
290 LayoutRect clipRect = unionRect(contentBoxRect(), containerElement()->renderBox()->frameRect());
291 clipRect.moveBy(additionalOffset);
295 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
297 // Since Lucida Grande is the default font, we want this to match the width
298 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
299 // IE for some encodings (in IE, the default font is encoding specific).
300 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
301 if (family == AtomicString("Lucida Grande"))
302 return scaleEmToUnits(901);
304 return RenderTextControl::getAvgCharWidth(family);
307 LayoutUnit RenderTextControlSingleLine::preferredContentWidth(float charWidth) const
310 bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
314 LayoutUnit result = static_cast<LayoutUnit>(ceiledLayoutUnit(charWidth * factor));
316 float maxCharWidth = 0.f;
317 AtomicString family = style()->font().family().family();
318 // Since Lucida Grande is the default font, we want this to match the width
319 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
320 // IE for some encodings (in IE, the default font is encoding specific).
321 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
322 if (family == AtomicString("Lucida Grande"))
323 maxCharWidth = scaleEmToUnits(4027);
324 else if (hasValidAvgCharWidth(family))
325 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
327 // For text inputs, IE adds some extra width.
328 if (maxCharWidth > 0.f)
329 result += maxCharWidth - charWidth;
331 if (includesDecoration) {
332 HTMLElement* spinButton = innerSpinButtonElement();
333 if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) {
334 result += spinRenderer->borderLeft() + spinRenderer->borderRight() +
335 spinRenderer->paddingLeft() + spinRenderer->paddingRight();
336 // Since the width of spinRenderer is not calculated yet, spinRenderer->width() returns 0.
337 // So computedStyle()->width() is used instead.
338 result += spinButton->computedStyle()->width().value();
345 LayoutUnit RenderTextControlSingleLine::computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
347 return lineHeight + nonContentHeight;
350 void RenderTextControlSingleLine::updateFromElement()
352 RenderTextControl::updateFromElement();
355 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
357 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
358 textBlockStyle->inheritFrom(startStyle);
359 adjustInnerTextStyle(startStyle, textBlockStyle.get());
361 textBlockStyle->setWhiteSpace(PRE);
362 textBlockStyle->setOverflowWrap(NormalOverflowWrap);
363 textBlockStyle->setOverflowX(OHIDDEN);
364 textBlockStyle->setOverflowY(OHIDDEN);
365 textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
367 if (m_desiredInnerTextHeight >= 0)
368 textBlockStyle->setHeight(Length(m_desiredInnerTextHeight, Fixed));
369 // Do not allow line-height to be smaller than our default.
370 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
371 textBlockStyle->setLineHeight(RenderStyle::initialLineHeight());
373 textBlockStyle->setDisplay(BLOCK);
375 return textBlockStyle.release();
378 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
380 RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
381 innerBlockStyle->inheritFrom(startStyle);
383 innerBlockStyle->setBoxFlex(1);
384 innerBlockStyle->setDisplay(BLOCK);
385 innerBlockStyle->setDirection(LTR);
387 // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable.
388 innerBlockStyle->setUserModify(READ_ONLY);
390 return innerBlockStyle.release();
393 bool RenderTextControlSingleLine::textShouldBeTruncated() const
395 return document()->focusedNode() != node()
396 && style()->textOverflow() == TextOverflowEllipsis;
399 void RenderTextControlSingleLine::autoscroll()
401 RenderLayer* layer = innerTextElement()->renderBox()->layer();
406 int RenderTextControlSingleLine::scrollWidth() const
408 if (innerTextElement())
409 return innerTextElement()->scrollWidth();
410 return RenderBlock::scrollWidth();
413 int RenderTextControlSingleLine::scrollHeight() const
415 if (innerTextElement())
416 return innerTextElement()->scrollHeight();
417 return RenderBlock::scrollHeight();
420 int RenderTextControlSingleLine::scrollLeft() const
422 if (innerTextElement())
423 return innerTextElement()->scrollLeft();
424 return RenderBlock::scrollLeft();
427 int RenderTextControlSingleLine::scrollTop() const
429 if (innerTextElement())
430 return innerTextElement()->scrollTop();
431 return RenderBlock::scrollTop();
434 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
436 if (innerTextElement())
437 innerTextElement()->setScrollLeft(newLeft);
440 void RenderTextControlSingleLine::setScrollTop(int newTop)
442 if (innerTextElement())
443 innerTextElement()->setScrollTop(newTop);
446 bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
448 RenderLayer* layer = innerTextElement()->renderBox()->layer();
449 if (layer && layer->scroll(direction, granularity, multiplier))
451 return RenderBlock::scroll(direction, granularity, multiplier, stopNode);
454 bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
456 RenderLayer* layer = innerTextElement()->renderBox()->layer();
457 if (layer && layer->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier))
459 return RenderBlock::logicalScroll(direction, granularity, multiplier, stopNode);
462 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
464 return node()->toInputElement();