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