10779a9c932b3af3a72164632b8677072f4bd36a
[WebKit-https.git] / Source / WebCore / rendering / RenderTextControl.cpp
1 /**
2  * Copyright (C) 2006, 2007 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 "RenderTheme.h"
29 #include "ScrollbarTheme.h"
30 #include "StyleInheritedData.h"
31 #include "TextIterator.h"
32 #include "VisiblePosition.h"
33 #include <wtf/unicode/CharacterNames.h>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 RenderTextControl::RenderTextControl(Element* element)
40     : RenderBlock(element)
41     , m_intrinsicLogicalHeight(0)
42 {
43     ASSERT(isHTMLTextFormControlElement(element));
44 }
45
46 RenderTextControl::~RenderTextControl()
47 {
48 }
49
50 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
51 {
52     return toHTMLTextFormControlElement(node());
53 }
54
55 HTMLElement* RenderTextControl::innerTextElement() const
56 {
57     return textFormControlElement()->innerTextElement();
58 }
59
60 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
61 {
62     RenderBlock::styleDidChange(diff, oldStyle);
63     Element* innerText = innerTextElement();
64     if (!innerText)
65         return;
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();
74     }
75     textFormControlElement()->updatePlaceholderVisibility(false);
76 }
77
78 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
79 {
80     bool isDisabled = false;
81     bool isReadOnlyControl = false;
82
83     if (node->isElementNode()) {
84         Element* element = toElement(node);
85         isDisabled = element->isDisabledFormControl();
86         isReadOnlyControl = element->isTextFormControl() && toHTMLTextFormControlElement(element)->isReadOnly();
87     }
88
89     style->setUserModify((isReadOnlyControl || isDisabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
90     return isDisabled;
91 }
92
93 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
94 {
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());
99
100     bool disabled = updateUserModifyProperty(node(), textBlockStyle);
101     if (disabled)
102         textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
103 }
104
105 int RenderTextControl::textBlockLogicalHeight() const
106 {
107     return logicalHeight() - borderAndPaddingLogicalHeight();
108 }
109
110 int RenderTextControl::textBlockLogicalWidth() const
111 {
112     Element* innerText = innerTextElement();
113     ASSERT(innerText);
114
115     LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
116     if (innerText->renderer())
117         unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
118
119     return unitWidth;
120 }
121
122 void RenderTextControl::updateFromElement()
123 {
124     Element* innerText = innerTextElement();
125     if (innerText && innerText->renderer())
126         updateUserModifyProperty(node(), innerText->renderer()->style());
127 }
128
129 int RenderTextControl::scrollbarThickness() const
130 {
131     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
132     return ScrollbarTheme::theme()->scrollbarThickness();
133 }
134
135 void RenderTextControl::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
136 {
137     HTMLElement* innerText = innerTextElement();
138     ASSERT(innerText);
139     if (RenderBox* innerTextBox = innerText->renderBox()) {
140         LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
141         m_intrinsicLogicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingLogicalHeight();
142
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();
147     }
148     RenderBox::computeLogicalHeight(m_intrinsicLogicalHeight, logicalTop, computedValues);
149 }
150
151 void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
152 {
153     HTMLElement* innerText = innerTextElement();
154     if (!innerText->renderer())
155         return;
156
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);
164 }
165
166 static const char* fontFamiliesWithInvalidCharWidth[] = {
167     "American Typewriter",
168     "Arial Hebrew",
169     "Chalkboard",
170     "Cochin",
171     "Corsiva Hebrew",
172     "Courier",
173     "Euphemia UCAS",
174     "Geneva",
175     "Gill Sans",
176     "Hei",
177     "Helvetica",
178     "Hoefler Text",
179     "InaiMathi",
180     "Kai",
181     "Lucida Grande",
182     "Marker Felt",
183     "Monaco",
184     "Mshtakan",
185     "New Peninim MT",
186     "Osaka",
187     "Raanana",
188     "STHeiti",
189     "Symbol",
190     "Times",
191     "Apple Braille",
192     "Apple LiGothic",
193     "Apple LiSung",
194     "Apple Symbols",
195     "AppleGothic",
196     "AppleMyungjo",
197     "#GungSeo",
198     "#HeadLineA",
199     "#PCMyungjo",
200     "#PilGi",
201 };
202
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
207 // all platforms.
208 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
209 {
210     if (family.isEmpty())
211         return false;
212
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('.'))
217         return false;
218
219     static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
220     
221     if (!fontFamiliesWithInvalidCharWidthMap) {
222         fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
223
224         for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
225             fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
226     }
227
228     return !fontFamiliesWithInvalidCharWidthMap->contains(family);
229 }
230
231 float RenderTextControl::getAvgCharWidth(AtomicString family)
232 {
233     if (hasValidAvgCharWidth(family))
234         return roundf(style()->font().primaryFont()->avgCharWidth());
235
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);
242 }
243
244 float RenderTextControl::scaleEmToUnits(int x) const
245 {
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);
249 }
250
251 void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
252 {
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;
260 }
261
262 void RenderTextControl::computePreferredLogicalWidths()
263 {
264     ASSERT(preferredLogicalWidthsDirty());
265
266     m_minPreferredLogicalWidth = 0;
267     m_maxPreferredLogicalWidth = 0;
268
269     if (style()->logicalWidth().isFixed() && style()->logicalWidth().value() >= 0)
270         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->logicalWidth().value());
271     else
272         computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
273
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()));
277     }
278
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()));
282     }
283
284     LayoutUnit toAdd = borderAndPaddingLogicalWidth();
285
286     m_minPreferredLogicalWidth += toAdd;
287     m_maxPreferredLogicalWidth += toAdd;
288
289     setPreferredLogicalWidthsDirty(false);
290 }
291
292 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
293 {
294     if (!size().isEmpty())
295         rects.append(pixelSnappedIntRect(additionalOffset, size()));
296 }
297
298 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
299 {
300     HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement();
301     RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
302     if (!placeholderRenderer)
303         return 0;
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);
309     }
310     return placeholderRenderer;
311 }
312
313 bool RenderTextControl::canBeReplacedWithInlineRunIn() const
314 {
315     return false;
316 }
317
318 } // namespace WebCore