Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderTextControlSingleLine.cpp
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/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"
38
39 using namespace std;
40
41 namespace WebCore {
42
43 using namespace HTMLNames;
44
45 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
46     : RenderTextControl(element)
47     , m_shouldDrawCapsLockIndicator(false)
48     , m_desiredInnerTextLogicalHeight(-1)
49 {
50 }
51
52 RenderTextControlSingleLine::~RenderTextControlSingleLine()
53 {
54 }
55
56 inline Element* RenderTextControlSingleLine::containerElement() const
57 {
58     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
59 }
60
61 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
62 {
63     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
64 }
65
66 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
67 {
68     return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
69 }
70
71 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
72 {
73     RenderTextControl::paint(paintInfo, paintOffset);
74
75     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
76         LayoutRect contentsRect = contentBoxRect();
77
78         // Center in the block progression direction.
79         if (isHorizontalWritingMode())
80             contentsRect.setY((height() - contentsRect.height()) / 2);
81         else
82             contentsRect.setX((width() - contentsRect.width()) / 2);
83
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));
87     }
88 }
89
90 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
91 {
92     return containerElement() ? contentLogicalHeight() : logicalHeight();
93 }
94
95 void RenderTextControlSingleLine::layout()
96 {
97     LayoutRectRecorder recorder(*this);
98     SubtreeLayoutScope layoutScope(this);
99
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.
106
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.
110
111     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
112     RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
113
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);
121     }
122     if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
123         viewPortRenderer->style()->setLogicalHeight(Length(Auto));
124         layoutScope.setNeedsLayout(viewPortRenderer);
125     }
126
127     RenderBlockFlow::layoutBlock(false);
128
129     Element* container = containerElement();
130     RenderBox* containerRenderer = container ? container->renderBox() : 0;
131
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);
138
139         m_desiredInnerTextLogicalHeight = desiredLogicalHeight;
140
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);
146         }
147     }
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);
158         } else
159             containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
160     }
161
162     // If we need another layout pass, we have changed one of children's height so we need to relayout them.
163     if (needsLayout())
164         RenderBlockFlow::layoutBlock(true);
165
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)));
170     } else
171         centerContainerIfNeeded(containerRenderer);
172
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()));
178         else
179             innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore()));
180         innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter());
181     }
182
183     HTMLElement* placeholderElement = inputElement()->placeholderElement();
184     if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
185         LayoutSize innerTextSize;
186         LayoutRectRecorder placeholderBoxRecorder(*placeholderBox);
187
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);
203
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();
208         }
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.
211         if (neededLayout)
212             computeOverflow(clientLogicalBottom());
213     }
214 }
215
216 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
217 {
218     if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
219         return false;
220
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());
233         }
234         hitInnerTextElement(result, pointInParent, accumulatedOffset);
235     }
236     return true;
237 }
238
239 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
240 {
241     m_desiredInnerTextLogicalHeight = -1;
242     RenderTextControl::styleDidChange(diff, oldStyle);
243
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());
250     }
251     Element* container = containerElement();
252     if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
253         containerRenderer->style()->setHeight(Length());
254         containerRenderer->style()->setWidth(Length());
255     }
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);
262 }
263
264 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
265 {
266     if (!node())
267         return;
268
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;
275
276     if (LocalFrame* frame = document().frame())
277         shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
278
279     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
280         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
281         repaint();
282     }
283 }
284
285 bool RenderTextControlSingleLine::hasControlClip() const
286 {
287     // Apply control clip for text fields with decorations.
288     return !!containerElement();
289 }
290
291 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
292 {
293     ASSERT(hasControlClip());
294     LayoutRect clipRect = contentBoxRect();
295     if (containerElement()->renderBox())
296         clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
297     clipRect.moveBy(additionalOffset);
298     return clipRect;
299 }
300
301 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
302 {
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);
309
310     return RenderTextControl::getAvgCharWidth(family);
311 }
312
313 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
314 {
315     int factor;
316     bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
317     if (factor <= 0)
318         factor = 20;
319
320     LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
321
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());
332
333     // For text inputs, IE adds some extra width.
334     if (maxCharWidth > 0.f)
335         result += maxCharWidth - charWidth;
336
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();
344         }
345     }
346
347     return result;
348 }
349
350 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
351 {
352     return lineHeight + nonContentHeight;
353 }
354
355 void RenderTextControlSingleLine::updateFromElement()
356 {
357     RenderTextControl::updateFromElement();
358 }
359
360 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
361 {
362     RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
363     textBlockStyle->inheritFrom(startStyle);
364     adjustInnerTextStyle(textBlockStyle.get());
365
366     textBlockStyle->setWhiteSpace(PRE);
367     textBlockStyle->setOverflowWrap(NormalOverflowWrap);
368     textBlockStyle->setOverflowX(OHIDDEN);
369     textBlockStyle->setOverflowY(OHIDDEN);
370     textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
371
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());
377
378     textBlockStyle->setDisplay(BLOCK);
379     textBlockStyle->setUnique();
380
381     if (inputElement()->shouldRevealPassword())
382         textBlockStyle->setTextSecurity(TSNONE);
383
384     return textBlockStyle.release();
385 }
386
387 bool RenderTextControlSingleLine::textShouldBeTruncated() const
388 {
389     return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
390 }
391
392 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
393 {
394     RenderBox* renderer = innerTextElement()->renderBox();
395     if (!renderer)
396         return;
397
398     renderer->autoscroll(position);
399 }
400
401 int RenderTextControlSingleLine::scrollWidth() const
402 {
403     if (innerTextElement())
404         return innerTextElement()->scrollWidth();
405     return RenderBlockFlow::scrollWidth();
406 }
407
408 int RenderTextControlSingleLine::scrollHeight() const
409 {
410     if (innerTextElement())
411         return innerTextElement()->scrollHeight();
412     return RenderBlockFlow::scrollHeight();
413 }
414
415 int RenderTextControlSingleLine::scrollLeft() const
416 {
417     if (innerTextElement())
418         return innerTextElement()->scrollLeft();
419     return RenderBlockFlow::scrollLeft();
420 }
421
422 int RenderTextControlSingleLine::scrollTop() const
423 {
424     if (innerTextElement())
425         return innerTextElement()->scrollTop();
426     return RenderBlockFlow::scrollTop();
427 }
428
429 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
430 {
431     if (innerTextElement())
432         innerTextElement()->setScrollLeft(newLeft);
433 }
434
435 void RenderTextControlSingleLine::setScrollTop(int newTop)
436 {
437     if (innerTextElement())
438         innerTextElement()->setScrollTop(newTop);
439 }
440
441 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
442 {
443     return toHTMLInputElement(node());
444 }
445
446 }