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/rendering/RenderTextControl.h"
25 #include "core/html/HTMLTextFormControlElement.h"
26 #include "core/rendering/HitTestResult.h"
27 #include "core/rendering/RenderTheme.h"
28 #include "platform/scroll/ScrollbarTheme.h"
29 #include "wtf/unicode/CharacterNames.h"
35 RenderTextControl::RenderTextControl(HTMLTextFormControlElement* element)
36 : RenderBlockFlow(element)
41 RenderTextControl::~RenderTextControl()
45 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
47 return toHTMLTextFormControlElement(node());
50 HTMLElement* RenderTextControl::innerTextElement() const
52 return textFormControlElement()->innerTextElement();
55 void RenderTextControl::addChild(RenderObject* newChild, RenderObject* beforeChild)
57 // FIXME: This is a terrible hack to get the caret over the placeholder text since it'll
58 // make us paint the placeholder first. (See https://trac.webkit.org/changeset/118733)
59 Node* node = newChild->node();
60 if (node && node->isElementNode() && toElement(node)->shadowPseudoId() == "-webkit-input-placeholder")
61 RenderBlockFlow::addChild(newChild, firstChild());
63 RenderBlockFlow::addChild(newChild, beforeChild);
66 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
68 RenderBlockFlow::styleDidChange(diff, oldStyle);
69 Element* innerText = innerTextElement();
72 RenderBlock* innerTextRenderer = toRenderBlock(innerText->renderer());
73 if (innerTextRenderer) {
74 // We may have set the width and the height in the old style in layout().
75 // Reset them now to avoid getting a spurious layout hint.
76 innerTextRenderer->style()->setHeight(Length());
77 innerTextRenderer->style()->setWidth(Length());
78 innerTextRenderer->setStyle(createInnerTextStyle(style()));
79 innerText->setNeedsStyleRecalc(SubtreeStyleChange);
81 textFormControlElement()->updatePlaceholderVisibility(false);
84 static inline void updateUserModifyProperty(HTMLTextFormControlElement* node, RenderStyle* style)
86 style->setUserModify(node->isDisabledOrReadOnly() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
89 void RenderTextControl::adjustInnerTextStyle(RenderStyle* textBlockStyle) const
91 // The inner block, if present, always has its direction set to LTR,
92 // so we need to inherit the direction and unicode-bidi style from the element.
93 textBlockStyle->setDirection(style()->direction());
94 textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
96 updateUserModifyProperty(textFormControlElement(), textBlockStyle);
99 int RenderTextControl::textBlockLogicalHeight() const
101 return logicalHeight() - borderAndPaddingLogicalHeight();
104 int RenderTextControl::textBlockLogicalWidth() const
106 Element* innerText = innerTextElement();
109 LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
110 if (innerText->renderer())
111 unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
116 void RenderTextControl::updateFromElement()
118 Element* innerText = innerTextElement();
119 if (innerText && innerText->renderer())
120 updateUserModifyProperty(textFormControlElement(), innerText->renderer()->style());
123 int RenderTextControl::scrollbarThickness() const
125 // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
126 return ScrollbarTheme::theme()->scrollbarThickness();
129 void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
131 HTMLElement* innerText = innerTextElement();
133 if (RenderBox* innerTextBox = innerText->renderBox()) {
134 LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
135 logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight);
137 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
138 if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)))
139 || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL || (style()->overflowY() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))))
140 logicalHeight += scrollbarThickness();
142 // FIXME: The logical height of the inner text box should have been added before calling computeLogicalHeight to
144 updateIntrinsicContentLogicalHeight(logicalHeight);
146 logicalHeight += borderAndPaddingHeight();
149 RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
152 void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
154 HTMLElement* innerText = innerTextElement();
155 if (!innerText->renderer())
158 LayoutPoint adjustedLocation = accumulatedOffset + location();
159 LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location());
160 if (hasOverflowClip())
161 localPoint += scrolledContentOffset();
162 result.setInnerNode(innerText);
163 result.setInnerNonSharedNode(innerText);
164 result.setLocalPoint(localPoint);
167 static const char* const fontFamiliesWithInvalidCharWidth[] = {
168 "American Typewriter",
204 // For font families where any of the fonts don't have a valid entry in the OS/2 table
205 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
206 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
207 // but, in order to get similar rendering across platforms, we do this check for
209 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
211 static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
213 if (family.isEmpty())
216 if (!fontFamiliesWithInvalidCharWidthMap) {
217 fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
219 for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
220 fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
223 return !fontFamiliesWithInvalidCharWidthMap->contains(family);
226 float RenderTextControl::getAvgCharWidth(AtomicString family)
228 if (hasValidAvgCharWidth(family))
229 return roundf(style()->font().primaryFont()->avgCharWidth());
231 const UChar ch = '0';
232 const String str = String(&ch, 1);
233 const Font& font = style()->font();
234 TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
235 textRun.disableRoundingHacks();
236 return font.width(textRun);
239 float RenderTextControl::scaleEmToUnits(int x) const
241 // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
242 float unitsPerEm = 2048.0f;
243 return roundf(style()->font().fontDescription().computedSize() * x / unitsPerEm);
246 void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
248 // Use average character width. Matches IE.
249 AtomicString family = style()->font().fontDescription().family().family();
250 maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAvgCharWidth(family));
251 if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox())
252 maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd();
253 if (!style()->logicalWidth().isPercent())
254 minLogicalWidth = maxLogicalWidth;
257 void RenderTextControl::computePreferredLogicalWidths()
259 ASSERT(preferredLogicalWidthsDirty());
261 m_minPreferredLogicalWidth = 0;
262 m_maxPreferredLogicalWidth = 0;
263 RenderStyle* styleToUse = style();
265 if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0)
266 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value());
268 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
270 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
271 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
272 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
275 if (styleToUse->logicalMaxWidth().isFixed()) {
276 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
277 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
280 LayoutUnit toAdd = borderAndPaddingLogicalWidth();
282 m_minPreferredLogicalWidth += toAdd;
283 m_maxPreferredLogicalWidth += toAdd;
285 clearPreferredLogicalWidthsDirty();
288 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
290 if (!size().isEmpty())
291 rects.append(pixelSnappedIntRect(additionalOffset, size()));
294 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
296 HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement();
297 RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
298 if (!placeholderRenderer)
300 if (relayoutChildren)
301 layoutScope.setChildNeedsLayout(placeholderRenderer);
302 return placeholderRenderer;
305 } // namespace WebCore