• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved.
3  *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  * Copyright (C) 2010 Google Inc. All rights reserved.
5  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "config.h"
25 #include "core/rendering/RenderTextControlSingleLine.h"
26 
27 #include "CSSValueKeywords.h"
28 #include "core/dom/shadow/ShadowRoot.h"
29 #include "core/editing/FrameSelection.h"
30 #include "core/html/shadow/ShadowElementNames.h"
31 #include "core/frame/Frame.h"
32 #include "core/rendering/HitTestResult.h"
33 #include "core/rendering/LayoutRectRecorder.h"
34 #include "core/rendering/RenderLayer.h"
35 #include "core/rendering/RenderTheme.h"
36 #include "platform/PlatformKeyboardEvent.h"
37 #include "platform/fonts/SimpleFontData.h"
38 
39 using namespace std;
40 
41 namespace WebCore {
42 
43 using namespace HTMLNames;
44 
RenderTextControlSingleLine(HTMLInputElement * element)45 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
46     : RenderTextControl(element)
47     , m_shouldDrawCapsLockIndicator(false)
48     , m_desiredInnerTextLogicalHeight(-1)
49 {
50     ASSERT(element->hasTagName(inputTag));
51 }
52 
~RenderTextControlSingleLine()53 RenderTextControlSingleLine::~RenderTextControlSingleLine()
54 {
55 }
56 
containerElement() const57 inline Element* RenderTextControlSingleLine::containerElement() const
58 {
59     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
60 }
61 
editingViewPortElement() const62 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
63 {
64     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
65 }
66 
innerSpinButtonElement() const67 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
68 {
69     return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
70 }
71 
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)72 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
73 {
74     RenderTextControl::paint(paintInfo, paintOffset);
75 
76     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
77         LayoutRect contentsRect = contentBoxRect();
78 
79         // Center in the block progression direction.
80         if (isHorizontalWritingMode())
81             contentsRect.setY((height() - contentsRect.height()) / 2);
82         else
83             contentsRect.setX((width() - contentsRect.width()) / 2);
84 
85         // Convert the rect into the coords used for painting the content
86         contentsRect.moveBy(paintOffset + location());
87         RenderTheme::theme().paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect));
88     }
89 }
90 
computeLogicalHeightLimit() const91 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
92 {
93     return containerElement() ? contentLogicalHeight() : logicalHeight();
94 }
95 
layout()96 void RenderTextControlSingleLine::layout()
97 {
98     LayoutRectRecorder recorder(*this);
99     SubtreeLayoutScope layoutScope(this);
100 
101     // FIXME: We should remove the height-related hacks in layout() and
102     // styleDidChange(). We need them because
103     // - Center the inner elements vertically if the input height is taller than
104     //   the intrinsic height of the inner elements.
105     // - Shrink the inner elment heights if the input height is samller than the
106     //   intrinsic heights of the inner elements.
107 
108     // We don't honor paddings and borders for textfields without decorations
109     // and type=search if the text height is taller than the contentHeight()
110     // because of compability.
111 
112     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
113     RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
114 
115     // To ensure consistency between layouts, we need to reset any conditionally overriden height.
116     if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) {
117         innerTextRenderer->style()->setLogicalHeight(Length(Auto));
118         layoutScope.setNeedsLayout(innerTextRenderer);
119     }
120     if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
121         viewPortRenderer->style()->setLogicalHeight(Length(Auto));
122         layoutScope.setNeedsLayout(viewPortRenderer);
123     }
124 
125     RenderBlockFlow::layoutBlock(false);
126 
127     Element* container = containerElement();
128     RenderBox* containerRenderer = container ? container->renderBox() : 0;
129 
130     // Set the text block height
131     LayoutUnit desiredLogicalHeight = textBlockLogicalHeight();
132     LayoutUnit logicalHeightLimit = computeLogicalHeightLimit();
133     if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) {
134         if (desiredLogicalHeight != innerTextRenderer->logicalHeight())
135             layoutScope.setNeedsLayout(this);
136 
137         m_desiredInnerTextLogicalHeight = desiredLogicalHeight;
138 
139         innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
140         layoutScope.setNeedsLayout(innerTextRenderer);
141         if (viewPortRenderer) {
142             viewPortRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
143             layoutScope.setNeedsLayout(viewPortRenderer);
144         }
145     }
146     // The container might be taller because of decoration elements.
147     if (containerRenderer) {
148         containerRenderer->layoutIfNeeded();
149         LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight();
150         if (containerLogicalHeight > logicalHeightLimit) {
151             containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed));
152             layoutScope.setNeedsLayout(this);
153         } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) {
154             containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed));
155             layoutScope.setNeedsLayout(this);
156         } else
157             containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
158     }
159 
160     // If we need another layout pass, we have changed one of children's height so we need to relayout them.
161     if (needsLayout())
162         RenderBlockFlow::layoutBlock(true);
163 
164     // Center the child block in the block progression direction (vertical centering for horizontal text fields).
165     if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) {
166         LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight();
167         innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2)));
168     } else
169         centerContainerIfNeeded(containerRenderer);
170 
171     // Ignores the paddings for the inner spin button.
172     if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) {
173         RenderBox* parentBox = innerSpinBox->parentBox();
174         if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection())
175             innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore()));
176         else
177             innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore()));
178         innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter());
179     }
180 
181     HTMLElement* placeholderElement = inputElement()->placeholderElement();
182     if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
183         LayoutSize innerTextSize;
184         if (innerTextRenderer)
185             innerTextSize = innerTextRenderer->size();
186         placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed));
187         placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed));
188         bool neededLayout = placeholderBox->needsLayout();
189         bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
190         placeholderBox->layoutIfNeeded();
191         LayoutPoint textOffset;
192         if (innerTextRenderer)
193             textOffset = innerTextRenderer->location();
194         if (editingViewPortElement() && editingViewPortElement()->renderBox())
195             textOffset += toLayoutSize(editingViewPortElement()->renderBox()->location());
196         if (containerRenderer)
197             textOffset += toLayoutSize(containerRenderer->location());
198         placeholderBox->setLocation(textOffset);
199 
200         if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) {
201             // This assumes a shadow tree without floats. If floats are added, the
202             // logic should be shared with RenderBlock::layoutBlockChild.
203             placeholderBox->repaint();
204         }
205         // The placeholder gets layout last, after the parent text control and its other children,
206         // so in order to get the correct overflow from the placeholder we need to recompute it now.
207         if (neededLayout)
208             computeOverflow(clientLogicalBottom());
209     }
210 }
211 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)212 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
213 {
214     if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
215         return false;
216 
217     // Say that we hit the inner text element if
218     //  - we hit a node inside the inner text element,
219     //  - we hit the <input> element (e.g. we're over the border or padding), or
220     //  - we hit regions not in any decoration buttons.
221     Element* container = containerElement();
222     if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) {
223         LayoutPoint pointInParent = locationInContainer.point();
224         if (container && editingViewPortElement()) {
225             if (editingViewPortElement()->renderBox())
226                 pointInParent -= toLayoutSize(editingViewPortElement()->renderBox()->location());
227             if (container->renderBox())
228                 pointInParent -= toLayoutSize(container->renderBox()->location());
229         }
230         hitInnerTextElement(result, pointInParent, accumulatedOffset);
231     }
232     return true;
233 }
234 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)235 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
236 {
237     m_desiredInnerTextLogicalHeight = -1;
238     RenderTextControl::styleDidChange(diff, oldStyle);
239 
240     // We may have set the width and the height in the old style in layout().
241     // Reset them now to avoid getting a spurious layout hint.
242     Element* viewPort = editingViewPortElement();
243     if (RenderObject* viewPortRenderer = viewPort ? viewPort->renderer() : 0) {
244         viewPortRenderer->style()->setHeight(Length());
245         viewPortRenderer->style()->setWidth(Length());
246     }
247     Element* container = containerElement();
248     if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
249         containerRenderer->style()->setHeight(Length());
250         containerRenderer->style()->setWidth(Length());
251     }
252     RenderObject* innerTextRenderer = innerTextElement()->renderer();
253     if (innerTextRenderer && diff == StyleDifferenceLayout)
254         innerTextRenderer->setNeedsLayout();
255     if (HTMLElement* placeholder = inputElement()->placeholderElement())
256         placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip);
257     setHasOverflowClip(false);
258 }
259 
capsLockStateMayHaveChanged()260 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
261 {
262     if (!node())
263         return;
264 
265     // Only draw the caps lock indicator if these things are true:
266     // 1) The field is a password field
267     // 2) The frame is active
268     // 3) The element is focused
269     // 4) The caps lock is on
270     bool shouldDrawCapsLockIndicator = false;
271 
272     if (Frame* frame = document().frame())
273         shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
274 
275     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
276         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
277         repaint();
278     }
279 }
280 
hasControlClip() const281 bool RenderTextControlSingleLine::hasControlClip() const
282 {
283     // Apply control clip for text fields with decorations.
284     return !!containerElement();
285 }
286 
controlClipRect(const LayoutPoint & additionalOffset) const287 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
288 {
289     ASSERT(hasControlClip());
290     LayoutRect clipRect = contentBoxRect();
291     if (containerElement()->renderBox())
292         clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
293     clipRect.moveBy(additionalOffset);
294     return clipRect;
295 }
296 
getAvgCharWidth(AtomicString family)297 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
298 {
299     // Since Lucida Grande is the default font, we want this to match the width
300     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
301     // IE for some encodings (in IE, the default font is encoding specific).
302     // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
303     if (family == "Lucida Grande")
304         return scaleEmToUnits(901);
305 
306     return RenderTextControl::getAvgCharWidth(family);
307 }
308 
preferredContentLogicalWidth(float charWidth) const309 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
310 {
311     int factor;
312     bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
313     if (factor <= 0)
314         factor = 20;
315 
316     LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
317 
318     float maxCharWidth = 0.f;
319     AtomicString family = style()->font().family().family();
320     // Since Lucida Grande is the default font, we want this to match the width
321     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
322     // IE for some encodings (in IE, the default font is encoding specific).
323     // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
324     if (family == "Lucida Grande")
325         maxCharWidth = scaleEmToUnits(4027);
326     else if (hasValidAvgCharWidth(family))
327         maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
328 
329     // For text inputs, IE adds some extra width.
330     if (maxCharWidth > 0.f)
331         result += maxCharWidth - charWidth;
332 
333     if (includesDecoration) {
334         HTMLElement* spinButton = innerSpinButtonElement();
335         if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) {
336             result += spinRenderer->borderAndPaddingLogicalWidth();
337             // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0.
338             // So computedStyle()->logicalWidth() is used instead.
339             result += spinButton->computedStyle()->logicalWidth().value();
340         }
341     }
342 
343     return result;
344 }
345 
computeControlLogicalHeight(LayoutUnit lineHeight,LayoutUnit nonContentHeight) const346 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
347 {
348     return lineHeight + nonContentHeight;
349 }
350 
updateFromElement()351 void RenderTextControlSingleLine::updateFromElement()
352 {
353     RenderTextControl::updateFromElement();
354 }
355 
createInnerTextStyle(const RenderStyle * startStyle) const356 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
357 {
358     RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
359     textBlockStyle->inheritFrom(startStyle);
360     adjustInnerTextStyle(textBlockStyle.get());
361 
362     textBlockStyle->setWhiteSpace(PRE);
363     textBlockStyle->setOverflowWrap(NormalOverflowWrap);
364     textBlockStyle->setOverflowX(OHIDDEN);
365     textBlockStyle->setOverflowY(OHIDDEN);
366     textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
367 
368     if (m_desiredInnerTextLogicalHeight >= 0)
369         textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed));
370     // Do not allow line-height to be smaller than our default.
371     if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
372         textBlockStyle->setLineHeight(RenderStyle::initialLineHeight());
373 
374     textBlockStyle->setDisplay(BLOCK);
375     textBlockStyle->setUnique();
376 
377     return textBlockStyle.release();
378 }
379 
textShouldBeTruncated() const380 bool RenderTextControlSingleLine::textShouldBeTruncated() const
381 {
382     return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
383 }
384 
autoscroll(const IntPoint & position)385 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
386 {
387     RenderBox* renderer = innerTextElement()->renderBox();
388     if (!renderer)
389         return;
390 
391     renderer->autoscroll(position);
392 }
393 
scrollWidth() const394 int RenderTextControlSingleLine::scrollWidth() const
395 {
396     if (innerTextElement())
397         return innerTextElement()->scrollWidth();
398     return RenderBlock::scrollWidth();
399 }
400 
scrollHeight() const401 int RenderTextControlSingleLine::scrollHeight() const
402 {
403     if (innerTextElement())
404         return innerTextElement()->scrollHeight();
405     return RenderBlock::scrollHeight();
406 }
407 
scrollLeft() const408 int RenderTextControlSingleLine::scrollLeft() const
409 {
410     if (innerTextElement())
411         return innerTextElement()->scrollLeft();
412     return RenderBlock::scrollLeft();
413 }
414 
scrollTop() const415 int RenderTextControlSingleLine::scrollTop() const
416 {
417     if (innerTextElement())
418         return innerTextElement()->scrollTop();
419     return RenderBlock::scrollTop();
420 }
421 
setScrollLeft(int newLeft)422 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
423 {
424     if (innerTextElement())
425         innerTextElement()->setScrollLeft(newLeft);
426 }
427 
setScrollTop(int newTop)428 void RenderTextControlSingleLine::setScrollTop(int newTop)
429 {
430     if (innerTextElement())
431         innerTextElement()->setScrollTop(newTop);
432 }
433 
inputElement() const434 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
435 {
436     return toHTMLInputElement(node());
437 }
438 
439 }
440