a43b1ef3ffa7f20dd2fbb0069fb5ebd122bb0779
[WebKit-https.git] / WebCore / rendering / RenderTextControl.cpp
1 /**
2  * Copyright (C) 2006 Apple Computer, Inc.
3  *
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.
8  *
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.
13  *
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.
18  *
19  */
20
21 #include "config.h"
22 #include "RenderTextControl.h"
23
24 #include "Document.h"
25 #include "Event.h"
26 #include "EventNames.h"
27 #include "Frame.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"
40 #include <math.h>
41
42 namespace WebCore {
43
44 using namespace EventNames;
45 using namespace HTMLNames;
46 using namespace std;
47
48 RenderTextControl::RenderTextControl(Node* node, bool multiLine)
49     : RenderBlock(node), m_dirty(false), m_multiLine(multiLine)
50 {
51 }
52
53 RenderTextControl::~RenderTextControl()
54 {
55     if (m_multiLine && node())
56         static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
57     // The renderer for the div has already been destroyed by destroyLeftoverChildren
58     if (m_div)
59         m_div->detach();
60 }
61
62 void RenderTextControl::setStyle(RenderStyle* style)
63 {
64     RenderBlock::setStyle(style);
65     if (m_div) {
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()))
70             if (n->renderer())
71                 n->renderer()->setStyle(divStyle);
72     }
73     setHasOverflowClip(false);
74     setReplaced(isInline());
75 }
76
77 RenderStyle* RenderTextControl::createDivStyle(RenderStyle* startStyle)
78 {
79     RenderStyle* divStyle = new (renderArena()) RenderStyle();
80     HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
81     
82     divStyle->inheritFrom(startStyle);
83     divStyle->setDisplay(BLOCK);
84     divStyle->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
85
86     if (m_multiLine) {
87         // Forward overflow properties.
88         divStyle->setOverflowX(startStyle->overflowX() == OVISIBLE ? OAUTO : startStyle->overflowX());
89         divStyle->setOverflowY(startStyle->overflowY() == OVISIBLE ? OAUTO : startStyle->overflowY());
90
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);
95         } else {
96             divStyle->setWhiteSpace(PRE_WRAP);
97             divStyle->setWordWrap(BREAK_WORD);
98         }
99
100     } else {
101         divStyle->setWhiteSpace(PRE);
102         divStyle->setOverflowX(OHIDDEN);
103         divStyle->setOverflowY(OHIDDEN);
104     }
105
106     if (!m_multiLine) {
107         // We're adding one extra pixel of padding to match WinIE.
108         divStyle->setPaddingLeft(Length(1, Fixed));
109         divStyle->setPaddingRight(Length(1, Fixed));
110     } else {
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));
114     }
115
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();
121         else
122             disabledTextColor = textColor.dark();
123         divStyle->setColor(disabledTextColor);
124     }
125
126     return divStyle;
127 }
128
129 void RenderTextControl::updateFromElement()
130 {
131     if (!m_div) {
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);
138         
139         RenderStyle* divStyle = createDivStyle(style());
140         divRenderer->setStyle(divStyle); 
141         
142         // Add div to Render tree
143         RenderBlock::addChild(divRenderer);
144     }
145
146     HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
147     m_div->renderer()->style()->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
148     String value;
149     if (m_multiLine)
150         value = static_cast<HTMLTextAreaElement*>(element)->value().copy();    
151     else
152         value = static_cast<HTMLInputElement*>(element)->value().copy();        
153     if (!element->valueMatchesRenderer() || m_multiLine) {
154         String oldText = text();
155         if (value.isNull())
156             value = "";
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();
165             setEdited(false);
166         }
167         element->setValueMatchesRenderer();
168     }
169 }
170
171 int RenderTextControl::selectionStart()
172 {
173     return indexForVisiblePosition(document()->frame()->selectionController()->start());        
174 }
175
176 int RenderTextControl::selectionEnd()
177 {
178     return indexForVisiblePosition(document()->frame()->selectionController()->end());
179 }
180
181 void RenderTextControl::setSelectionStart(int start)
182 {
183     setSelectionRange(start, max(start, selectionEnd()));
184 }
185  
186 void RenderTextControl::setSelectionEnd(int end)
187 {
188     setSelectionRange(min(end, selectionStart()), end);
189 }
190     
191 void RenderTextControl::select()
192 {
193     setSelectionRange(0, text().length());
194 }
195
196 void RenderTextControl::setSelectionRange(int start, int end)
197 {
198     end = max(end, 0);
199     start = min(max(start, 0), end);
200     
201     document()->updateLayout();
202
203     if (style()->visibility() == HIDDEN) {
204         if (m_multiLine)
205             static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end);
206         else
207             static_cast<HTMLInputElement*>(node())->cacheSelection(start, end);
208         return;
209     }
210     VisiblePosition startPosition = visiblePositionForIndex(start);
211     VisiblePosition endPosition;
212     if (start == end)
213         endPosition = startPosition;
214     else
215         endPosition = visiblePositionForIndex(end);
216     
217     ASSERT(startPosition.isNotNull() && endPosition.isNotNull());
218     ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
219
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);
225 }
226
227 VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
228 {    
229     if (index <= 0)
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);
237 }
238
239 int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos)
240 {
241     Position indexPosition = pos.deepEquivalent();
242     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_div)
243         return 0;
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());
249 }
250
251 void RenderTextControl::subtreeHasChanged()
252 {
253     bool wasPreviouslyEdited = isEdited();
254     setEdited(true);
255     HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
256     if (m_multiLine) {
257         element->setValueMatchesRenderer(false);
258         document()->frame()->textDidChangeInTextArea(element);
259     } else {
260         HTMLInputElement* input = static_cast<HTMLInputElement*>(element);
261         if (input) {
262             input->setValueFromRenderer(text());
263             if (!wasPreviouslyEdited)
264                 document()->frame()->textFieldDidBeginEditing(input);
265             document()->frame()->textDidChangeInTextField(input);
266         }
267     }
268 }
269
270 String RenderTextControl::text()
271 {
272     if (m_div)
273         return m_div->textContent().replace('\\', backslashAsCurrencySymbol());
274     return String();
275 }
276
277 String RenderTextControl::textWithHardLineBreaks()
278 {
279     String s("");
280     
281     if (!m_div || !m_div->firstChild())
282         return s;
283
284     document()->updateLayout();
285
286     RenderObject* renderer = m_div->firstChild()->renderer();
287     if (!renderer)
288         return s;
289
290     InlineBox* box = renderer->inlineBox(0, DOWNSTREAM);
291     if (!box)
292         return s;
293
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);
303
304             s.append(range->toString(true, ec));
305             s.append("\n");
306
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);
310         }
311     }
312     s.append(range->toString(true, ec));
313     ASSERT(ec == 0);
314
315     return s.replace('\\', backslashAsCurrencySymbol());
316 }
317
318 void RenderTextControl::calcHeight()
319 {
320     
321     int rows = 1;
322     if (m_multiLine)
323         rows = static_cast<HTMLTextAreaElement*>(node())->rows();
324     
325     int line = m_div->renderer()->lineHeight(true);
326     int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom() +
327                 m_div->renderer()->paddingTop() + m_div->renderer()->paddingBottom();
328     
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))
333         scrollbarSize = 15;
334
335     m_height = line * rows + toAdd + scrollbarSize;
336     
337     RenderBlock::calcHeight();
338 }
339
340 short RenderTextControl::baselinePosition(bool b, bool isRootLineBox) const
341 {
342     if (m_multiLine)
343         return height() + marginTop() + marginBottom();
344     return RenderBlock::baselinePosition(b, isRootLineBox);
345 }
346
347 bool RenderTextControl::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
348 {
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());
353         return true;
354     }  
355     return false;
356 }
357
358 void RenderTextControl::layout()
359 {    
360     int oldHeight = m_height;
361     calcHeight();
362     bool relayoutChildren = oldHeight != m_height;
363     
364     // Set the div's height
365     int divHeight = m_height - paddingTop() - paddingBottom() - borderTop() - borderBottom();
366     m_div->renderer()->style()->setHeight(Length(divHeight, Fixed));
367
368     RenderBlock::layoutBlock(relayoutChildren);
369 }
370
371 void RenderTextControl::calcMinMaxWidth()
372 {
373     m_minWidth = 0;
374     m_maxWidth = 0;
375
376     if (style()->width().isFixed() && style()->width().value() > 0)
377         m_minWidth = m_maxWidth = calcContentBoxWidth(style()->width().value());
378     else {
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));
383         int factor;
384         int scrollbarSize = 0;
385         if (m_multiLine) {
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)
389                 scrollbarSize = 15;
390         }
391         else {
392             factor = static_cast<HTMLInputElement*>(node())->size();
393             if (factor <= 0)
394                 factor = 20;
395         }
396         m_maxWidth = (int)ceilf(charWidth * factor) + scrollbarSize;
397     }
398     
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()))
403         m_minWidth = 0;
404     else
405         m_minWidth = m_maxWidth;
406     
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()));
410     }
411
412     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight() + 
413                 m_div->renderer()->paddingLeft() + m_div->renderer()->paddingRight();
414     m_minWidth += toAdd;
415     m_maxWidth += toAdd;
416
417     setMinMaxKnown();    
418 }
419
420 void RenderTextControl::forwardEvent(Event* evt)
421 {
422     if (evt->type() == blurEvent) {
423         RenderObject* innerRenderer = m_div->renderer();
424         if (innerRenderer) {
425             RenderLayer* innerLayer = innerRenderer->layer();
426             if (innerLayer)
427                 innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
428         }
429     } else
430         m_div->defaultEventHandler(evt);
431 }
432
433 void RenderTextControl::selectionChanged(bool userTriggered)
434 {
435     HTMLGenericFormElement* element = static_cast<HTMLGenericFormElement*>(node());
436     if (m_multiLine)
437         static_cast<HTMLTextAreaElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
438     else
439         static_cast<HTMLInputElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
440     if (document()->frame()->selectionController()->isRange() && userTriggered)
441         element->onSelect();
442 }
443
444 int RenderTextControl::scrollWidth() const
445 {
446     if (m_div)
447         return m_div->scrollWidth();
448     return RenderBlock::scrollWidth();
449 }
450
451 int RenderTextControl::scrollHeight() const
452 {
453     if (m_div)
454         return m_div->scrollHeight();
455     return RenderBlock::scrollHeight();
456 }
457
458 int RenderTextControl::scrollLeft() const
459 {
460     if (m_div)
461         return m_div->scrollLeft();
462     return RenderBlock::scrollLeft();
463 }
464
465 int RenderTextControl::scrollTop() const
466 {
467     if (m_div)
468         return m_div->scrollTop();
469     return RenderBlock::scrollTop();
470 }
471
472 void RenderTextControl::setScrollLeft(int newLeft)
473 {
474     if (m_div)
475         m_div->setScrollLeft(newLeft);
476 }
477
478 void RenderTextControl::setScrollTop(int newTop)
479 {
480     if (m_div)
481         m_div->setScrollTop(newTop);
482 }
483
484 }