2
* Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3
* (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
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.
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.
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.
23
#include "RenderTextControl.h"
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>
39
RenderTextControl::RenderTextControl(Node* node)
42
ASSERT(toTextFormControl(node));
45
RenderTextControl::~RenderTextControl()
49
HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
51
return static_cast<HTMLTextFormControlElement*>(node());
54
HTMLElement* RenderTextControl::innerTextElement() const
56
return textFormControlElement()->innerTextElement();
59
void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
61
RenderBlock::styleDidChange(diff, oldStyle);
62
Element* innerText = innerTextElement();
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();
74
textFormControlElement()->updatePlaceholderVisibility(false);
77
static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
79
bool isEnabled = true;
80
bool isReadOnlyControl = false;
82
if (node->isElementNode()) {
83
Element* element = static_cast<Element*>(node);
84
isEnabled = element->isEnabledFormControl();
85
isReadOnlyControl = element->isTextFormControl() && static_cast<HTMLTextFormControlElement*>(element)->readOnly();
88
style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
92
void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
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());
99
bool disabled = updateUserModifyProperty(node(), textBlockStyle);
101
textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
104
int RenderTextControl::textBlockHeight() const
106
return height() - borderAndPaddingHeight();
109
int RenderTextControl::textBlockWidth() const
111
Element* innerText = innerTextElement();
114
LayoutUnit unitWidth = width() - borderAndPaddingWidth();
115
if (innerText->renderer())
116
unitWidth -= innerText->renderBox()->paddingLeft() + innerText->renderBox()->paddingRight();
121
void RenderTextControl::updateFromElement()
123
Element* innerText = innerTextElement();
124
if (innerText && innerText->renderer())
125
updateUserModifyProperty(node(), innerText->renderer()->style());
128
VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
131
return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
132
ExceptionCode ec = 0;
133
RefPtr<Range> range = Range::create(document());
134
range->selectNodeContents(innerTextElement(), ec);
136
CharacterIterator it(range.get());
137
it.advance(index - 1);
138
return VisiblePosition(it.range()->endPosition(), UPSTREAM);
141
int RenderTextControl::scrollbarThickness() const
143
// FIXME: We should get the size of the scrollbar from the RenderTheme instead.
144
return ScrollbarTheme::theme()->scrollbarThickness();
147
void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
149
HTMLElement* innerText = innerTextElement();
151
if (RenderBox* innerTextBox = innerText->renderBox()) {
152
LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
153
logicalHeight = computeControlHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight();
155
// We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
156
if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))
157
logicalHeight += scrollbarThickness();
160
RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
163
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
165
HTMLElement* innerText = innerTextElement();
166
if (!innerText->renderer())
169
LayoutPoint adjustedLocation = accumulatedOffset + location();
170
LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location());
171
if (hasOverflowClip())
172
localPoint += scrolledContentOffset();
173
result.setInnerNode(innerText);
174
result.setInnerNonSharedNode(innerText);
175
result.setLocalPoint(localPoint);
178
static const char* fontFamiliesWithInvalidCharWidth[] = {
179
"American Typewriter",
215
// For font families where any of the fonts don't have a valid entry in the OS/2 table
216
// for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
217
// from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
218
// but, in order to get similar rendering across platforms, we do this check for
220
bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
222
static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
224
if (family.isEmpty())
227
if (!fontFamiliesWithInvalidCharWidthMap) {
228
fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
230
for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
231
fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
234
return !fontFamiliesWithInvalidCharWidthMap->contains(family);
237
float RenderTextControl::getAvgCharWidth(AtomicString family)
239
if (hasValidAvgCharWidth(family))
240
return roundf(style()->font().primaryFont()->avgCharWidth());
242
const UChar ch = '0';
243
const String str = String(&ch, 1);
244
const Font& font = style()->font();
245
TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
246
textRun.disableRoundingHacks();
247
return font.width(textRun);
250
float RenderTextControl::scaleEmToUnits(int x) const
252
// This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
253
float unitsPerEm = 2048.0f;
254
return roundf(style()->font().size() * x / unitsPerEm);
257
void RenderTextControl::computePreferredLogicalWidths()
259
ASSERT(preferredLogicalWidthsDirty());
261
m_minPreferredLogicalWidth = 0;
262
m_maxPreferredLogicalWidth = 0;
264
if (style()->width().isFixed() && style()->width().value() >= 0)
265
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value());
267
// Use average character width. Matches IE.
268
AtomicString family = style()->font().family().family();
269
m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family));
270
if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox())
271
m_maxPreferredLogicalWidth += innerTextRenderBox->paddingLeft() + innerTextRenderBox->paddingRight();
274
if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
275
m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
276
m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
277
} else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
278
m_minPreferredLogicalWidth = 0;
280
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
282
if (style()->maxWidth().isFixed()) {
283
m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
284
m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
287
LayoutUnit toAdd = borderAndPaddingWidth();
289
m_minPreferredLogicalWidth += toAdd;
290
m_maxPreferredLogicalWidth += toAdd;
292
setPreferredLogicalWidthsDirty(false);
295
void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
297
if (!size().isEmpty())
298
rects.append(pixelSnappedIntRect(additionalOffset, size()));
301
RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
303
HTMLElement* placeholder = toTextFormControl(node())->placeholderElement();
304
RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
305
if (!placeholderRenderer)
307
if (relayoutChildren) {
308
// The markParents arguments should be false because this function is
309
// called from layout() of the parent and the placeholder layout doesn't
310
// affect the parent layout.
311
placeholderRenderer->setChildNeedsLayout(true, MarkOnlyThis);
313
return placeholderRenderer;
316
bool RenderTextControl::canBeReplacedWithInlineRunIn() const
321
} // namespace WebCore