2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
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.
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.
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.
23 #include "core/layout/LayoutTextControl.h"
25 #include "core/html/HTMLTextFormControlElement.h"
26 #include "core/layout/HitTestResult.h"
27 #include "core/layout/LayoutTheme.h"
28 #include "core/layout/TextRunConstructor.h"
29 #include "platform/scroll/ScrollbarTheme.h"
30 #include "wtf/text/CharacterNames.h"
34 LayoutTextControl::LayoutTextControl(HTMLTextFormControlElement
* element
)
35 : LayoutBlockFlow(element
)
40 LayoutTextControl::~LayoutTextControl()
44 HTMLTextFormControlElement
* LayoutTextControl::textFormControlElement() const
46 return toHTMLTextFormControlElement(node());
49 HTMLElement
* LayoutTextControl::innerEditorElement() const
51 return textFormControlElement()->innerEditorElement();
54 void LayoutTextControl::addChild(LayoutObject
* newChild
, LayoutObject
* beforeChild
)
56 // FIXME: This is a terrible hack to get the caret over the placeholder text since it'll
57 // make us paint the placeholder first. (See https://trac.webkit.org/changeset/118733)
58 Node
* node
= newChild
->node();
59 if (node
&& node
->isElementNode() && toElement(node
)->shadowPseudoId() == "-webkit-input-placeholder")
60 LayoutBlockFlow::addChild(newChild
, firstChild());
62 LayoutBlockFlow::addChild(newChild
, beforeChild
);
65 void LayoutTextControl::styleDidChange(StyleDifference diff
, const ComputedStyle
* oldStyle
)
67 LayoutBlockFlow::styleDidChange(diff
, oldStyle
);
68 Element
* innerEditor
= innerEditorElement();
71 LayoutBlock
* innerEditorLayoutObject
= toLayoutBlock(innerEditor
->layoutObject());
72 if (innerEditorLayoutObject
) {
73 // We may have set the width and the height in the old style in layout().
74 // Reset them now to avoid getting a spurious layout hint.
75 innerEditorLayoutObject
->mutableStyleRef().setHeight(Length());
76 innerEditorLayoutObject
->mutableStyleRef().setWidth(Length());
77 innerEditorLayoutObject
->setStyle(createInnerEditorStyle(styleRef()));
78 innerEditor
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::Control
));
80 textFormControlElement()->updatePlaceholderVisibility();
83 static inline void updateUserModifyProperty(HTMLTextFormControlElement
& node
, ComputedStyle
& style
)
85 style
.setUserModify(node
.isDisabledOrReadOnly() ? READ_ONLY
: READ_WRITE_PLAINTEXT_ONLY
);
88 void LayoutTextControl::adjustInnerEditorStyle(ComputedStyle
& textBlockStyle
) const
90 // The inner block, if present, always has its direction set to LTR,
91 // so we need to inherit the direction and unicode-bidi style from the element.
92 textBlockStyle
.setDirection(style()->direction());
93 textBlockStyle
.setUnicodeBidi(style()->unicodeBidi());
95 updateUserModifyProperty(*textFormControlElement(), textBlockStyle
);
98 int LayoutTextControl::textBlockLogicalHeight() const
100 return logicalHeight() - borderAndPaddingLogicalHeight();
103 int LayoutTextControl::textBlockLogicalWidth() const
105 Element
* innerEditor
= innerEditorElement();
108 LayoutUnit unitWidth
= logicalWidth() - borderAndPaddingLogicalWidth();
109 if (innerEditor
->layoutObject())
110 unitWidth
-= innerEditor
->layoutBox()->paddingStart() + innerEditor
->layoutBox()->paddingEnd();
115 void LayoutTextControl::updateFromElement()
117 Element
* innerEditor
= innerEditorElement();
118 if (innerEditor
&& innerEditor
->layoutObject())
119 updateUserModifyProperty(*textFormControlElement(), innerEditor
->layoutObject()->mutableStyleRef());
122 int LayoutTextControl::scrollbarThickness() const
124 // FIXME: We should get the size of the scrollbar from the LayoutTheme instead.
125 return ScrollbarTheme::theme()->scrollbarThickness();
128 void LayoutTextControl::computeLogicalHeight(LayoutUnit logicalHeight
, LayoutUnit logicalTop
, LogicalExtentComputedValues
& computedValues
) const
130 HTMLElement
* innerEditor
= innerEditorElement();
132 if (LayoutBox
* innerEditorBox
= innerEditor
->layoutBox()) {
133 LayoutUnit nonContentHeight
= innerEditorBox
->borderAndPaddingHeight() + innerEditorBox
->marginHeight();
134 logicalHeight
= computeControlLogicalHeight(innerEditorBox
->lineHeight(true, HorizontalLine
, PositionOfInteriorLineBoxes
), nonContentHeight
);
136 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
137 if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL
|| (style()->overflowX() == OAUTO
&& innerEditor
->layoutObject()->style()->overflowWrap() == NormalOverflowWrap
)))
138 || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL
|| (style()->overflowY() == OAUTO
&& innerEditor
->layoutObject()->style()->overflowWrap() == NormalOverflowWrap
))))
139 logicalHeight
+= scrollbarThickness();
141 // FIXME: The logical height of the inner text box should have been added before calling computeLogicalHeight to
143 setIntrinsicContentLogicalHeight(logicalHeight
);
145 logicalHeight
+= borderAndPaddingHeight();
148 LayoutBox::computeLogicalHeight(logicalHeight
, logicalTop
, computedValues
);
151 void LayoutTextControl::hitInnerEditorElement(HitTestResult
& result
, const LayoutPoint
& pointInContainer
, const LayoutPoint
& accumulatedOffset
)
153 HTMLElement
* innerEditor
= innerEditorElement();
154 if (!innerEditor
->layoutObject())
157 LayoutPoint adjustedLocation
= accumulatedOffset
+ location();
158 LayoutPoint localPoint
= pointInContainer
- toLayoutSize(adjustedLocation
+ innerEditor
->layoutBox()->location());
159 if (hasOverflowClip())
160 localPoint
+= scrolledContentOffset();
161 result
.setNodeAndPosition(innerEditor
, localPoint
);
164 static const char* const fontFamiliesWithInvalidCharWidth
[] = {
165 "American Typewriter",
201 // For font families where any of the fonts don't have a valid entry in the OS/2 table
202 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
203 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
204 // but, in order to get similar rendering across platforms, we do this check for
206 bool LayoutTextControl::hasValidAvgCharWidth(const AtomicString
& family
)
208 static HashSet
<AtomicString
>* fontFamiliesWithInvalidCharWidthMap
= nullptr;
210 if (family
.isEmpty())
213 if (!fontFamiliesWithInvalidCharWidthMap
) {
214 fontFamiliesWithInvalidCharWidthMap
= new HashSet
<AtomicString
>;
216 for (size_t i
= 0; i
< WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth
); ++i
)
217 fontFamiliesWithInvalidCharWidthMap
->add(AtomicString(fontFamiliesWithInvalidCharWidth
[i
]));
220 return !fontFamiliesWithInvalidCharWidthMap
->contains(family
);
223 float LayoutTextControl::getAvgCharWidth(const AtomicString
& family
) const
225 if (hasValidAvgCharWidth(family
))
226 return roundf(style()->font().primaryFont()->avgCharWidth());
228 const UChar ch
= '0';
229 const String str
= String(&ch
, 1);
230 const Font
& font
= style()->font();
231 TextRun textRun
= constructTextRun(font
, str
, styleRef(), TextRun::AllowTrailingExpansion
);
232 return font
.width(textRun
);
235 float LayoutTextControl::scaleEmToUnits(int x
) const
237 // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
238 float unitsPerEm
= 2048.0f
;
239 return roundf(style()->font().fontDescription().computedSize() * x
/ unitsPerEm
);
242 void LayoutTextControl::computeIntrinsicLogicalWidths(LayoutUnit
& minLogicalWidth
, LayoutUnit
& maxLogicalWidth
) const
244 // Use average character width. Matches IE.
245 AtomicString family
= style()->font().fontDescription().family().family();
246 maxLogicalWidth
= preferredContentLogicalWidth(const_cast<LayoutTextControl
*>(this)->getAvgCharWidth(family
));
247 if (LayoutBox
* innerEditorLayoutBox
= innerEditorElement()->layoutBox())
248 maxLogicalWidth
+= innerEditorLayoutBox
->paddingStart() + innerEditorLayoutBox
->paddingEnd();
249 if (!style()->logicalWidth().hasPercent())
250 minLogicalWidth
= maxLogicalWidth
;
253 void LayoutTextControl::computePreferredLogicalWidths()
255 ASSERT(preferredLogicalWidthsDirty());
257 m_minPreferredLogicalWidth
= 0;
258 m_maxPreferredLogicalWidth
= 0;
259 const ComputedStyle
& styleToUse
= styleRef();
261 if (styleToUse
.logicalWidth().isFixed() && styleToUse
.logicalWidth().value() >= 0)
262 m_minPreferredLogicalWidth
= m_maxPreferredLogicalWidth
= adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalWidth().value());
264 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth
, m_maxPreferredLogicalWidth
);
266 if (styleToUse
.logicalMinWidth().isFixed() && styleToUse
.logicalMinWidth().value() > 0) {
267 m_maxPreferredLogicalWidth
= std::max(m_maxPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMinWidth().value()));
268 m_minPreferredLogicalWidth
= std::max(m_minPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMinWidth().value()));
271 if (styleToUse
.logicalMaxWidth().isFixed()) {
272 m_maxPreferredLogicalWidth
= std::min(m_maxPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMaxWidth().value()));
273 m_minPreferredLogicalWidth
= std::min(m_minPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMaxWidth().value()));
276 LayoutUnit toAdd
= borderAndPaddingLogicalWidth();
278 m_minPreferredLogicalWidth
+= toAdd
;
279 m_maxPreferredLogicalWidth
+= toAdd
;
281 clearPreferredLogicalWidthsDirty();
284 void LayoutTextControl::addOutlineRects(Vector
<LayoutRect
>& rects
, const LayoutPoint
& additionalOffset
, IncludeBlockVisualOverflowOrNot
) const
286 rects
.append(LayoutRect(additionalOffset
, size()));
289 LayoutObject
* LayoutTextControl::layoutSpecialExcludedChild(bool relayoutChildren
, SubtreeLayoutScope
& layoutScope
)
291 HTMLElement
* placeholder
= toHTMLTextFormControlElement(node())->placeholderElement();
292 LayoutObject
* placeholderLayoutObject
= placeholder
? placeholder
->layoutObject() : nullptr;
293 if (!placeholderLayoutObject
)
295 if (relayoutChildren
)
296 layoutScope
.setChildNeedsLayout(placeholderLayoutObject
);
297 return placeholderLayoutObject
;