Use "= default" to denote default constructor or destructor
[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 "Font.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 "RenderLayer.h"
37 #include "RenderScrollbar.h"
38 #include "RenderTheme.h"
39 #include "RenderView.h"
40 #include "StyleResolver.h"
41 #include "TextControlInnerElements.h"
42 #include <wtf/StackStats.h>
43
44 #if PLATFORM(IOS)
45 #include "RenderThemeIOS.h"
46 #endif
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement& element, RenderStyle&& style)
53     : RenderTextControl(element, WTFMove(style))
54 {
55 }
56
57 RenderTextControlSingleLine::~RenderTextControlSingleLine() = default;
58
59 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
60 {
61     return inputElement().innerSpinButtonElement();
62 }
63
64 void RenderTextControlSingleLine::centerRenderer(RenderBox& renderer) const
65 {
66     LayoutUnit logicalHeightDiff = renderer.logicalHeight() - contentLogicalHeight();
67     renderer.setLogicalTop(renderer.logicalTop() - logicalHeightDiff / 2);
68 }
69
70 static void setNeedsLayoutOnAncestors(RenderObject* start, RenderObject* ancestor)
71 {
72     ASSERT(start != ancestor);
73     for (RenderObject* renderer = start; renderer != ancestor; renderer = renderer->parent()) {
74         ASSERT(renderer);
75         renderer->setNeedsLayout(MarkOnlyThis);
76     }
77 }
78
79 void RenderTextControlSingleLine::layout()
80 {
81     StackStats::LayoutCheckPoint layoutCheckPoint;
82
83     // FIXME: We should remove the height-related hacks in layout() and
84     // styleDidChange(). We need them because
85     // - Center the inner elements vertically if the input height is taller than
86     //   the intrinsic height of the inner elements.
87     // - Shrink the inner elment heights if the input height is samller than the
88     //   intrinsic heights of the inner elements.
89
90     // We don't honor paddings and borders for textfields without decorations
91     // and type=search if the text height is taller than the contentHeight()
92     // because of compability.
93
94     RenderTextControlInnerBlock* innerTextRenderer = innerTextElement()->renderer();
95     RenderBox* innerBlockRenderer = innerBlockElement() ? innerBlockElement()->renderBox() : 0;
96
97     // To ensure consistency between layouts, we need to reset any conditionally overriden height.
98     if (innerTextRenderer && !innerTextRenderer->style().logicalHeight().isAuto()) {
99         innerTextRenderer->mutableStyle().setLogicalHeight(Length(Auto));
100         setNeedsLayoutOnAncestors(innerTextRenderer, this);
101     }
102     if (innerBlockRenderer && !innerBlockRenderer->style().logicalHeight().isAuto()) {
103         innerBlockRenderer->mutableStyle().setLogicalHeight(Length(Auto));
104         setNeedsLayoutOnAncestors(innerBlockRenderer, this);
105     }
106
107     RenderBlockFlow::layoutBlock(false);
108
109     HTMLElement* container = containerElement();
110     RenderBox* containerRenderer = container ? container->renderBox() : 0;
111
112     // Set the text block height
113     LayoutUnit desiredLogicalHeight = textBlockLogicalHeight();
114     LayoutUnit logicalHeightLimit = logicalHeight();
115     if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) {
116         if (desiredLogicalHeight != innerTextRenderer->logicalHeight())
117             setNeedsLayout(MarkOnlyThis);
118
119         innerTextRenderer->mutableStyle().setLogicalHeight(Length(desiredLogicalHeight, Fixed));
120         innerTextRenderer->setNeedsLayout(MarkOnlyThis);
121         if (innerBlockRenderer) {
122             innerBlockRenderer->mutableStyle().setLogicalHeight(Length(desiredLogicalHeight, Fixed));
123             innerBlockRenderer->setNeedsLayout(MarkOnlyThis);
124         }
125     }
126     // The container might be taller because of decoration elements.
127     if (containerRenderer) {
128         containerRenderer->layoutIfNeeded();
129         LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight();
130         if (containerLogicalHeight > logicalHeightLimit) {
131             containerRenderer->mutableStyle().setLogicalHeight(Length(logicalHeightLimit, Fixed));
132             setNeedsLayout(MarkOnlyThis);
133         } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) {
134             containerRenderer->mutableStyle().setLogicalHeight(Length(contentLogicalHeight(), Fixed));
135             setNeedsLayout(MarkOnlyThis);
136         } else
137             containerRenderer->mutableStyle().setLogicalHeight(Length(containerLogicalHeight, Fixed));
138     }
139
140     // If we need another layout pass, we have changed one of children's height so we need to relayout them.
141     if (needsLayout())
142         RenderBlockFlow::layoutBlock(true);
143
144     // Center the child block in the block progression direction (vertical centering for horizontal text fields).
145     if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight())
146         centerRenderer(*innerTextRenderer);
147     else if (container && containerRenderer && containerRenderer->height() != contentLogicalHeight())
148         centerRenderer(*containerRenderer);
149
150     HTMLElement* placeholderElement = inputElement().placeholderElement();
151     if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
152         LayoutSize innerTextSize;
153         if (innerTextRenderer)
154             innerTextSize = innerTextRenderer->size();
155         placeholderBox->mutableStyle().setWidth(Length(innerTextSize.width() - placeholderBox->horizontalBorderAndPaddingExtent(), Fixed));
156         placeholderBox->mutableStyle().setHeight(Length(innerTextSize.height() - placeholderBox->verticalBorderAndPaddingExtent(), Fixed));
157         bool neededLayout = placeholderBox->needsLayout();
158         bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
159         placeholderBox->layoutIfNeeded();
160         LayoutPoint textOffset;
161         if (innerTextRenderer)
162             textOffset = innerTextRenderer->location();
163         if (innerBlockElement() && innerBlockElement()->renderBox())
164             textOffset += toLayoutSize(innerBlockElement()->renderBox()->location());
165         if (containerRenderer)
166             textOffset += toLayoutSize(containerRenderer->location());
167         placeholderBox->setLocation(textOffset);
168
169         if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) {
170             // This assumes a shadow tree without floats. If floats are added, the
171             // logic should be shared with RenderBlock::layoutBlockChild.
172             placeholderBox->repaint();
173         }
174         // The placeholder gets layout last, after the parent text control and its other children,
175         // so in order to get the correct overflow from the placeholder we need to recompute it now.
176         if (neededLayout)
177             computeOverflow(clientLogicalBottom());
178     }
179
180 #if PLATFORM(IOS)
181     // FIXME: We should not be adjusting styles during layout. <rdar://problem/7675493>
182     if (inputElement().isSearchField())
183         RenderThemeIOS::adjustRoundBorderRadius(mutableStyle(), *this);
184 #endif
185 }
186
187 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
188 {
189     if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
190         return false;
191
192     // Say that we hit the inner text element if
193     //  - we hit a node inside the inner text element,
194     //  - we hit the <input> element (e.g. we're over the border or padding), or
195     //  - we hit regions not in any decoration buttons.
196     HTMLElement* container = containerElement();
197     if (result.innerNode()->isDescendantOf(innerTextElement().get()) || result.innerNode() == &inputElement() || (container && container == result.innerNode())) {
198         LayoutPoint pointInParent = locationInContainer.point();
199         if (container && innerBlockElement()) {
200             if (innerBlockElement()->renderBox())
201                 pointInParent -= toLayoutSize(innerBlockElement()->renderBox()->location());
202             if (container->renderBox())
203                 pointInParent -= toLayoutSize(container->renderBox()->location());
204         }
205         hitInnerTextElement(result, pointInParent, accumulatedOffset);
206     }
207     return true;
208 }
209
210 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
211 {
212     RenderTextControl::styleDidChange(diff, oldStyle);
213
214     // We may have set the width and the height in the old style in layout().
215     // Reset them now to avoid getting a spurious layout hint.
216     HTMLElement* innerBlock = innerBlockElement();
217     if (auto* innerBlockRenderer = innerBlock ? innerBlock->renderer() : nullptr) {
218         innerBlockRenderer->mutableStyle().setHeight(Length());
219         innerBlockRenderer->mutableStyle().setWidth(Length());
220     }
221     HTMLElement* container = containerElement();
222     if (auto* containerRenderer = container ? container->renderer() : nullptr) {
223         containerRenderer->mutableStyle().setHeight(Length());
224         containerRenderer->mutableStyle().setWidth(Length());
225     }
226     if (diff == StyleDifferenceLayout) {
227         if (auto innerTextRenderer = innerTextElement()->renderer())
228             innerTextRenderer->setNeedsLayout(MarkContainingBlockChain);
229         if (auto* placeholder = inputElement().placeholderElement()) {
230             if (placeholder->renderer())
231                 placeholder->renderer()->setNeedsLayout(MarkContainingBlockChain);
232         }
233     }
234     setHasOverflowClip(false);
235 }
236
237 bool RenderTextControlSingleLine::hasControlClip() const
238 {
239     // Apply control clip for text fields with decorations.
240     return !!containerElement();
241 }
242
243 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
244 {
245     ASSERT(hasControlClip());
246     LayoutRect clipRect = contentBoxRect();
247     if (containerElement()->renderBox())
248         clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
249     clipRect.moveBy(additionalOffset);
250     return clipRect;
251 }
252
253 float RenderTextControlSingleLine::getAverageCharWidth()
254 {
255 #if !PLATFORM(IOS)
256     // Since Lucida Grande is the default font, we want this to match the width
257     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
258     // IE for some encodings (in IE, the default font is encoding specific).
259     // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
260     if (style().fontCascade().firstFamily() == "Lucida Grande")
261         return scaleEmToUnits(901);
262 #endif
263
264     return RenderTextControl::getAverageCharWidth();
265 }
266
267 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
268 {
269     int factor;
270     bool includesDecoration = inputElement().sizeShouldIncludeDecoration(factor);
271     if (factor <= 0)
272         factor = 20;
273
274     LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
275
276     float maxCharWidth = 0.f;
277
278 #if !PLATFORM(IOS)
279     const AtomicString& family = style().fontCascade().firstFamily();
280     // Since Lucida Grande is the default font, we want this to match the width
281     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
282     // IE for some encodings (in IE, the default font is encoding specific).
283     // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
284     if (family == "Lucida Grande")
285         maxCharWidth = scaleEmToUnits(4027);
286     else if (style().fontCascade().hasValidAverageCharWidth())
287         maxCharWidth = roundf(style().fontCascade().primaryFont().maxCharWidth());
288 #endif
289
290     // For text inputs, IE adds some extra width.
291     if (maxCharWidth > 0.f)
292         result += maxCharWidth - charWidth;
293
294     if (includesDecoration)
295         result += inputElement().decorationWidth();
296
297     return result;
298 }
299
300 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
301 {
302     return lineHeight + nonContentHeight;
303 }
304
305 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
306 {
307     RenderTextControlInnerBlock* renderer = innerTextElement()->renderer();
308     if (!renderer)
309         return;
310     RenderLayer* layer = renderer->layer();
311     if (layer)
312         layer->autoscroll(position);
313 }
314
315 int RenderTextControlSingleLine::scrollWidth() const
316 {
317     if (innerTextElement())
318         return innerTextElement()->scrollWidth();
319     return RenderBlockFlow::scrollWidth();
320 }
321
322 int RenderTextControlSingleLine::scrollHeight() const
323 {
324     if (innerTextElement())
325         return innerTextElement()->scrollHeight();
326     return RenderBlockFlow::scrollHeight();
327 }
328
329 int RenderTextControlSingleLine::scrollLeft() const
330 {
331     if (innerTextElement())
332         return innerTextElement()->scrollLeft();
333     return RenderBlockFlow::scrollLeft();
334 }
335
336 int RenderTextControlSingleLine::scrollTop() const
337 {
338     if (innerTextElement())
339         return innerTextElement()->scrollTop();
340     return RenderBlockFlow::scrollTop();
341 }
342
343 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
344 {
345     if (innerTextElement())
346         innerTextElement()->setScrollLeft(newLeft);
347 }
348
349 void RenderTextControlSingleLine::setScrollTop(int newTop)
350 {
351     if (innerTextElement())
352         innerTextElement()->setScrollTop(newTop);
353 }
354
355 bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Element** stopElement, RenderBox* startBox, const IntPoint& wheelEventAbsolutePoint)
356 {
357     RenderTextControlInnerBlock* renderer = innerTextElement()->renderer();
358     if (!renderer)
359         return false;
360     RenderLayer* layer = renderer->layer();
361     if (layer && layer->scroll(direction, granularity, multiplier))
362         return true;
363     return RenderBlockFlow::scroll(direction, granularity, multiplier, stopElement, startBox, wheelEventAbsolutePoint);
364 }
365
366 bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Element** stopElement)
367 {
368     RenderLayer* layer = innerTextElement()->renderer()->layer();
369     if (layer && layer->scroll(logicalToPhysical(direction, style().isHorizontalWritingMode(), style().isFlippedBlocksWritingMode()), granularity, multiplier))
370         return true;
371     return RenderBlockFlow::logicalScroll(direction, granularity, multiplier, stopElement);
372 }
373
374 HTMLInputElement& RenderTextControlSingleLine::inputElement() const
375 {
376     return downcast<HTMLInputElement>(RenderTextControl::textFormControlElement());
377 }
378
379 }