2 * Copyright (C) 2006 Apple Computer, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "RenderTextControl.h"
26 #include "EventNames.h"
28 #include "HTMLBRElement.h"
29 #include "HTMLInputElement.h"
30 #include "HTMLNames.h"
31 #include "HTMLTextAreaElement.h"
32 #include "HTMLTextFieldInnerElement.h"
33 #include "HitTestResult.h"
34 #include "RenderTheme.h"
35 #include "SelectionController.h"
36 #include "TextIterator.h"
37 #include "TextStyle.h"
38 #include "htmlediting.h"
39 #include "visible_units.h"
44 using namespace EventNames;
45 using namespace HTMLNames;
48 RenderTextControl::RenderTextControl(Node* node, bool multiLine)
49 : RenderBlock(node), m_dirty(false), m_multiLine(multiLine)
53 RenderTextControl::~RenderTextControl()
55 if (m_multiLine && node())
56 static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
57 // The renderer for the div has already been destroyed by destroyLeftoverChildren
62 void RenderTextControl::setStyle(RenderStyle* style)
64 RenderBlock::setStyle(style);
66 RenderBlock* divRenderer = static_cast<RenderBlock*>(m_div->renderer());
67 RenderStyle* divStyle = createDivStyle(style);
68 divRenderer->setStyle(divStyle);
69 for (Node *n = m_div->firstChild(); n; n = n->traverseNextNode(m_div.get()))
71 n->renderer()->setStyle(divStyle);
73 setHasOverflowClip(false);
74 setReplaced(isInline());
77 RenderStyle* RenderTextControl::createDivStyle(RenderStyle* startStyle)
79 RenderStyle* divStyle = new (renderArena()) RenderStyle();
80 HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
82 divStyle->inheritFrom(startStyle);
83 divStyle->setDisplay(BLOCK);
84 divStyle->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
87 // Forward overflow properties.
88 divStyle->setOverflowX(startStyle->overflowX() == OVISIBLE ? OAUTO : startStyle->overflowX());
89 divStyle->setOverflowY(startStyle->overflowY() == OVISIBLE ? OAUTO : startStyle->overflowY());
91 // Set word wrap property based on wrap attribute
92 if (static_cast<HTMLTextAreaElement*>(element)->wrap() == HTMLTextAreaElement::ta_NoWrap) {
93 divStyle->setWhiteSpace(PRE);
94 divStyle->setWordWrap(WBNORMAL);
96 divStyle->setWhiteSpace(PRE_WRAP);
97 divStyle->setWordWrap(BREAK_WORD);
101 divStyle->setWhiteSpace(PRE);
102 divStyle->setOverflowX(OHIDDEN);
103 divStyle->setOverflowY(OHIDDEN);
107 // We're adding one extra pixel of padding to match WinIE.
108 divStyle->setPaddingLeft(Length(1, Fixed));
109 divStyle->setPaddingRight(Length(1, Fixed));
111 // We're adding three extra pixels of padding to line textareas up with text fields.
112 divStyle->setPaddingLeft(Length(3, Fixed));
113 divStyle->setPaddingRight(Length(3, Fixed));
116 if (!element->isEnabled()) {
117 Color textColor = startStyle->color();
118 Color disabledTextColor;
119 if (differenceSquared(textColor, Color::white) > differenceSquared(startStyle->backgroundColor(), Color::white))
120 disabledTextColor = textColor.light();
122 disabledTextColor = textColor.dark();
123 divStyle->setColor(disabledTextColor);
129 void RenderTextControl::updateFromElement()
132 // Create the div and give it a parent, renderer, and style
133 m_div = new HTMLTextFieldInnerElement(document(), node());
134 RenderBlock* divRenderer = new (renderArena()) RenderBlock(m_div.get());
135 m_div->setRenderer(divRenderer);
136 m_div->setAttached();
137 m_div->setInDocument(true);
139 RenderStyle* divStyle = createDivStyle(style());
140 divRenderer->setStyle(divStyle);
142 // Add div to Render tree
143 RenderBlock::addChild(divRenderer);
146 HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
147 m_div->renderer()->style()->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
150 value = static_cast<HTMLTextAreaElement*>(element)->value().copy();
152 value = static_cast<HTMLInputElement*>(element)->value().copy();
153 if (!element->valueMatchesRenderer() || m_multiLine) {
154 String oldText = text();
157 value.replace('\\', backslashAsCurrencySymbol());
158 if (value != oldText || !m_div->hasChildNodes()) {
159 ExceptionCode ec = 0;
160 m_div->setInnerText(value, ec);
161 if (value.endsWith("\n") || value.endsWith("\r"))
162 m_div->appendChild(new HTMLBRElement(document()), ec);
163 if (document()->frame())
164 document()->frame()->clearUndoRedoOperations();
167 element->setValueMatchesRenderer();
171 int RenderTextControl::selectionStart()
173 return indexForVisiblePosition(document()->frame()->selectionController()->start());
176 int RenderTextControl::selectionEnd()
178 return indexForVisiblePosition(document()->frame()->selectionController()->end());
181 void RenderTextControl::setSelectionStart(int start)
183 setSelectionRange(start, max(start, selectionEnd()));
186 void RenderTextControl::setSelectionEnd(int end)
188 setSelectionRange(min(end, selectionStart()), end);
191 void RenderTextControl::select()
193 setSelectionRange(0, text().length());
196 void RenderTextControl::setSelectionRange(int start, int end)
199 start = min(max(start, 0), end);
201 document()->updateLayout();
203 if (style()->visibility() == HIDDEN) {
205 static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end);
207 static_cast<HTMLInputElement*>(node())->cacheSelection(start, end);
210 VisiblePosition startPosition = visiblePositionForIndex(start);
211 VisiblePosition endPosition;
213 endPosition = startPosition;
215 endPosition = visiblePositionForIndex(end);
217 ASSERT(startPosition.isNotNull() && endPosition.isNotNull());
218 ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
220 Selection newSelection = Selection(startPosition, endPosition);
221 document()->frame()->selectionController()->setSelection(newSelection);
222 // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
223 // The granularity in the selection controller should be used, and then this line of code would not be needed.
224 document()->frame()->setSelectionGranularity(CharacterGranularity);
227 VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
230 return VisiblePosition(m_div.get(), 0, DOWNSTREAM);
231 ExceptionCode ec = 0;
232 RefPtr<Range> range = new Range(document());
233 range->selectNodeContents(m_div.get(), ec);
234 CharacterIterator it(range.get());
235 it.advance(index - 1);
236 return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
239 int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos)
241 Position indexPosition = pos.deepEquivalent();
242 if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_div)
244 ExceptionCode ec = 0;
245 RefPtr<Range> range = new Range(document());
246 range->setStart(m_div.get(), 0, ec);
247 range->setEnd(indexPosition.node(), indexPosition.offset(), ec);
248 return TextIterator::rangeLength(range.get());
251 void RenderTextControl::subtreeHasChanged()
253 bool wasPreviouslyEdited = isEdited();
255 HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
257 element->setValueMatchesRenderer(false);
258 document()->frame()->textDidChangeInTextArea(element);
260 HTMLInputElement* input = static_cast<HTMLInputElement*>(element);
262 input->setValueFromRenderer(text());
263 if (!wasPreviouslyEdited)
264 document()->frame()->textFieldDidBeginEditing(input);
265 document()->frame()->textDidChangeInTextField(input);
270 String RenderTextControl::text()
273 return m_div->textContent().replace('\\', backslashAsCurrencySymbol());
277 String RenderTextControl::textWithHardLineBreaks()
281 if (!m_div || !m_div->firstChild())
284 document()->updateLayout();
286 RenderObject* renderer = m_div->firstChild()->renderer();
290 InlineBox* box = renderer->inlineBox(0, DOWNSTREAM);
294 ExceptionCode ec = 0;
295 RefPtr<Range> range = new Range(document());
296 range->selectNodeContents(m_div.get(), ec);
297 for (RootInlineBox* line = box->root(); line; line = line->nextRootBox()) {
298 // If we're at a soft wrap, then insert the hard line break here
299 if (!line->endsWithBreak() && line->nextRootBox()) {
300 // Update range so it ends before this wrap
301 ASSERT(line->lineBreakObj());
302 range->setEnd(line->lineBreakObj()->node(), line->lineBreakPos(), ec);
304 s.append(range->toString(true, ec));
307 // Update range so it starts after this wrap
308 range->setEnd(m_div.get(), maxDeepOffset(m_div.get()), ec);
309 range->setStart(line->lineBreakObj()->node(), line->lineBreakPos(), ec);
312 s.append(range->toString(true, ec));
315 return s.replace('\\', backslashAsCurrencySymbol());
318 void RenderTextControl::calcHeight()
323 rows = static_cast<HTMLTextAreaElement*>(node())->rows();
325 int line = m_div->renderer()->lineHeight(true);
326 int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom() +
327 m_div->renderer()->paddingTop() + m_div->renderer()->paddingBottom();
329 // FIXME: We should get the size of the scrollbar from the RenderTheme instead of hard coding it here.
330 int scrollbarSize = 0;
331 // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
332 if (m_div->renderer()->style()->overflowX() == OSCROLL || (m_div->renderer()->style()->overflowX() == OAUTO && m_div->renderer()->style()->wordWrap() == WBNORMAL))
335 m_height = line * rows + toAdd + scrollbarSize;
337 RenderBlock::calcHeight();
340 short RenderTextControl::baselinePosition(bool b, bool isRootLineBox) const
343 return height() + marginTop() + marginBottom();
344 return RenderBlock::baselinePosition(b, isRootLineBox);
347 bool RenderTextControl::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
349 // If we're within the text control, we want to act as if we've hit the inner div, incase the point
350 // was on the control but not on the div (see Radar 4617841).
351 if (RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) {
352 result.setInnerNode(m_div.get());
358 void RenderTextControl::layout()
360 int oldHeight = m_height;
362 bool relayoutChildren = oldHeight != m_height;
364 // Set the div's height
365 int divHeight = m_height - paddingTop() - paddingBottom() - borderTop() - borderBottom();
366 m_div->renderer()->style()->setHeight(Length(divHeight, Fixed));
368 RenderBlock::layoutBlock(relayoutChildren);
371 void RenderTextControl::calcMinMaxWidth()
376 if (style()->width().isFixed() && style()->width().value() > 0)
377 m_minWidth = m_maxWidth = calcContentBoxWidth(style()->width().value());
379 // Figure out how big a text control needs to be for a given number of characters
380 // (using "0" as the nominal character).
381 const UChar ch = '0';
382 float charWidth = style()->font().floatWidth(TextRun(&ch, 1), TextStyle(0, 0, 0, false, false, false));
384 int scrollbarSize = 0;
386 factor = static_cast<HTMLTextAreaElement*>(node())->cols();
387 // FIXME: We should get the size of the scrollbar from the RenderTheme instead of hard coding it here.
388 if (m_div->renderer()->style()->overflowY() != OHIDDEN)
392 factor = static_cast<HTMLInputElement*>(node())->size();
396 m_maxWidth = (int)ceilf(charWidth * factor) + scrollbarSize;
399 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
400 m_maxWidth = max(m_maxWidth, calcContentBoxWidth(style()->minWidth().value()));
401 m_minWidth = max(m_minWidth, calcContentBoxWidth(style()->minWidth().value()));
402 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
405 m_minWidth = m_maxWidth;
407 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
408 m_maxWidth = min(m_maxWidth, calcContentBoxWidth(style()->maxWidth().value()));
409 m_minWidth = min(m_minWidth, calcContentBoxWidth(style()->maxWidth().value()));
412 int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight() +
413 m_div->renderer()->paddingLeft() + m_div->renderer()->paddingRight();
420 void RenderTextControl::forwardEvent(Event* evt)
422 if (evt->type() == blurEvent) {
423 RenderObject* innerRenderer = m_div->renderer();
425 RenderLayer* innerLayer = innerRenderer->layer();
426 if (innerLayer && !m_multiLine)
427 innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
430 m_div->defaultEventHandler(evt);
433 void RenderTextControl::selectionChanged(bool userTriggered)
435 HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
437 static_cast<HTMLTextAreaElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
439 static_cast<HTMLInputElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
440 if (document()->frame()->selectionController()->isRange() && userTriggered)
444 int RenderTextControl::scrollWidth() const
447 return m_div->scrollWidth();
448 return RenderBlock::scrollWidth();
451 int RenderTextControl::scrollHeight() const
454 return m_div->scrollHeight();
455 return RenderBlock::scrollHeight();
458 int RenderTextControl::scrollLeft() const
461 return m_div->scrollLeft();
462 return RenderBlock::scrollLeft();
465 int RenderTextControl::scrollTop() const
468 return m_div->scrollTop();
469 return RenderBlock::scrollTop();
472 void RenderTextControl::setScrollLeft(int newLeft)
475 m_div->setScrollLeft(newLeft);
478 void RenderTextControl::setScrollTop(int newTop)
481 m_div->setScrollTop(newTop);