tizen beta release
[profile/ivi/webkit-efl.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 "ScrollbarTheme.h"
29 #include "TextIterator.h"
30 #include "VisiblePosition.h"
31 #include <wtf/unicode/CharacterNames.h>
32
33 using namespace std;
34
35 namespace WebCore {
36
37 // Value chosen by observation.  This can be tweaked.
38 static const int minColorContrastValue = 1300;
39 // For transparent or translucent background color, use lightening.
40 static const int minDisabledColorAlphaValue = 128;
41
42 static Color disabledTextColor(const Color& textColor, const Color& backgroundColor)
43 {
44     // The explicit check for black is an optimization for the 99% case (black on white).
45     // This also means that black on black will turn into grey on black when disabled.
46     Color disabledColor;
47     if (textColor.rgb() == Color::black || backgroundColor.alpha() < minDisabledColorAlphaValue || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
48         disabledColor = textColor.light();
49     else
50         disabledColor = textColor.dark();
51     
52     // If there's not very much contrast between the disabled color and the background color,
53     // just leave the text color alone.  We don't want to change a good contrast color scheme so that it has really bad contrast.
54     // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
55     if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
56         return textColor;
57     
58     return disabledColor;
59 }
60
61 RenderTextControl::RenderTextControl(Node* node)
62     : RenderBlock(node)
63 {
64     ASSERT(toTextFormControl(node));
65 }
66
67 RenderTextControl::~RenderTextControl()
68 {
69 }
70
71 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
72 {
73     return static_cast<HTMLTextFormControlElement*>(node());
74 }
75
76 HTMLElement* RenderTextControl::innerTextElement() const
77 {
78     return textFormControlElement()->innerTextElement();
79 }
80
81 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
82 {
83     RenderBlock::styleDidChange(diff, oldStyle);
84     Element* innerText = innerTextElement();
85     if (!innerText)
86         return;
87     RenderBlock* innerTextRenderer = toRenderBlock(innerText->renderer());
88     if (innerTextRenderer) {
89         // We may have set the width and the height in the old style in layout().
90         // Reset them now to avoid getting a spurious layout hint.
91         innerTextRenderer->style()->setHeight(Length());
92         innerTextRenderer->style()->setWidth(Length());
93         innerTextRenderer->setStyle(createInnerTextStyle(style()));
94         innerText->setNeedsStyleRecalc();
95     }
96     textFormControlElement()->updatePlaceholderVisibility(false);
97 }
98
99 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
100 {
101     bool isEnabled = true;
102     bool isReadOnlyControl = false;
103
104     if (node->isElementNode()) {
105         Element* element = static_cast<Element*>(node);
106         isEnabled = element->isEnabledFormControl();
107         isReadOnlyControl = element->isReadOnlyFormControl();
108     }
109
110     style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
111     return !isEnabled;
112 }
113
114 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
115 {
116     // The inner block, if present, always has its direction set to LTR,
117     // so we need to inherit the direction and unicode-bidi style from the element.
118     textBlockStyle->setDirection(style()->direction());
119     textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
120
121     bool disabled = updateUserModifyProperty(node(), textBlockStyle);
122     if (disabled)
123         textBlockStyle->setColor(disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
124 }
125
126 int RenderTextControl::textBlockHeight() const
127 {
128     return height() - borderAndPaddingHeight();
129 }
130
131 int RenderTextControl::textBlockWidth() const
132 {
133     Element* innerText = innerTextElement();
134     ASSERT(innerText);
135     return width() - borderAndPaddingWidth() - innerText->renderBox()->paddingLeft() - innerText->renderBox()->paddingRight();
136 }
137
138 void RenderTextControl::updateFromElement()
139 {
140     Element* innerText = innerTextElement();
141     if (innerText)
142         updateUserModifyProperty(node(), innerText->renderer()->style());
143 }
144
145 VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
146 {
147     if (index <= 0)
148         return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
149     ExceptionCode ec = 0;
150     RefPtr<Range> range = Range::create(document());
151     range->selectNodeContents(innerTextElement(), ec);
152     ASSERT(!ec);
153     CharacterIterator it(range.get());
154     it.advance(index - 1);
155     return VisiblePosition(it.range()->endPosition(), UPSTREAM);
156 }
157
158 int RenderTextControl::scrollbarThickness() const
159 {
160     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
161     return ScrollbarTheme::theme()->scrollbarThickness();
162 }
163
164 void RenderTextControl::computeLogicalHeight()
165 {
166     HTMLElement* innerText = innerTextElement();
167     ASSERT(innerText);
168     RenderBox* innerTextRenderBox = innerText->renderBox();
169
170     setHeight(innerTextRenderBox->borderTop() + innerTextRenderBox->borderBottom() +
171               innerTextRenderBox->paddingTop() + innerTextRenderBox->paddingBottom() +
172               innerTextRenderBox->marginTop() + innerTextRenderBox->marginBottom());
173
174     adjustControlHeightBasedOnLineHeight(innerText->renderBox()->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes));
175     setHeight(height() + borderAndPaddingHeight());
176
177     // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
178     if (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && innerText->renderer()->style()->wordWrap() == NormalWordWrap))
179         setHeight(height() + scrollbarThickness());
180
181     RenderBlock::computeLogicalHeight();
182 }
183
184 void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
185 {
186     LayoutPoint adjustedLocation = accumulatedOffset + location();
187     HTMLElement* innerText = innerTextElement();
188     result.setInnerNode(innerText);
189     result.setInnerNonSharedNode(innerText);
190     result.setLocalPoint(pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()));
191 }
192
193 static const char* fontFamiliesWithInvalidCharWidth[] = {
194     "American Typewriter",
195     "Arial Hebrew",
196     "Chalkboard",
197     "Cochin",
198     "Corsiva Hebrew",
199     "Courier",
200     "Euphemia UCAS",
201     "Geneva",
202     "Gill Sans",
203     "Hei",
204     "Helvetica",
205     "Hoefler Text",
206     "InaiMathi",
207     "Kai",
208     "Lucida Grande",
209     "Marker Felt",
210     "Monaco",
211     "Mshtakan",
212     "New Peninim MT",
213     "Osaka",
214     "Raanana",
215     "STHeiti",
216     "Symbol",
217     "Times",
218     "Apple Braille",
219     "Apple LiGothic",
220     "Apple LiSung",
221     "Apple Symbols",
222     "AppleGothic",
223     "AppleMyungjo",
224     "#GungSeo",
225     "#HeadLineA",
226     "#PCMyungjo",
227     "#PilGi",
228 };
229
230 // For font families where any of the fonts don't have a valid entry in the OS/2 table
231 // for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
232 // from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
233 // but, in order to get similar rendering across platforms, we do this check for
234 // all platforms.
235 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
236 {
237     static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
238
239     if (!fontFamiliesWithInvalidCharWidthMap) {
240         fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
241
242         for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
243             fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
244     }
245
246     return !fontFamiliesWithInvalidCharWidthMap->contains(family);
247 }
248
249 float RenderTextControl::getAvgCharWidth(AtomicString family)
250 {
251     if (hasValidAvgCharWidth(family))
252         return roundf(style()->font().primaryFont()->avgCharWidth());
253
254     const UChar ch = '0';
255     const String str = String(&ch, 1);
256     const Font& font = style()->font();
257     TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
258     textRun.disableRoundingHacks();
259     return font.width(textRun);
260 }
261
262 float RenderTextControl::scaleEmToUnits(int x) const
263 {
264     // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
265     float unitsPerEm = 2048.0f;
266     return roundf(style()->font().size() * x / unitsPerEm);
267 }
268
269 void RenderTextControl::computePreferredLogicalWidths()
270 {
271     ASSERT(preferredLogicalWidthsDirty());
272
273     m_minPreferredLogicalWidth = 0;
274     m_maxPreferredLogicalWidth = 0;
275
276     if (style()->width().isFixed() && style()->width().value() > 0)
277         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
278     else {
279         // Use average character width. Matches IE.
280         AtomicString family = style()->font().family().family();
281         RenderBox* innerTextRenderBox = innerTextElement()->renderBox();
282         m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + innerTextRenderBox->paddingLeft() + innerTextRenderBox->paddingRight();
283     }
284
285     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
286         m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
287         m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
288     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
289         m_minPreferredLogicalWidth = 0;
290     else
291         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
292
293     if (style()->maxWidth().isFixed()) {
294         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
295         m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
296     }
297
298     LayoutUnit toAdd = borderAndPaddingWidth();
299
300     m_minPreferredLogicalWidth += toAdd;
301     m_maxPreferredLogicalWidth += toAdd;
302
303     setPreferredLogicalWidthsDirty(false);
304 }
305
306 void RenderTextControl::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset)
307 {
308     if (!size().isEmpty())
309         rects.append(LayoutRect(additionalOffset, size()));
310 }
311
312 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
313 {
314     HTMLElement* placeholder = toTextFormControl(node())->placeholderElement();
315     RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
316     if (!placeholderRenderer)
317         return 0;
318     if (relayoutChildren) {
319         // The markParents arguments should be false because this function is
320         // called from layout() of the parent and the placeholder layout doesn't
321         // affect the parent layout.
322         placeholderRenderer->setChildNeedsLayout(true, false);
323     }
324     return placeholderRenderer;
325 }
326
327 } // namespace WebCore