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 "RenderTextControl.h"
25 #include "HTMLTextFormControlElement.h"
26 #include "HitTestResult.h"
27 #include "RenderText.h"
28 #include "RenderTheme.h"
29 #include "ScrollbarTheme.h"
30 #include "StyleInheritedData.h"
31 #include "TextIterator.h"
32 #include "VisiblePosition.h"
33 #include <wtf/unicode/CharacterNames.h>
39 RenderTextControl::RenderTextControl(Element* element)
40 : RenderBlock(element)
41 , m_intrinsicLogicalHeight(0)
43 ASSERT(isHTMLTextFormControlElement(element));
46 RenderTextControl::~RenderTextControl()
50 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
52 return toHTMLTextFormControlElement(node());
55 HTMLElement* RenderTextControl::innerTextElement() const
57 return textFormControlElement()->innerTextElement();
60 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
62 RenderBlock::styleDidChange(diff, oldStyle);
63 Element* innerText = innerTextElement();
66 RenderBlock* innerTextRenderer = toRenderBlock(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->style()->setHeight(Length());
71 innerTextRenderer->style()->setWidth(Length());
72 innerTextRenderer->setStyle(createInnerTextStyle(style()));
73 innerText->setNeedsStyleRecalc();
75 textFormControlElement()->updatePlaceholderVisibility(false);
78 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
80 bool isDisabled = false;
81 bool isReadOnlyControl = false;
83 if (node->isElementNode()) {
84 Element* element = toElement(node);
85 isDisabled = element->isDisabledFormControl();
86 isReadOnlyControl = element->isTextFormControl() && toHTMLTextFormControlElement(element)->isReadOnly();
89 style->setUserModify((isReadOnlyControl || isDisabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
93 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
95 // The inner block, if present, always has its direction set to LTR,
96 // so we need to inherit the direction and unicode-bidi style from the element.
97 textBlockStyle->setDirection(style()->direction());
98 textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
100 bool disabled = updateUserModifyProperty(node(), textBlockStyle);
102 textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
105 int RenderTextControl::textBlockLogicalHeight() const
107 return logicalHeight() - borderAndPaddingLogicalHeight();
110 int RenderTextControl::textBlockLogicalWidth() const
112 Element* innerText = innerTextElement();
115 LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
116 if (innerText->renderer())
117 unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
122 void RenderTextControl::updateFromElement()
124 Element* innerText = innerTextElement();
125 if (innerText && innerText->renderer())
126 updateUserModifyProperty(node(), innerText->renderer()->style());
129 int RenderTextControl::scrollbarThickness() const
131 // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
132 return ScrollbarTheme::theme()->scrollbarThickness();
135 void RenderTextControl::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
137 HTMLElement* innerText = innerTextElement();
139 if (RenderBox* innerTextBox = innerText->renderBox()) {
140 LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
141 m_intrinsicLogicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingLogicalHeight();
143 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
144 if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)))
145 || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL || (style()->overflowY() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))))
146 m_intrinsicLogicalHeight += scrollbarThickness();
148 RenderBox::computeLogicalHeight(m_intrinsicLogicalHeight, logicalTop, computedValues);
151 void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
153 HTMLElement* innerText = innerTextElement();
154 if (!innerText->renderer())
157 LayoutPoint adjustedLocation = accumulatedOffset + location();
158 LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location());
159 if (hasOverflowClip())
160 localPoint += scrolledContentOffset();
161 result.setInnerNode(innerText);
162 result.setInnerNonSharedNode(innerText);
163 result.setLocalPoint(localPoint);
166 static const char* fontFamiliesWithInvalidCharWidth[] = {
167 "American Typewriter",
203 // For font families where any of the fonts don't have a valid entry in the OS/2 table
204 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
205 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
206 // but, in order to get similar rendering across platforms, we do this check for
208 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
210 if (family.isEmpty())
213 // Internal fonts on OS X also have an invalid entry in the table for avgCharWidth.
214 // They are hidden by having a name that begins with a period, so simply search
215 // for that here rather than try to keep the list up to date.
216 if (family.startsWith('.'))
219 static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
221 if (!fontFamiliesWithInvalidCharWidthMap) {
222 fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
224 for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
225 fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
228 return !fontFamiliesWithInvalidCharWidthMap->contains(family);
231 float RenderTextControl::getAvgCharWidth(AtomicString family)
233 if (hasValidAvgCharWidth(family))
234 return roundf(style()->font().primaryFont()->avgCharWidth());
236 const UChar ch = '0';
237 const String str = String(&ch, 1);
238 const Font& font = style()->font();
239 TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
240 textRun.disableRoundingHacks();
241 return font.width(textRun);
244 float RenderTextControl::scaleEmToUnits(int x) const
246 // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
247 float unitsPerEm = 2048.0f;
248 return roundf(style()->font().size() * x / unitsPerEm);
251 void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
253 // Use average character width. Matches IE.
254 const AtomicString& family = style()->font().firstFamily();
255 maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAvgCharWidth(family));
256 if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox())
257 maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd();
258 if (!style()->logicalWidth().isPercent())
259 minLogicalWidth = maxLogicalWidth;
262 void RenderTextControl::computePreferredLogicalWidths()
264 ASSERT(preferredLogicalWidthsDirty());
266 m_minPreferredLogicalWidth = 0;
267 m_maxPreferredLogicalWidth = 0;
269 if (style()->logicalWidth().isFixed() && style()->logicalWidth().value() >= 0)
270 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->logicalWidth().value());
272 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
274 if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) {
275 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value()));
276 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value()));
279 if (style()->logicalMaxWidth().isFixed()) {
280 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value()));
281 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value()));
284 LayoutUnit toAdd = borderAndPaddingLogicalWidth();
286 m_minPreferredLogicalWidth += toAdd;
287 m_maxPreferredLogicalWidth += toAdd;
289 setPreferredLogicalWidthsDirty(false);
292 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
294 if (!size().isEmpty())
295 rects.append(pixelSnappedIntRect(additionalOffset, size()));
298 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
300 HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement();
301 RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
302 if (!placeholderRenderer)
304 if (relayoutChildren) {
305 // The markParents arguments should be false because this function is
306 // called from layout() of the parent and the placeholder layout doesn't
307 // affect the parent layout.
308 placeholderRenderer->setChildNeedsLayout(true, MarkOnlyThis);
310 return placeholderRenderer;
313 bool RenderTextControl::canBeReplacedWithInlineRunIn() const
318 } // namespace WebCore