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 "RenderTextControlSingleLine.h"
26
27#include "CSSFontSelector.h"
28#include "CSSValueKeywords.h"
29#include "Chrome.h"
30#include "Frame.h"
31#include "FrameSelection.h"
32#include "FrameView.h"
33#include "HTMLNames.h"
34#include "HitTestResult.h"
35#include "LocalizedStrings.h"
36#include "Page.h"
37#include "PlatformKeyboardEvent.h"
38#include "RenderLayer.h"
39#include "RenderScrollbar.h"
40#include "RenderTheme.h"
41#include "Settings.h"
42#include "SimpleFontData.h"
43#include "StyleResolver.h"
44#include "TextControlInnerElements.h"
45#include <wtf/StackStats.h>
46
47using namespace std;
48
49namespace WebCore {
50
51using namespace HTMLNames;
52
53RenderTextControlSingleLine::RenderTextControlSingleLine(Element* element)
54    : RenderTextControl(element)
55    , m_shouldDrawCapsLockIndicator(false)
56    , m_desiredInnerTextLogicalHeight(-1)
57{
58    ASSERT(element->isHTMLElement());
59    ASSERT(element->toInputElement());
60}
61
62RenderTextControlSingleLine::~RenderTextControlSingleLine()
63{
64}
65
66inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
67{
68    return inputElement()->innerSpinButtonElement();
69}
70
71RenderStyle* RenderTextControlSingleLine::textBaseStyle() const
72{
73    HTMLElement* innerBlock = innerBlockElement();
74    return innerBlock ? innerBlock->renderer()->style() : style();
75}
76
77void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
78{
79    RenderTextControl::paint(paintInfo, paintOffset);
80
81    if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
82        LayoutRect contentsRect = contentBoxRect();
83
84        // Center in the block progression direction.
85        if (isHorizontalWritingMode())
86            contentsRect.setY((height() - contentsRect.height()) / 2);
87        else
88            contentsRect.setX((width() - contentsRect.width()) / 2);
89
90        // Convert the rect into the coords used for painting the content
91        contentsRect.moveBy(paintOffset + location());
92        theme()->paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect));
93    }
94}
95
96LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
97{
98    return containerElement() ? contentLogicalHeight() : logicalHeight();
99}
100
101static void setNeedsLayoutOnAncestors(RenderObject* start, RenderObject* ancestor)
102{
103    ASSERT(start != ancestor);
104    for (RenderObject* renderer = start; renderer != ancestor; renderer = renderer->parent()) {
105        ASSERT(renderer);
106        renderer->setNeedsLayout(true, MarkOnlyThis);
107    }
108}
109
110void RenderTextControlSingleLine::layout()
111{
112    StackStats::LayoutCheckPoint layoutCheckPoint;
113
114    // FIXME: We should remove the height-related hacks in layout() and
115    // styleDidChange(). We need them because
116    // - Center the inner elements vertically if the input height is taller than
117    //   the intrinsic height of the inner elements.
118    // - Shrink the inner elment heights if the input height is samller than the
119    //   intrinsic heights of the inner elements.
120
121    // We don't honor paddings and borders for textfields without decorations
122    // and type=search if the text height is taller than the contentHeight()
123    // because of compability.
124
125    RenderBox* innerTextRenderer = innerTextElement()->renderBox();
126    RenderBox* innerBlockRenderer = innerBlockElement() ? innerBlockElement()->renderBox() : 0;
127
128    // To ensure consistency between layouts, we need to reset any conditionally overriden height.
129    if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) {
130        innerTextRenderer->style()->setLogicalHeight(Length(Auto));
131        setNeedsLayoutOnAncestors(innerTextRenderer, this);
132    }
133    if (innerBlockRenderer && !innerBlockRenderer->style()->logicalHeight().isAuto()) {
134        innerBlockRenderer->style()->setLogicalHeight(Length(Auto));
135        setNeedsLayoutOnAncestors(innerBlockRenderer, this);
136    }
137
138    RenderBlock::layoutBlock(false);
139
140    HTMLElement* container = containerElement();
141    RenderBox* containerRenderer = container ? container->renderBox() : 0;
142
143    // Set the text block height
144    LayoutUnit desiredLogicalHeight = textBlockLogicalHeight();
145    LayoutUnit logicalHeightLimit = computeLogicalHeightLimit();
146    if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) {
147        if (desiredLogicalHeight != innerTextRenderer->logicalHeight())
148            setNeedsLayout(true, MarkOnlyThis);
149
150        m_desiredInnerTextLogicalHeight = desiredLogicalHeight;
151
152        innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
153        innerTextRenderer->setNeedsLayout(true, MarkOnlyThis);
154        if (innerBlockRenderer) {
155            innerBlockRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
156            innerBlockRenderer->setNeedsLayout(true, MarkOnlyThis);
157        }
158    }
159    // The container might be taller because of decoration elements.
160    if (containerRenderer) {
161        containerRenderer->layoutIfNeeded();
162        LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight();
163        if (containerLogicalHeight > logicalHeightLimit) {
164            containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed));
165            setNeedsLayout(true, MarkOnlyThis);
166        } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) {
167            containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed));
168            setNeedsLayout(true, MarkOnlyThis);
169        } else
170            containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
171    }
172
173    // If we need another layout pass, we have changed one of children's height so we need to relayout them.
174    if (needsLayout())
175        RenderBlock::layoutBlock(true);
176
177    // Center the child block in the block progression direction (vertical centering for horizontal text fields).
178    if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) {
179        LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight();
180        innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2)));
181    } else
182        centerContainerIfNeeded(containerRenderer);
183
184    // Ignores the paddings for the inner spin button.
185    if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) {
186        RenderBox* parentBox = innerSpinBox->parentBox();
187        if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection())
188            innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore()));
189        else
190            innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore()));
191        innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter());
192    }
193
194    HTMLElement* placeholderElement = inputElement()->placeholderElement();
195    if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
196        LayoutSize innerTextSize;
197        if (innerTextRenderer)
198            innerTextSize = innerTextRenderer->size();
199        placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed));
200        placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed));
201        bool neededLayout = placeholderBox->needsLayout();
202        bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
203        placeholderBox->layoutIfNeeded();
204        LayoutPoint textOffset;
205        if (innerTextRenderer)
206            textOffset = innerTextRenderer->location();
207        if (innerBlockElement() && innerBlockElement()->renderBox())
208            textOffset += toLayoutSize(innerBlockElement()->renderBox()->location());
209        if (containerRenderer)
210            textOffset += toLayoutSize(containerRenderer->location());
211        placeholderBox->setLocation(textOffset);
212
213        if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) {
214            // This assumes a shadow tree without floats. If floats are added, the
215            // logic should be shared with RenderBlock::layoutBlockChild.
216            placeholderBox->repaint();
217        }
218        // The placeholder gets layout last, after the parent text control and its other children,
219        // so in order to get the correct overflow from the placeholder we need to recompute it now.
220        if (neededLayout)
221            computeOverflow(clientLogicalBottom());
222    }
223}
224
225bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
226{
227    if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
228        return false;
229
230    // Say that we hit the inner text element if
231    //  - we hit a node inside the inner text element,
232    //  - we hit the <input> element (e.g. we're over the border or padding), or
233    //  - we hit regions not in any decoration buttons.
234    HTMLElement* container = containerElement();
235    if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) {
236        LayoutPoint pointInParent = locationInContainer.point();
237        if (container && innerBlockElement()) {
238            if (innerBlockElement()->renderBox())
239                pointInParent -= toLayoutSize(innerBlockElement()->renderBox()->location());
240            if (container->renderBox())
241                pointInParent -= toLayoutSize(container->renderBox()->location());
242        }
243        hitInnerTextElement(result, pointInParent, accumulatedOffset);
244    }
245    return true;
246}
247
248void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
249{
250    m_desiredInnerTextLogicalHeight = -1;
251    RenderTextControl::styleDidChange(diff, oldStyle);
252
253    // We may have set the width and the height in the old style in layout().
254    // Reset them now to avoid getting a spurious layout hint.
255    HTMLElement* innerBlock = innerBlockElement();
256    if (RenderObject* innerBlockRenderer = innerBlock ? innerBlock->renderer() : 0) {
257        innerBlockRenderer->style()->setHeight(Length());
258        innerBlockRenderer->style()->setWidth(Length());
259    }
260    HTMLElement* container = containerElement();
261    if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
262        containerRenderer->style()->setHeight(Length());
263        containerRenderer->style()->setWidth(Length());
264    }
265    RenderObject* innerTextRenderer = innerTextElement()->renderer();
266    if (innerTextRenderer && diff == StyleDifferenceLayout)
267        innerTextRenderer->setNeedsLayout(true, MarkContainingBlockChain);
268    if (HTMLElement* placeholder = inputElement()->placeholderElement())
269        placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip);
270    setHasOverflowClip(false);
271}
272
273void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
274{
275    if (!node() || !document())
276        return;
277
278    // Only draw the caps lock indicator if these things are true:
279    // 1) The field is a password field
280    // 2) The frame is active
281    // 3) The element is focused
282    // 4) The caps lock is on
283    bool shouldDrawCapsLockIndicator = false;
284
285    if (Frame* frame = document()->frame())
286        shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
287                                      && frame->selection()->isFocusedAndActive()
288                                      && document()->focusedElement() == node()
289                                      && PlatformKeyboardEvent::currentCapsLockState();
290
291    if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
292        m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
293        repaint();
294    }
295}
296
297bool RenderTextControlSingleLine::hasControlClip() const
298{
299    // Apply control clip for text fields with decorations.
300    return !!containerElement();
301}
302
303LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
304{
305    ASSERT(hasControlClip());
306    LayoutRect clipRect = contentBoxRect();
307    if (containerElement()->renderBox())
308        clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
309    clipRect.moveBy(additionalOffset);
310    return clipRect;
311}
312
313float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
314{
315    // Since Lucida Grande is the default font, we want this to match the width
316    // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
317    // IE for some encodings (in IE, the default font is encoding specific).
318    // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
319    if (family == "Lucida Grande")
320        return scaleEmToUnits(901);
321
322    return RenderTextControl::getAvgCharWidth(family);
323}
324
325LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
326{
327    int factor;
328    bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
329    if (factor <= 0)
330        factor = 20;
331
332    LayoutUnit result = static_cast<LayoutUnit>(ceiledLayoutUnit(charWidth * factor));
333
334    float maxCharWidth = 0.f;
335    const AtomicString& family = style()->font().firstFamily();
336    // Since Lucida Grande is the default font, we want this to match the width
337    // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
338    // IE for some encodings (in IE, the default font is encoding specific).
339    // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
340    if (family == "Lucida Grande")
341        maxCharWidth = scaleEmToUnits(4027);
342    else if (hasValidAvgCharWidth(family))
343        maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
344
345    // For text inputs, IE adds some extra width.
346    if (maxCharWidth > 0.f)
347        result += maxCharWidth - charWidth;
348
349    if (includesDecoration) {
350        HTMLElement* spinButton = innerSpinButtonElement();
351        if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) {
352            result += spinRenderer->borderAndPaddingLogicalWidth();
353            // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0.
354            // So computedStyle()->logicalWidth() is used instead.
355            result += spinButton->computedStyle()->logicalWidth().value();
356        }
357    }
358
359    return result;
360}
361
362LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
363{
364    return lineHeight + nonContentHeight;
365}
366
367void RenderTextControlSingleLine::updateFromElement()
368{
369    RenderTextControl::updateFromElement();
370}
371
372PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
373{
374    RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
375    textBlockStyle->inheritFrom(startStyle);
376    adjustInnerTextStyle(startStyle, textBlockStyle.get());
377
378    textBlockStyle->setWhiteSpace(PRE);
379    textBlockStyle->setOverflowWrap(NormalOverflowWrap);
380    textBlockStyle->setOverflowX(OHIDDEN);
381    textBlockStyle->setOverflowY(OHIDDEN);
382    textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
383
384    if (m_desiredInnerTextLogicalHeight >= 0)
385        textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed));
386    // Do not allow line-height to be smaller than our default.
387    if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
388        textBlockStyle->setLineHeight(RenderStyle::initialLineHeight());
389
390    textBlockStyle->setDisplay(BLOCK);
391
392    return textBlockStyle.release();
393}
394
395PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
396{
397    RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
398    innerBlockStyle->inheritFrom(startStyle);
399
400    innerBlockStyle->setFlexGrow(1);
401    // min-width: 0; is needed for correct shrinking.
402    // FIXME: Remove this line when https://bugs.webkit.org/show_bug.cgi?id=111790 is fixed.
403    innerBlockStyle->setMinWidth(Length(0, Fixed));
404    innerBlockStyle->setDisplay(BLOCK);
405    innerBlockStyle->setDirection(LTR);
406
407    // 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.
408    innerBlockStyle->setUserModify(READ_ONLY);
409
410    return innerBlockStyle.release();
411}
412
413bool RenderTextControlSingleLine::textShouldBeTruncated() const
414{
415    return document()->focusedElement() != node()
416        && style()->textOverflow() == TextOverflowEllipsis;
417}
418
419void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
420{
421    RenderBox* renderer = innerTextElement()->renderBox();
422    if (!renderer)
423        return;
424    RenderLayer* layer = renderer->layer();
425    if (layer)
426        layer->autoscroll(position);
427}
428
429int RenderTextControlSingleLine::scrollWidth() const
430{
431    if (innerTextElement())
432        return innerTextElement()->scrollWidth();
433    return RenderBlock::scrollWidth();
434}
435
436int RenderTextControlSingleLine::scrollHeight() const
437{
438    if (innerTextElement())
439        return innerTextElement()->scrollHeight();
440    return RenderBlock::scrollHeight();
441}
442
443int RenderTextControlSingleLine::scrollLeft() const
444{
445    if (innerTextElement())
446        return innerTextElement()->scrollLeft();
447    return RenderBlock::scrollLeft();
448}
449
450int RenderTextControlSingleLine::scrollTop() const
451{
452    if (innerTextElement())
453        return innerTextElement()->scrollTop();
454    return RenderBlock::scrollTop();
455}
456
457void RenderTextControlSingleLine::setScrollLeft(int newLeft)
458{
459    if (innerTextElement())
460        innerTextElement()->setScrollLeft(newLeft);
461}
462
463void RenderTextControlSingleLine::setScrollTop(int newTop)
464{
465    if (innerTextElement())
466        innerTextElement()->setScrollTop(newTop);
467}
468
469bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
470{
471    RenderBox* renderer = innerTextElement()->renderBox();
472    if (!renderer)
473        return false;
474    RenderLayer* layer = renderer->layer();
475    if (layer && layer->scroll(direction, granularity, multiplier))
476        return true;
477    return RenderBlock::scroll(direction, granularity, multiplier, stopNode);
478}
479
480bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
481{
482    RenderLayer* layer = innerTextElement()->renderBox()->layer();
483    if (layer && layer->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier))
484        return true;
485    return RenderBlock::logicalScroll(direction, granularity, multiplier, stopNode);
486}
487
488HTMLInputElement* RenderTextControlSingleLine::inputElement() const
489{
490    return node()->toInputElement();
491}
492
493}
494