Unreviewed, rolling out r154826.
[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 {
42     ASSERT(isHTMLTextFormControlElement(element));
43 }
44
45 RenderTextControl::~RenderTextControl()
46 {
47 }
48
49 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
50 {
51     return toHTMLTextFormControlElement(node());
52 }
53
54 HTMLElement* RenderTextControl::innerTextElement() const
55 {
56     return textFormControlElement()->innerTextElement();
57 }
58
59 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
60 {
61     RenderBlock::styleDidChange(diff, oldStyle);
62     Element* innerText = innerTextElement();
63     if (!innerText)
64         return;
65     RenderBlock* innerTextRenderer = toRenderBlock(innerText->renderer());
66     if (innerTextRenderer) {
67         // We may have set the width and the height in the old style in layout().
68         // Reset them now to avoid getting a spurious layout hint.
69         innerTextRenderer->style()->setHeight(Length());
70         innerTextRenderer->style()->setWidth(Length());
71         innerTextRenderer->setStyle(createInnerTextStyle(style()));
72         innerText->setNeedsStyleRecalc();
73     }
74     textFormControlElement()->updatePlaceholderVisibility(false);
75 }
76
77 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
78 {
79     bool isDisabled = false;
80     bool isReadOnlyControl = false;
81
82     if (node->isElementNode()) {
83         Element* element = toElement(node);
84         isDisabled = element->isDisabledFormControl();
85         isReadOnlyControl = element->isTextFormControl() && toHTMLTextFormControlElement(element)->isReadOnly();
86     }
87
88     style->setUserModify((isReadOnlyControl || isDisabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
89     return isDisabled;
90 }
91
92 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
93 {
94     // The inner block, if present, always has its direction set to LTR,
95     // so we need to inherit the direction and unicode-bidi style from the element.
96     textBlockStyle->setDirection(style()->direction());
97     textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
98
99     bool disabled = updateUserModifyProperty(node(), textBlockStyle);
100     if (disabled)
101         textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
102 }
103
104 int RenderTextControl::textBlockLogicalHeight() const
105 {
106     return logicalHeight() - borderAndPaddingLogicalHeight();
107 }
108
109 int RenderTextControl::textBlockLogicalWidth() const
110 {
111     Element* innerText = innerTextElement();
112     ASSERT(innerText);
113
114     LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
115     if (innerText->renderer())
116         unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
117
118     return unitWidth;
119 }
120
121 void RenderTextControl::updateFromElement()
122 {
123     Element* innerText = innerTextElement();
124     if (innerText && innerText->renderer())
125         updateUserModifyProperty(node(), innerText->renderer()->style());
126 }
127
128 int RenderTextControl::scrollbarThickness() const
129 {
130     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
131     return ScrollbarTheme::theme()->scrollbarThickness();
132 }
133
134 void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
135 {
136     HTMLElement* innerText = innerTextElement();
137     ASSERT(innerText);
138     if (RenderBox* innerTextBox = innerText->renderBox()) {
139         LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
140         logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight();
141
142         // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
143         if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)))
144             || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL ||  (style()->overflowY() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))))
145             logicalHeight += scrollbarThickness();
146     }
147
148     RenderBox::computeLogicalHeight(logicalHeight, 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