• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/rendering/RenderTextControl.h"
24 
25 #include "core/html/HTMLTextFormControlElement.h"
26 #include "core/rendering/HitTestResult.h"
27 #include "core/rendering/RenderTheme.h"
28 #include "core/rendering/TextRunConstructor.h"
29 #include "platform/scroll/ScrollbarTheme.h"
30 #include "wtf/unicode/CharacterNames.h"
31 
32 namespace blink {
33 
RenderTextControl(HTMLTextFormControlElement * element)34 RenderTextControl::RenderTextControl(HTMLTextFormControlElement* element)
35     : RenderBlockFlow(element)
36 {
37     ASSERT(element);
38 }
39 
~RenderTextControl()40 RenderTextControl::~RenderTextControl()
41 {
42 }
43 
textFormControlElement() const44 HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
45 {
46     return toHTMLTextFormControlElement(node());
47 }
48 
innerEditorElement() const49 HTMLElement* RenderTextControl::innerEditorElement() const
50 {
51     return textFormControlElement()->innerEditorElement();
52 }
53 
addChild(RenderObject * newChild,RenderObject * beforeChild)54 void RenderTextControl::addChild(RenderObject* newChild, RenderObject* beforeChild)
55 {
56     // FIXME: This is a terrible hack to get the caret over the placeholder text since it'll
57     // make us paint the placeholder first. (See https://trac.webkit.org/changeset/118733)
58     Node* node = newChild->node();
59     if (node && node->isElementNode() && toElement(node)->shadowPseudoId() == "-webkit-input-placeholder")
60         RenderBlockFlow::addChild(newChild, firstChild());
61     else
62         RenderBlockFlow::addChild(newChild, beforeChild);
63 }
64 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)65 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
66 {
67     RenderBlockFlow::styleDidChange(diff, oldStyle);
68     Element* innerEditor = innerEditorElement();
69     if (!innerEditor)
70         return;
71     RenderBlock* innerEditorRenderer = toRenderBlock(innerEditor->renderer());
72     if (innerEditorRenderer) {
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         innerEditorRenderer->style()->setHeight(Length());
76         innerEditorRenderer->style()->setWidth(Length());
77         innerEditorRenderer->setStyle(createInnerEditorStyle(style()));
78         innerEditor->setNeedsStyleRecalc(SubtreeStyleChange);
79     }
80     textFormControlElement()->updatePlaceholderVisibility(false);
81 }
82 
updateUserModifyProperty(HTMLTextFormControlElement * node,RenderStyle * style)83 static inline void updateUserModifyProperty(HTMLTextFormControlElement* node, RenderStyle* style)
84 {
85     style->setUserModify(node->isDisabledOrReadOnly() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
86 }
87 
adjustInnerEditorStyle(RenderStyle * textBlockStyle) const88 void RenderTextControl::adjustInnerEditorStyle(RenderStyle* textBlockStyle) const
89 {
90     // The inner block, if present, always has its direction set to LTR,
91     // so we need to inherit the direction and unicode-bidi style from the element.
92     textBlockStyle->setDirection(style()->direction());
93     textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
94 
95     updateUserModifyProperty(textFormControlElement(), textBlockStyle);
96 }
97 
textBlockLogicalHeight() const98 int RenderTextControl::textBlockLogicalHeight() const
99 {
100     return logicalHeight() - borderAndPaddingLogicalHeight();
101 }
102 
textBlockLogicalWidth() const103 int RenderTextControl::textBlockLogicalWidth() const
104 {
105     Element* innerEditor = innerEditorElement();
106     ASSERT(innerEditor);
107 
108     LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
109     if (innerEditor->renderer())
110         unitWidth -= innerEditor->renderBox()->paddingStart() + innerEditor->renderBox()->paddingEnd();
111 
112     return unitWidth;
113 }
114 
updateFromElement()115 void RenderTextControl::updateFromElement()
116 {
117     Element* innerEditor = innerEditorElement();
118     if (innerEditor && innerEditor->renderer())
119         updateUserModifyProperty(textFormControlElement(), innerEditor->renderer()->style());
120 }
121 
scrollbarThickness() const122 int RenderTextControl::scrollbarThickness() const
123 {
124     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
125     return ScrollbarTheme::theme()->scrollbarThickness();
126 }
127 
computeLogicalHeight(LayoutUnit logicalHeight,LayoutUnit logicalTop,LogicalExtentComputedValues & computedValues) const128 void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
129 {
130     HTMLElement* innerEditor = innerEditorElement();
131     ASSERT(innerEditor);
132     if (RenderBox* innerEditorBox = innerEditor->renderBox()) {
133         LayoutUnit nonContentHeight = innerEditorBox->borderAndPaddingHeight() + innerEditorBox->marginHeight();
134         logicalHeight = computeControlLogicalHeight(innerEditorBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight);
135 
136         // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
137         if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && innerEditor->renderer()->style()->overflowWrap() == NormalOverflowWrap)))
138             || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL ||  (style()->overflowY() == OAUTO && innerEditor->renderer()->style()->overflowWrap() == NormalOverflowWrap))))
139             logicalHeight += scrollbarThickness();
140 
141         // FIXME: The logical height of the inner text box should have been added before calling computeLogicalHeight to
142         // avoid this hack.
143         updateIntrinsicContentLogicalHeight(logicalHeight);
144 
145         logicalHeight += borderAndPaddingHeight();
146     }
147 
148     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
149 }
150 
hitInnerEditorElement(HitTestResult & result,const LayoutPoint & pointInContainer,const LayoutPoint & accumulatedOffset)151 void RenderTextControl::hitInnerEditorElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
152 {
153     HTMLElement* innerEditor = innerEditorElement();
154     if (!innerEditor->renderer())
155         return;
156 
157     LayoutPoint adjustedLocation = accumulatedOffset + location();
158     LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerEditor->renderBox()->location());
159     if (hasOverflowClip())
160         localPoint += scrolledContentOffset();
161     result.setInnerNode(innerEditor);
162     result.setInnerNonSharedNode(innerEditor);
163     result.setLocalPoint(localPoint);
164 }
165 
166 static const char* const 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.
hasValidAvgCharWidth(AtomicString family)208 bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
209 {
210     static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
211 
212     if (family.isEmpty())
213         return false;
214 
215     if (!fontFamiliesWithInvalidCharWidthMap) {
216         fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
217 
218         for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
219             fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
220     }
221 
222     return !fontFamiliesWithInvalidCharWidthMap->contains(family);
223 }
224 
getAvgCharWidth(AtomicString family)225 float RenderTextControl::getAvgCharWidth(AtomicString family)
226 {
227     if (hasValidAvgCharWidth(family))
228         return roundf(style()->font().primaryFont()->avgCharWidth());
229 
230     const UChar ch = '0';
231     const String str = String(&ch, 1);
232     const Font& font = style()->font();
233     TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
234     return font.width(textRun);
235 }
236 
scaleEmToUnits(int x) const237 float RenderTextControl::scaleEmToUnits(int x) const
238 {
239     // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
240     float unitsPerEm = 2048.0f;
241     return roundf(style()->font().fontDescription().computedSize() * x / unitsPerEm);
242 }
243 
computeIntrinsicLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const244 void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
245 {
246     // Use average character width. Matches IE.
247     AtomicString family = style()->font().fontDescription().family().family();
248     maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAvgCharWidth(family));
249     if (RenderBox* innerEditorRenderBox = innerEditorElement()->renderBox())
250         maxLogicalWidth += innerEditorRenderBox->paddingStart() + innerEditorRenderBox->paddingEnd();
251     if (!style()->logicalWidth().isPercent())
252         minLogicalWidth = maxLogicalWidth;
253 }
254 
computePreferredLogicalWidths()255 void RenderTextControl::computePreferredLogicalWidths()
256 {
257     ASSERT(preferredLogicalWidthsDirty());
258 
259     m_minPreferredLogicalWidth = 0;
260     m_maxPreferredLogicalWidth = 0;
261     RenderStyle* styleToUse = style();
262 
263     if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0)
264         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value());
265     else
266         computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
267 
268     if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
269         m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
270         m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
271     }
272 
273     if (styleToUse->logicalMaxWidth().isFixed()) {
274         m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
275         m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
276     }
277 
278     LayoutUnit toAdd = borderAndPaddingLogicalWidth();
279 
280     m_minPreferredLogicalWidth += toAdd;
281     m_maxPreferredLogicalWidth += toAdd;
282 
283     clearPreferredLogicalWidthsDirty();
284 }
285 
addFocusRingRects(Vector<LayoutRect> & rects,const LayoutPoint & additionalOffset,const RenderLayerModelObject *) const286 void RenderTextControl::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) const
287 {
288     if (!size().isEmpty())
289         rects.append(LayoutRect(additionalOffset, size()));
290 }
291 
layoutSpecialExcludedChild(bool relayoutChildren,SubtreeLayoutScope & layoutScope)292 RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
293 {
294     HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement();
295     RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
296     if (!placeholderRenderer)
297         return 0;
298     if (relayoutChildren)
299         layoutScope.setChildNeedsLayout(placeholderRenderer);
300     return placeholderRenderer;
301 }
302 
303 } // namespace blink
304