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