Update To 11.40.268.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 "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"
38
39 namespace blink {
40
41 using namespace HTMLNames;
42
43 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
44     : RenderTextControl(element)
45     , m_shouldDrawCapsLockIndicator(false)
46     , m_desiredInnerEditorLogicalHeight(-1)
47 {
48 }
49
50 RenderTextControlSingleLine::~RenderTextControlSingleLine()
51 {
52 }
53
54 inline Element* RenderTextControlSingleLine::containerElement() const
55 {
56     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
57 }
58
59 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
60 {
61     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
62 }
63
64 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
65 {
66     return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
67 }
68
69 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
70 {
71     RenderTextControl::paint(paintInfo, paintOffset);
72
73     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
74         LayoutRect contentsRect = contentBoxRect();
75
76         // Center in the block progression direction.
77         if (isHorizontalWritingMode())
78             contentsRect.setY((height() - contentsRect.height()) / 2);
79         else
80             contentsRect.setX((width() - contentsRect.width()) / 2);
81
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));
85     }
86 }
87
88 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
89 {
90     return containerElement() ? contentLogicalHeight() : logicalHeight();
91 }
92
93 void RenderTextControlSingleLine::layout()
94 {
95     SubtreeLayoutScope layoutScope(*this);
96
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.
103
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.
107
108     RenderBox* innerEditorRenderer = innerEditorElement()->renderBox();
109     RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
110
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);
118     }
119     if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
120         viewPortRenderer->style()->setLogicalHeight(Length(Auto));
121         layoutScope.setNeedsLayout(viewPortRenderer);
122     }
123
124     RenderBlockFlow::layoutBlock(false);
125
126     Element* container = containerElement();
127     RenderBox* containerRenderer = container ? container->renderBox() : 0;
128
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);
135
136         m_desiredInnerEditorLogicalHeight = desiredLogicalHeight;
137
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);
143         }
144     }
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);
155         } else
156             containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
157     }
158
159     // If we need another layout pass, we have changed one of children's height so we need to relayout them.
160     if (needsLayout())
161         RenderBlockFlow::layoutBlock(true);
162
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)));
167     } else
168         centerContainerIfNeeded(containerRenderer);
169
170     HTMLElement* placeholderElement = inputElement()->placeholderElement();
171     if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
172         LayoutSize innerEditorSize;
173
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);
188
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.
191         if (neededLayout)
192             computeOverflow(clientLogicalBottom());
193     }
194 }
195
196 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
197 {
198     if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
199         return false;
200
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());
213         }
214         hitInnerEditorElement(result, pointInParent, accumulatedOffset);
215     }
216     return true;
217 }
218
219 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
220 {
221     m_desiredInnerEditorLogicalHeight = -1;
222     RenderTextControl::styleDidChange(diff, oldStyle);
223
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());
230     }
231     Element* container = containerElement();
232     if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
233         containerRenderer->style()->setHeight(Length());
234         containerRenderer->style()->setWidth(Length());
235     }
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);
242 }
243
244 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
245 {
246     if (!node())
247         return;
248
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;
255
256     if (LocalFrame* frame = document().frame())
257         shouldDrawCapsLockIndicator = inputElement()->type() == InputTypeNames::password && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
258
259     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
260         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
261         setShouldDoFullPaintInvalidation();
262     }
263 }
264
265 bool RenderTextControlSingleLine::hasControlClip() const
266 {
267     // Apply control clip for text fields with decorations.
268     return !!containerElement();
269 }
270
271 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
272 {
273     ASSERT(hasControlClip());
274     LayoutRect clipRect = contentBoxRect();
275     if (containerElement()->renderBox())
276         clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
277     clipRect.moveBy(additionalOffset);
278     return clipRect;
279 }
280
281 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
282 {
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);
289
290     return RenderTextControl::getAvgCharWidth(family);
291 }
292
293 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
294 {
295     int factor;
296     bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
297     if (factor <= 0)
298         factor = 20;
299
300     LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
301
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());
312
313     // For text inputs, IE adds some extra width.
314     if (maxCharWidth > 0.f)
315         result += maxCharWidth - charWidth;
316
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();
324         }
325     }
326
327     return result;
328 }
329
330 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
331 {
332     return lineHeight + nonContentHeight;
333 }
334
335 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerEditorStyle(const RenderStyle* startStyle) const
336 {
337     RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
338     textBlockStyle->inheritFrom(startStyle);
339     adjustInnerEditorStyle(textBlockStyle.get());
340
341     textBlockStyle->setWhiteSpace(PRE);
342     textBlockStyle->setOverflowWrap(NormalOverflowWrap);
343     textBlockStyle->setOverflowX(OHIDDEN);
344     textBlockStyle->setOverflowY(OHIDDEN);
345     textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
346
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());
352
353     textBlockStyle->setDisplay(BLOCK);
354     textBlockStyle->setUnique();
355
356     if (inputElement()->shouldRevealPassword())
357         textBlockStyle->setTextSecurity(TSNONE);
358
359     return textBlockStyle.release();
360 }
361
362 bool RenderTextControlSingleLine::textShouldBeTruncated() const
363 {
364     return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
365 }
366
367 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
368 {
369     RenderBox* renderer = innerEditorElement()->renderBox();
370     if (!renderer)
371         return;
372
373     renderer->autoscroll(position);
374 }
375
376 LayoutUnit RenderTextControlSingleLine::scrollWidth() const
377 {
378     if (innerEditorElement())
379         return innerEditorElement()->scrollWidth();
380     return RenderBlockFlow::scrollWidth();
381 }
382
383 LayoutUnit RenderTextControlSingleLine::scrollHeight() const
384 {
385     if (innerEditorElement())
386         return innerEditorElement()->scrollHeight();
387     return RenderBlockFlow::scrollHeight();
388 }
389
390 LayoutUnit RenderTextControlSingleLine::scrollLeft() const
391 {
392     if (innerEditorElement())
393         return innerEditorElement()->scrollLeft();
394     return RenderBlockFlow::scrollLeft();
395 }
396
397 LayoutUnit RenderTextControlSingleLine::scrollTop() const
398 {
399     if (innerEditorElement())
400         return innerEditorElement()->scrollTop();
401     return RenderBlockFlow::scrollTop();
402 }
403
404 void RenderTextControlSingleLine::setScrollLeft(LayoutUnit newLeft)
405 {
406     if (innerEditorElement())
407         innerEditorElement()->setScrollLeft(newLeft);
408 }
409
410 void RenderTextControlSingleLine::setScrollTop(LayoutUnit newTop)
411 {
412     if (innerEditorElement())
413         innerEditorElement()->setScrollTop(newTop);
414 }
415
416 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
417 {
418     return toHTMLInputElement(node());
419 }
420
421 }