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