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)
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.
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.
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.
26 #include "HTMLTextFormControlElement.h"
28 #include "AXObjectCache.h"
29 #include "Attribute.h"
31 #include "ChromeClient.h"
34 #include "EventNames.h"
36 #include "HTMLBRElement.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLNames.h"
41 #include "RenderBox.h"
42 #include "RenderTextControl.h"
43 #include "RenderTheme.h"
44 #include "ScriptEventListener.h"
46 #include "TextIterator.h"
47 #include <wtf/text/StringBuilder.h>
51 using namespace HTMLNames;
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)
63 HTMLTextFormControlElement::~HTMLTextFormControlElement()
67 void HTMLTextFormControlElement::insertedIntoDocument()
69 HTMLFormControlElement::insertedIntoDocument();
70 String initialValue = value();
71 setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
74 void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
76 if (supportsPlaceholder())
77 updatePlaceholderVisibility(false);
79 HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
82 void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
84 if (supportsPlaceholder())
85 updatePlaceholderVisibility(false);
87 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
90 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
92 if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
93 m_lastChangeWasUserEdit = true;
98 HTMLFormControlElementWithState::defaultEventHandler(event);
101 void HTMLTextFormControlElement::forwardEvent(Event* event)
103 if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
105 innerTextElement()->defaultEventHandler(event);
108 String HTMLTextFormControlElement::strippedPlaceholder() const
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;
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)
123 stripped.append(character);
125 return stripped.toString();
128 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
130 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
132 const AtomicString& attributeValue = getAttribute(placeholderAttr);
133 return attributeValue.string().find(isNotLineBreak) == notFound;
136 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
138 return supportsPlaceholder()
140 && isEmptySuggestedValue()
141 && !isPlaceholderEmpty()
142 && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()))
143 && (!renderer() || renderer()->style()->visibility() == VISIBLE);
146 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
148 if (!supportsPlaceholder())
150 if (!placeholderElement() || placeholderValueChanged)
151 updatePlaceholderText();
152 HTMLElement* placeholder = placeholderElement();
155 ExceptionCode ec = 0;
156 placeholder->getInlineStyleDecl()->setProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? "visible" : "hidden", ec);
160 RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
162 if (!isTextFormControl())
164 document()->updateLayoutIgnorePendingStylesheets();
165 return toRenderTextControl(renderer());
168 void HTMLTextFormControlElement::setSelectionStart(int start)
170 setSelectionRange(start, max(start, selectionEnd()), selectionDirection());
173 void HTMLTextFormControlElement::setSelectionEnd(int end)
175 setSelectionRange(min(end, selectionStart()), end, selectionDirection());
178 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
180 setSelectionRange(selectionStart(), selectionEnd(), direction);
183 void HTMLTextFormControlElement::select()
185 setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection);
188 String HTMLTextFormControlElement::selectedText() const
190 if (!isTextFormControl())
192 return value().substring(selectionStart(), selectionEnd() - selectionStart());
195 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
197 if (m_textAsOfLastFormControlChangeEvent != value()) {
198 HTMLElement::dispatchChangeEvent();
199 setTextAsOfLastFormControlChangeEvent(value());
201 setChangedSinceLastFormControlChangeEvent(false);
204 static inline bool hasVisibleTextArea(RenderTextControl* textControl, HTMLElement* innerText)
207 return textControl->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height();
210 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
212 TextFieldSelectionDirection direction = SelectionHasNoDirection;
213 if (directionString == "forward")
214 direction = SelectionHasForwardDirection;
215 else if (directionString == "backward")
216 direction = SelectionHasBackwardDirection;
218 return setSelectionRange(start, end, direction);
221 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
223 document()->updateLayoutIgnorePendingStylesheets();
225 if (!renderer() || !renderer()->isTextControl())
229 start = min(max(start, 0), end);
231 RenderTextControl* control = toRenderTextControl(renderer());
232 if (!hasVisibleTextArea(control, innerTextElement())) {
233 cacheSelection(start, end, direction);
236 VisiblePosition startPosition = control->visiblePositionForIndex(start);
237 VisiblePosition endPosition;
239 endPosition = startPosition;
241 endPosition = control->visiblePositionForIndex(end);
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);
249 VisibleSelection newSelection;
250 if (direction == SelectionHasBackwardDirection)
251 newSelection = VisibleSelection(endPosition, startPosition);
253 newSelection = VisibleSelection(startPosition, endPosition);
254 newSelection.setIsDirectional(direction != SelectionHasNoDirection);
256 if (Frame* frame = document()->frame())
257 frame->selection()->setSelection(newSelection);
260 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
262 Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
263 if (enclosingTextFormControl(indexPosition) != this)
265 ExceptionCode ec = 0;
266 RefPtr<Range> range = Range::create(indexPosition.document());
267 range->setStart(innerTextElement(), 0, ec);
269 range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ec);
271 return TextIterator::rangeLength(range.get());
274 int HTMLTextFormControlElement::selectionStart() const
276 if (!isTextFormControl())
278 if (document()->focusedNode() != this && hasCachedSelection())
279 return m_cachedSelectionStart;
281 return computeSelectionStart();
284 int HTMLTextFormControlElement::computeSelectionStart() const
286 ASSERT(isTextFormControl());
287 Frame* frame = document()->frame();
291 return indexForVisiblePosition(frame->selection()->start());
294 int HTMLTextFormControlElement::selectionEnd() const
296 if (!isTextFormControl())
298 if (document()->focusedNode() != this && hasCachedSelection())
299 return m_cachedSelectionEnd;
300 return computeSelectionEnd();
303 int HTMLTextFormControlElement::computeSelectionEnd() const
305 ASSERT(isTextFormControl());
306 Frame* frame = document()->frame();
310 return indexForVisiblePosition(frame->selection()->end());
313 static const AtomicString& directionString(TextFieldSelectionDirection direction)
315 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none"));
316 DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward"));
317 DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward"));
320 case SelectionHasNoDirection:
322 case SelectionHasForwardDirection:
324 case SelectionHasBackwardDirection:
328 ASSERT_NOT_REACHED();
332 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
334 if (!isTextFormControl())
335 return directionString(SelectionHasNoDirection);
336 if (document()->focusedNode() != this && hasCachedSelection())
337 return directionString(m_cachedSelectionDirection);
339 return directionString(computeSelectionDirection());
342 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
344 ASSERT(isTextFormControl());
345 Frame* frame = document()->frame();
347 return SelectionHasNoDirection;
349 const VisibleSelection& selection = frame->selection()->selection();
350 return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
353 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
355 if (node->isTextNode()) {
356 containerNode = node;
357 offsetInContainer = offset;
359 containerNode = node->parentNode();
360 offsetInContainer = node->nodeIndex() + offset;
364 PassRefPtr<Range> HTMLTextFormControlElement::selection() const
366 if (!renderer() || !isTextFormControl() || !hasCachedSelection())
369 int start = m_cachedSelectionStart;
370 int end = m_cachedSelectionEnd;
372 ASSERT(start <= end);
373 HTMLElement* innerText = innerTextElement();
377 if (!innerText->firstChild())
378 return Range::create(document(), innerText, 0, innerText, 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;
388 if (offset <= start && start <= offset + length)
389 setContainerAndOffsetForRange(node, start - offset, startNode, start);
391 if (offset <= end && end <= offset + length) {
392 setContainerAndOffsetForRange(node, end - offset, endNode, end);
399 if (!startNode || !endNode)
402 return Range::create(document(), startNode, start, endNode, end);
405 void HTMLTextFormControlElement::restoreCachedSelection()
407 setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
410 void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
412 if (!renderer() || !isTextFormControl())
415 // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
416 cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
418 if (Frame* frame = document()->frame()) {
419 if (frame->selection()->isRange() && userTriggered)
420 dispatchEvent(Event::create(eventNames().selectEvent, true, false));
424 void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr)
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));
433 HTMLFormControlElementWithState::parseMappedAttribute(attr);
436 void HTMLTextFormControlElement::notifyFormStateChanged()
438 Frame* frame = document()->frame();
442 if (Page* page = frame->page())
443 page->chrome()->client()->formStateDidChange(this);
446 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
448 if (!isTextFormControl())
450 return m_lastChangeWasUserEdit;
453 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
455 if (!isTextFormControl())
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);
463 ExceptionCode ec = 0;
464 innerTextElement()->setInnerText(value, ec);
467 if (value.endsWith("\n") || value.endsWith("\r")) {
468 innerTextElement()->appendChild(HTMLBRElement::create(document()), ec);
473 setFormControlValueMatchesRenderer(true);
476 static String finishText(StringBuilder& result)
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();
485 String HTMLTextFormControlElement::innerTextValue() const
487 HTMLElement* innerText = innerTextElement();
488 if (!innerText || !isTextFormControl())
489 return emptyString();
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());
498 return finishText(result);
501 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
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();
518 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
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())
526 RenderBlock* renderer = toRenderBlock(innerText->renderer());
531 unsigned breakOffset;
532 RootInlineBox* line = renderer->firstRootBox();
536 getNextSoftBreak(line, breakNode, breakOffset);
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);
552 getNextSoftBreak(line, breakNode, breakOffset);
554 result.append(data.characters() + position, length - position);
556 while (breakNode == node)
557 getNextSoftBreak(line, breakNode, breakOffset);
559 return finishText(result);
562 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
564 ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
565 || position.containerNode() || !position.anchorNode()->shadowAncestorNode());
566 Node* container = position.containerNode();
569 Node* ancestor = container->shadowAncestorNode();
570 return ancestor != container ? toTextFormControl(ancestor) : 0;
573 } // namespace Webcore