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