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