~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/rendering/RenderTextControl.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "StyleInheritedData.h"
 
31
#include "TextIterator.h"
 
32
#include "VisiblePosition.h"
 
33
#include <wtf/unicode/CharacterNames.h>
 
34
 
 
35
using namespace std;
 
36
 
 
37
namespace WebCore {
 
38
 
 
39
RenderTextControl::RenderTextControl(Node* node)
 
40
    : RenderBlock(node)
 
41
{
 
42
    ASSERT(toTextFormControl(node));
 
43
}
 
44
 
 
45
RenderTextControl::~RenderTextControl()
 
46
{
 
47
}
 
48
 
 
49
HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
 
50
{
 
51
    return static_cast<HTMLTextFormControlElement*>(node());
 
52
}
 
53
 
 
54
HTMLElement* RenderTextControl::innerTextElement() const
 
55
{
 
56
    return textFormControlElement()->innerTextElement();
 
57
}
 
58
 
 
59
void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 
60
{
 
61
    RenderBlock::styleDidChange(diff, oldStyle);
 
62
    Element* innerText = innerTextElement();
 
63
    if (!innerText)
 
64
        return;
 
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();
 
73
    }
 
74
    textFormControlElement()->updatePlaceholderVisibility(false);
 
75
}
 
76
 
 
77
static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
 
78
{
 
79
    bool isEnabled = true;
 
80
    bool isReadOnlyControl = false;
 
81
 
 
82
    if (node->isElementNode()) {
 
83
        Element* element = static_cast<Element*>(node);
 
84
        isEnabled = element->isEnabledFormControl();
 
85
        isReadOnlyControl = element->isTextFormControl() && static_cast<HTMLTextFormControlElement*>(element)->readOnly();
 
86
    }
 
87
 
 
88
    style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
 
89
    return !isEnabled;
 
90
}
 
91
 
 
92
void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
 
93
{
 
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());
 
98
 
 
99
    bool disabled = updateUserModifyProperty(node(), textBlockStyle);
 
100
    if (disabled)
 
101
        textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
 
102
}
 
103
 
 
104
int RenderTextControl::textBlockHeight() const
 
105
{
 
106
    return height() - borderAndPaddingHeight();
 
107
}
 
108
 
 
109
int RenderTextControl::textBlockWidth() const
 
110
{
 
111
    Element* innerText = innerTextElement();
 
112
    ASSERT(innerText);
 
113
 
 
114
    LayoutUnit unitWidth = width() - borderAndPaddingWidth();
 
115
    if (innerText->renderer())
 
116
        unitWidth -= innerText->renderBox()->paddingLeft() + innerText->renderBox()->paddingRight();
 
117
 
 
118
    return unitWidth;
 
119
}
 
120
 
 
121
void RenderTextControl::updateFromElement()
 
122
{
 
123
    Element* innerText = innerTextElement();
 
124
    if (innerText && innerText->renderer())
 
125
        updateUserModifyProperty(node(), innerText->renderer()->style());
 
126
}
 
127
 
 
128
VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
 
129
{
 
130
    if (index <= 0)
 
131
        return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
 
132
    ExceptionCode ec = 0;
 
133
    RefPtr<Range> range = Range::create(document());
 
134
    range->selectNodeContents(innerTextElement(), ec);
 
135
    ASSERT(!ec);
 
136
    CharacterIterator it(range.get());
 
137
    it.advance(index - 1);
 
138
    return VisiblePosition(it.range()->endPosition(), UPSTREAM);
 
139
}
 
140
 
 
141
int RenderTextControl::scrollbarThickness() const
 
142
{
 
143
    // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
 
144
    return ScrollbarTheme::theme()->scrollbarThickness();
 
145
}
 
146
 
 
147
void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
 
148
{
 
149
    HTMLElement* innerText = innerTextElement();
 
150
    ASSERT(innerText);
 
151
    if (RenderBox* innerTextBox = innerText->renderBox()) {
 
152
        LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
 
153
        logicalHeight = computeControlHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight();
 
154
 
 
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();
 
158
    }
 
159
 
 
160
    RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
 
161
}
 
162
 
 
163
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
 
164
{
 
165
    HTMLElement* innerText = innerTextElement();
 
166
    if (!innerText->renderer())
 
167
        return;
 
168
 
 
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);
 
176
}
 
177
 
 
178
static const char* fontFamiliesWithInvalidCharWidth[] = {
 
179
    "American Typewriter",
 
180
    "Arial Hebrew",
 
181
    "Chalkboard",
 
182
    "Cochin",
 
183
    "Corsiva Hebrew",
 
184
    "Courier",
 
185
    "Euphemia UCAS",
 
186
    "Geneva",
 
187
    "Gill Sans",
 
188
    "Hei",
 
189
    "Helvetica",
 
190
    "Hoefler Text",
 
191
    "InaiMathi",
 
192
    "Kai",
 
193
    "Lucida Grande",
 
194
    "Marker Felt",
 
195
    "Monaco",
 
196
    "Mshtakan",
 
197
    "New Peninim MT",
 
198
    "Osaka",
 
199
    "Raanana",
 
200
    "STHeiti",
 
201
    "Symbol",
 
202
    "Times",
 
203
    "Apple Braille",
 
204
    "Apple LiGothic",
 
205
    "Apple LiSung",
 
206
    "Apple Symbols",
 
207
    "AppleGothic",
 
208
    "AppleMyungjo",
 
209
    "#GungSeo",
 
210
    "#HeadLineA",
 
211
    "#PCMyungjo",
 
212
    "#PilGi",
 
213
};
 
214
 
 
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
 
219
// all platforms.
 
220
bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
 
221
{
 
222
    static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
 
223
 
 
224
    if (family.isEmpty())
 
225
        return false;
 
226
 
 
227
    if (!fontFamiliesWithInvalidCharWidthMap) {
 
228
        fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
 
229
 
 
230
        for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
 
231
            fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
 
232
    }
 
233
 
 
234
    return !fontFamiliesWithInvalidCharWidthMap->contains(family);
 
235
}
 
236
 
 
237
float RenderTextControl::getAvgCharWidth(AtomicString family)
 
238
{
 
239
    if (hasValidAvgCharWidth(family))
 
240
        return roundf(style()->font().primaryFont()->avgCharWidth());
 
241
 
 
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);
 
248
}
 
249
 
 
250
float RenderTextControl::scaleEmToUnits(int x) const
 
251
{
 
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);
 
255
}
 
256
 
 
257
void RenderTextControl::computePreferredLogicalWidths()
 
258
{
 
259
    ASSERT(preferredLogicalWidthsDirty());
 
260
 
 
261
    m_minPreferredLogicalWidth = 0;
 
262
    m_maxPreferredLogicalWidth = 0;
 
263
 
 
264
    if (style()->width().isFixed() && style()->width().value() >= 0)
 
265
        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value());
 
266
    else {
 
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();
 
272
    }
 
273
 
 
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;
 
279
    else
 
280
        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
 
281
 
 
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()));
 
285
    }
 
286
 
 
287
    LayoutUnit toAdd = borderAndPaddingWidth();
 
288
 
 
289
    m_minPreferredLogicalWidth += toAdd;
 
290
    m_maxPreferredLogicalWidth += toAdd;
 
291
 
 
292
    setPreferredLogicalWidthsDirty(false);
 
293
}
 
294
 
 
295
void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
 
296
{
 
297
    if (!size().isEmpty())
 
298
        rects.append(pixelSnappedIntRect(additionalOffset, size()));
 
299
}
 
300
 
 
301
RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
 
302
{
 
303
    HTMLElement* placeholder = toTextFormControl(node())->placeholderElement();
 
304
    RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
 
305
    if (!placeholderRenderer)
 
306
        return 0;
 
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);
 
312
    }
 
313
    return placeholderRenderer;
 
314
}
 
315
 
 
316
bool RenderTextControl::canBeReplacedWithInlineRunIn() const
 
317
{
 
318
    return false;
 
319
}
 
320
 
 
321
} // namespace WebCore