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