initial import
[vuplus_webkit] / Source / WebCore / html / HTMLTextFormControlElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLTextFormControlElement.h"
27
28 #include "AXObjectCache.h"
29 #include "Attribute.h"
30 #include "Chrome.h"
31 #include "ChromeClient.h"
32 #include "Document.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "Frame.h"
36 #include "HTMLBRElement.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLNames.h"
40 #include "Page.h"
41 #include "RenderBox.h"
42 #include "RenderTextControl.h"
43 #include "RenderTheme.h"
44 #include "ScriptEventListener.h"
45 #include "Text.h"
46 #include "TextIterator.h"
47 #include <wtf/text/StringBuilder.h>
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52 using namespace std;
53
54 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form)
55     : HTMLFormControlElementWithState(tagName, doc, form)
56     , m_lastChangeWasUserEdit(false)
57     , m_cachedSelectionStart(-1)
58     , m_cachedSelectionEnd(-1)
59     , m_cachedSelectionDirection(SelectionHasNoDirection)
60 {
61 }
62
63 HTMLTextFormControlElement::~HTMLTextFormControlElement()
64 {
65 }
66
67 void HTMLTextFormControlElement::insertedIntoDocument()
68 {
69     HTMLFormControlElement::insertedIntoDocument();
70     String initialValue = value();
71     setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
72 }
73
74 void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
75 {
76     if (supportsPlaceholder())
77         updatePlaceholderVisibility(false);
78     handleFocusEvent();
79     HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
80 }
81
82 void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
83 {
84     if (supportsPlaceholder())
85         updatePlaceholderVisibility(false);
86     handleBlurEvent();
87     HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
88 }
89
90 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
91 {
92     if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
93         m_lastChangeWasUserEdit = true;
94         subtreeHasChanged();
95         return;
96     }
97
98     HTMLFormControlElementWithState::defaultEventHandler(event);
99 }
100
101 void HTMLTextFormControlElement::forwardEvent(Event* event)
102 {
103     if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
104         return;
105     innerTextElement()->defaultEventHandler(event);
106 }
107
108 String HTMLTextFormControlElement::strippedPlaceholder() const
109 {
110     // According to the HTML5 specification, we need to remove CR and LF from
111     // the attribute value.
112     const AtomicString& attributeValue = getAttribute(placeholderAttr);
113     if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
114         return attributeValue;
115
116     StringBuilder stripped;
117     unsigned length = attributeValue.length();
118     stripped.reserveCapacity(length);
119     for (unsigned i = 0; i < length; ++i) {
120         UChar character = attributeValue[i];
121         if (character == newlineCharacter || character == carriageReturn)
122             continue;
123         stripped.append(character);
124     }
125     return stripped.toString();
126 }
127
128 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
129
130 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
131 {
132     const AtomicString& attributeValue = getAttribute(placeholderAttr);
133     return attributeValue.string().find(isNotLineBreak) == notFound;
134 }
135
136 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
137 {
138     return supportsPlaceholder()
139         && isEmptyValue()
140         && isEmptySuggestedValue()
141         && !isPlaceholderEmpty()
142         && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()))
143         && (!renderer() || renderer()->style()->visibility() == VISIBLE);
144 }
145
146 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
147 {
148     if (!supportsPlaceholder())
149         return;
150     if (!placeholderElement() || placeholderValueChanged)
151         updatePlaceholderText();
152     HTMLElement* placeholder = placeholderElement();
153     if (!placeholder)
154         return;
155     ExceptionCode ec = 0;
156     placeholder->getInlineStyleDecl()->setProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? "visible" : "hidden", ec);
157     ASSERT(!ec);
158 }
159
160 RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
161 {
162     if (!isTextFormControl())
163         return 0;
164     document()->updateLayoutIgnorePendingStylesheets();
165     return toRenderTextControl(renderer());
166 }
167
168 void HTMLTextFormControlElement::setSelectionStart(int start)
169 {
170     setSelectionRange(start, max(start, selectionEnd()), selectionDirection());
171 }
172
173 void HTMLTextFormControlElement::setSelectionEnd(int end)
174 {
175     setSelectionRange(min(end, selectionStart()), end, selectionDirection());
176 }
177
178 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
179 {
180     setSelectionRange(selectionStart(), selectionEnd(), direction);
181 }
182
183 void HTMLTextFormControlElement::select()
184 {
185     setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection);
186 }
187
188 String HTMLTextFormControlElement::selectedText() const
189 {
190     if (!isTextFormControl())
191         return String();
192     return value().substring(selectionStart(), selectionEnd() - selectionStart());
193 }
194
195 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
196 {
197     if (m_textAsOfLastFormControlChangeEvent != value()) {
198         HTMLElement::dispatchChangeEvent();
199         setTextAsOfLastFormControlChangeEvent(value());
200     }
201     setChangedSinceLastFormControlChangeEvent(false);
202 }
203
204 static inline bool hasVisibleTextArea(RenderTextControl* textControl, HTMLElement* innerText)
205 {
206     ASSERT(textControl);
207     return textControl->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height();
208 }
209
210 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
211 {
212     TextFieldSelectionDirection direction = SelectionHasNoDirection;
213     if (directionString == "forward")
214         direction = SelectionHasForwardDirection;
215     else if (directionString == "backward")
216         direction = SelectionHasBackwardDirection;
217
218     return setSelectionRange(start, end, direction);
219 }
220
221 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
222 {
223     document()->updateLayoutIgnorePendingStylesheets();
224
225     if (!renderer() || !renderer()->isTextControl())
226         return;
227
228     end = max(end, 0);
229     start = min(max(start, 0), end);
230
231     RenderTextControl* control = toRenderTextControl(renderer());
232     if (!hasVisibleTextArea(control, innerTextElement())) {
233         cacheSelection(start, end, direction);
234         return;
235     }
236     VisiblePosition startPosition = control->visiblePositionForIndex(start);
237     VisiblePosition endPosition;
238     if (start == end)
239         endPosition = startPosition;
240     else
241         endPosition = control->visiblePositionForIndex(end);
242
243     // startPosition and endPosition can be null position for example when
244     // "-webkit-user-select: none" style attribute is specified.
245     if (startPosition.isNotNull() && endPosition.isNotNull()) {
246         ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this
247             && endPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this);
248     }
249     VisibleSelection newSelection;
250     if (direction == SelectionHasBackwardDirection)
251         newSelection = VisibleSelection(endPosition, startPosition);
252     else
253         newSelection = VisibleSelection(startPosition, endPosition);
254     newSelection.setIsDirectional(direction != SelectionHasNoDirection);
255
256     if (Frame* frame = document()->frame())
257         frame->selection()->setSelection(newSelection);
258 }
259
260 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
261 {
262     Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
263     if (enclosingTextFormControl(indexPosition) != this)
264         return 0;
265     ExceptionCode ec = 0;
266     RefPtr<Range> range = Range::create(indexPosition.document());
267     range->setStart(innerTextElement(), 0, ec);
268     ASSERT(!ec);
269     range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ec);
270     ASSERT(!ec);
271     return TextIterator::rangeLength(range.get());
272 }
273
274 int HTMLTextFormControlElement::selectionStart() const
275 {
276     if (!isTextFormControl())
277         return 0;
278     if (document()->focusedNode() != this && hasCachedSelection())
279         return m_cachedSelectionStart;
280
281     return computeSelectionStart();
282 }
283
284 int HTMLTextFormControlElement::computeSelectionStart() const
285 {
286     ASSERT(isTextFormControl());
287     Frame* frame = document()->frame();
288     if (!frame)
289         return 0;
290
291     return indexForVisiblePosition(frame->selection()->start());
292 }
293
294 int HTMLTextFormControlElement::selectionEnd() const
295 {
296     if (!isTextFormControl())
297         return 0;
298     if (document()->focusedNode() != this && hasCachedSelection())
299         return m_cachedSelectionEnd;
300     return computeSelectionEnd();
301 }
302
303 int HTMLTextFormControlElement::computeSelectionEnd() const
304 {
305     ASSERT(isTextFormControl());
306     Frame* frame = document()->frame();
307     if (!frame)
308         return 0;
309
310     return indexForVisiblePosition(frame->selection()->end());
311 }
312
313 static const AtomicString& directionString(TextFieldSelectionDirection direction)
314 {
315     DEFINE_STATIC_LOCAL(const AtomicString, none, ("none"));
316     DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward"));
317     DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward"));
318
319     switch (direction) {
320     case SelectionHasNoDirection:
321         return none;
322     case SelectionHasForwardDirection:
323         return forward;
324     case SelectionHasBackwardDirection:
325         return backward;
326     }
327
328     ASSERT_NOT_REACHED();
329     return none;
330 }
331
332 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
333 {
334     if (!isTextFormControl())
335         return directionString(SelectionHasNoDirection);
336     if (document()->focusedNode() != this && hasCachedSelection())
337         return directionString(m_cachedSelectionDirection);
338
339     return directionString(computeSelectionDirection());
340 }
341
342 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
343 {
344     ASSERT(isTextFormControl());
345     Frame* frame = document()->frame();
346     if (!frame)
347         return SelectionHasNoDirection;
348
349     const VisibleSelection& selection = frame->selection()->selection();
350     return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
351 }
352
353 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
354 {
355     if (node->isTextNode()) {
356         containerNode = node;
357         offsetInContainer = offset;
358     } else {
359         containerNode = node->parentNode();
360         offsetInContainer = node->nodeIndex() + offset;
361     }
362 }
363
364 PassRefPtr<Range> HTMLTextFormControlElement::selection() const
365 {
366     if (!renderer() || !isTextFormControl() || !hasCachedSelection())
367         return 0;
368
369     int start = m_cachedSelectionStart;
370     int end = m_cachedSelectionEnd;
371
372     ASSERT(start <= end);
373     HTMLElement* innerText = innerTextElement();
374     if (!innerText)
375         return 0;
376
377     if (!innerText->firstChild())
378         return Range::create(document(), innerText, 0, innerText, 0);
379
380     int offset = 0;
381     Node* startNode = 0;
382     Node* endNode = 0;
383     for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) {
384         ASSERT(!node->firstChild());
385         ASSERT(node->isTextNode() || node->hasTagName(brTag));
386         int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
387
388         if (offset <= start && start <= offset + length)
389             setContainerAndOffsetForRange(node, start - offset, startNode, start);
390
391         if (offset <= end && end <= offset + length) {
392             setContainerAndOffsetForRange(node, end - offset, endNode, end);
393             break;
394         }
395
396         offset += length;
397     }
398
399     if (!startNode || !endNode)
400         return 0;
401
402     return Range::create(document(), startNode, start, endNode, end);
403 }
404
405 void HTMLTextFormControlElement::restoreCachedSelection()
406 {
407     setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
408 }
409
410 void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
411 {
412     if (!renderer() || !isTextFormControl())
413         return;
414
415     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
416     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
417
418     if (Frame* frame = document()->frame()) {
419         if (frame->selection()->isRange() && userTriggered)
420             dispatchEvent(Event::create(eventNames().selectEvent, true, false));
421     }
422 }
423
424 void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr)
425 {
426     if (attr->name() == placeholderAttr)
427         updatePlaceholderVisibility(true);
428     else if (attr->name() == onselectAttr)
429         setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
430     else if (attr->name() == onchangeAttr)
431         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
432     else
433         HTMLFormControlElementWithState::parseMappedAttribute(attr);
434 }
435
436 void HTMLTextFormControlElement::notifyFormStateChanged()
437 {
438     Frame* frame = document()->frame();
439     if (!frame)
440         return;
441
442     if (Page* page = frame->page())
443         page->chrome()->client()->formStateDidChange(this);
444 }
445
446 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
447 {
448     if (!isTextFormControl())
449         return false;
450     return m_lastChangeWasUserEdit;
451 }
452
453 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
454 {
455     if (!isTextFormControl())
456         return;
457
458     bool textIsChanged = value != innerTextValue();
459     if (textIsChanged || !innerTextElement()->hasChildNodes()) {
460         if (textIsChanged && document() && renderer() && AXObjectCache::accessibilityEnabled())
461             document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXValueChanged, false);
462
463         ExceptionCode ec = 0;
464         innerTextElement()->setInnerText(value, ec);
465         ASSERT(!ec);
466
467         if (value.endsWith("\n") || value.endsWith("\r")) {
468             innerTextElement()->appendChild(HTMLBRElement::create(document()), ec);
469             ASSERT(!ec);
470         }
471     }
472
473     setFormControlValueMatchesRenderer(true);
474 }
475
476 static String finishText(StringBuilder& result)
477 {
478     // Remove one trailing newline; there's always one that's collapsed out by rendering.
479     size_t size = result.length();
480     if (size && result[size - 1] == '\n')
481         result.resize(--size);
482     return result.toString();
483 }
484
485 String HTMLTextFormControlElement::innerTextValue() const
486 {
487     HTMLElement* innerText = innerTextElement();
488     if (!innerText || !isTextFormControl())
489         return emptyString();
490
491     StringBuilder result;
492     for (Node* node = innerText; node; node = node->traverseNextNode(innerText)) {
493         if (node->hasTagName(brTag))
494             result.append(newlineCharacter);
495         else if (node->isTextNode())
496             result.append(static_cast<Text*>(node)->data());
497     }
498     return finishText(result);
499 }
500
501 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
502 {
503     RootInlineBox* next;
504     for (; line; line = next) {
505         next = line->nextRootBox();
506         if (next && !line->endsWithBreak()) {
507             ASSERT(line->lineBreakObj());
508             breakNode = line->lineBreakObj()->node();
509             breakOffset = line->lineBreakPos();
510             line = next;
511             return;
512         }
513     }
514     breakNode = 0;
515     breakOffset = 0;
516 }
517
518 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
519 {
520     // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
521     // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
522     HTMLElement* innerText = innerTextElement();
523     if (!innerText || !isTextFormControl())
524         return value();
525
526     RenderBlock* renderer = toRenderBlock(innerText->renderer());
527     if (!renderer)
528         return value();
529
530     Node* breakNode;
531     unsigned breakOffset;
532     RootInlineBox* line = renderer->firstRootBox();
533     if (!line)
534         return value();
535
536     getNextSoftBreak(line, breakNode, breakOffset);
537
538     StringBuilder result;
539     for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) {
540         if (node->hasTagName(brTag))
541             result.append(newlineCharacter);
542         else if (node->isTextNode()) {
543             String data = static_cast<Text*>(node)->data();
544             unsigned length = data.length();
545             unsigned position = 0;
546             while (breakNode == node && breakOffset <= length) {
547                 if (breakOffset > position) {
548                     result.append(data.characters() + position, breakOffset - position);
549                     position = breakOffset;
550                     result.append(newlineCharacter);
551                 }
552                 getNextSoftBreak(line, breakNode, breakOffset);
553             }
554             result.append(data.characters() + position, length - position);
555         }
556         while (breakNode == node)
557             getNextSoftBreak(line, breakNode, breakOffset);
558     }
559     return finishText(result);
560 }
561
562 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
563 {
564     ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
565            || position.containerNode() || !position.anchorNode()->shadowAncestorNode());
566     Node* container = position.containerNode();
567     if (!container)
568         return 0;
569     Node* ancestor = container->shadowAncestorNode();
570     return ancestor != container ? toTextFormControl(ancestor) : 0;
571 }
572
573 } // namespace Webcore