1/**
2 * Copyright (C) 2006, 2007, 2014 Apple Inc. All rights reserved.
3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23#include "RenderTextControl.h"
24
25#include "HTMLTextFormControlElement.h"
26#include "HitTestResult.h"
27#include "RenderText.h"
28#include "RenderTextControlSingleLine.h"
29#include "RenderTheme.h"
30#include "ScrollbarTheme.h"
31#include "StyleInheritedData.h"
32#include "StyleProperties.h"
33#include "TextControlInnerElements.h"
34#include "VisiblePosition.h"
35#include <wtf/IsoMallocInlines.h>
36#include <wtf/unicode/CharacterNames.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTextControl);
41WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTextControlInnerContainer);
42
43RenderTextControl::RenderTextControl(HTMLTextFormControlElement& element, RenderStyle&& style)
44 : RenderBlockFlow(element, WTFMove(style))
45{
46}
47
48RenderTextControl::~RenderTextControl() = default;
49
50HTMLTextFormControlElement& RenderTextControl::textFormControlElement() const
51{
52 return downcast<HTMLTextFormControlElement>(nodeForNonAnonymous());
53}
54
55RefPtr<TextControlInnerTextElement> RenderTextControl::innerTextElement() const
56{
57 return textFormControlElement().innerTextElement();
58}
59
60void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
61{
62 RenderBlockFlow::styleDidChange(diff, oldStyle);
63 auto innerText = innerTextElement();
64 if (!innerText)
65 return;
66 RenderTextControlInnerBlock* innerTextRenderer = innerText->renderer();
67 if (innerTextRenderer) {
68 // We may have set the width and the height in the old style in layout().
69 // Reset them now to avoid getting a spurious layout hint.
70 innerTextRenderer->mutableStyle().setHeight(Length());
71 innerTextRenderer->mutableStyle().setWidth(Length());
72 innerTextRenderer->setStyle(textFormControlElement().createInnerTextStyle(style()));
73 }
74 textFormControlElement().updatePlaceholderVisibility();
75}
76
77int RenderTextControl::textBlockLogicalHeight() const
78{
79 return logicalHeight() - borderAndPaddingLogicalHeight();
80}
81
82int RenderTextControl::textBlockLogicalWidth() const
83{
84 auto innerText = innerTextElement();
85 ASSERT(innerText);
86
87 LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
88 if (innerText->renderer())
89 unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
90
91 return unitWidth;
92}
93
94int RenderTextControl::scrollbarThickness() const
95{
96 // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
97 return ScrollbarTheme::theme().scrollbarThickness();
98}
99
100RenderBox::LogicalExtentComputedValues RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const
101{
102 auto innerText = innerTextElement();
103 ASSERT(innerText);
104 if (RenderBox* innerTextBox = innerText->renderBox()) {
105 LayoutUnit nonContentHeight = innerTextBox->verticalBorderAndPaddingExtent() + innerTextBox->verticalMarginExtent();
106 logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight);
107
108 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
109 if ((isHorizontalWritingMode() && (style().overflowX() == Overflow::Scroll || (style().overflowX() == Overflow::Auto && innerText->renderer()->style().overflowWrap() == OverflowWrap::Normal)))
110 || (!isHorizontalWritingMode() && (style().overflowY() == Overflow::Scroll || (style().overflowY() == Overflow::Auto && innerText->renderer()->style().overflowWrap() == OverflowWrap::Normal))))
111 logicalHeight += scrollbarThickness();
112
113 // FIXME: The logical height of the inner text box should have been added
114 // before calling computeLogicalHeight to avoid this hack.
115 cacheIntrinsicContentLogicalHeightForFlexItem(logicalHeight);
116
117 logicalHeight += verticalBorderAndPaddingExtent();
118 }
119
120 return RenderBox::computeLogicalHeight(logicalHeight, logicalTop);
121}
122
123void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
124{
125 auto innerText = innerTextElement();
126 if (!innerText->renderer())
127 return;
128
129 LayoutPoint adjustedLocation = accumulatedOffset + location();
130 LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()) + toLayoutSize(scrollPosition());
131 result.setInnerNode(innerText.get());
132 result.setInnerNonSharedNode(innerText.get());
133 result.setLocalPoint(localPoint);
134}
135
136float RenderTextControl::getAverageCharWidth()
137{
138 float width;
139 if (style().fontCascade().fastAverageCharWidthIfAvailable(width))
140 return width;
141
142 const UChar ch = '0';
143 const String str = String(&ch, 1);
144 const FontCascade& font = style().fontCascade();
145 TextRun textRun = constructTextRun(str, style(), AllowTrailingExpansion);
146 return font.width(textRun);
147}
148
149float RenderTextControl::scaleEmToUnits(int x) const
150{
151 // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
152 float unitsPerEm = 2048.0f;
153 return roundf(style().fontCascade().size() * x / unitsPerEm);
154}
155
156void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
157{
158 // Use average character width. Matches IE.
159 maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAverageCharWidth());
160 if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox())
161 maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd();
162 if (!style().logicalWidth().isPercentOrCalculated())
163 minLogicalWidth = maxLogicalWidth;
164}
165
166void RenderTextControl::computePreferredLogicalWidths()
167{
168 ASSERT(preferredLogicalWidthsDirty());
169
170 m_minPreferredLogicalWidth = 0;
171 m_maxPreferredLogicalWidth = 0;
172
173 if (style().logicalWidth().isFixed() && style().logicalWidth().value() >= 0)
174 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style().logicalWidth().value());
175 else
176 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
177
178 if (style().logicalMinWidth().isFixed() && style().logicalMinWidth().value() > 0) {
179 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().logicalMinWidth().value()));
180 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().logicalMinWidth().value()));
181 }
182
183 if (style().logicalMaxWidth().isFixed()) {
184 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().logicalMaxWidth().value()));
185 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().logicalMaxWidth().value()));
186 }
187
188 LayoutUnit toAdd = borderAndPaddingLogicalWidth();
189
190 m_minPreferredLogicalWidth += toAdd;
191 m_maxPreferredLogicalWidth += toAdd;
192
193 setPreferredLogicalWidthsDirty(false);
194}
195
196void RenderTextControl::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
197{
198 if (!size().isEmpty())
199 rects.append(LayoutRect(additionalOffset, size()));
200}
201
202void RenderTextControl::layoutExcludedChildren(bool relayoutChildren)
203{
204 RenderBlockFlow::layoutExcludedChildren(relayoutChildren);
205
206 HTMLElement* placeholder = textFormControlElement().placeholderElement();
207 RenderElement* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
208 if (!placeholderRenderer)
209 return;
210 placeholderRenderer->setIsExcludedFromNormalLayout(true);
211
212 if (relayoutChildren) {
213 // The markParents arguments should be false because this function is
214 // called from layout() of the parent and the placeholder layout doesn't
215 // affect the parent layout.
216 placeholderRenderer->setChildNeedsLayout(MarkOnlyThis);
217 }
218}
219
220#if PLATFORM(IOS_FAMILY)
221bool RenderTextControl::canScroll() const
222{
223 auto innerText = innerTextElement();
224 return innerText && innerText->renderer() && innerText->renderer()->hasOverflowClip();
225}
226
227int RenderTextControl::innerLineHeight() const
228{
229 auto innerText = innerTextElement();
230 if (innerText && innerText->renderer())
231 return innerText->renderer()->style().computedLineHeight();
232 return style().computedLineHeight();
233}
234#endif
235
236} // namespace WebCore
237