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 "core/html/HTMLTextFormControlElement.h"
28 #include "HTMLNames.h"
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/accessibility/AXObjectCache.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/dom/Text.h"
35 #include "core/dom/shadow/ShadowRoot.h"
36 #include "core/editing/FrameSelection.h"
37 #include "core/editing/TextIterator.h"
38 #include "core/events/Event.h"
39 #include "core/frame/LocalFrame.h"
40 #include "core/frame/UseCounter.h"
41 #include "core/html/HTMLBRElement.h"
42 #include "core/html/shadow/ShadowElementNames.h"
43 #include "core/rendering/RenderBlock.h"
44 #include "core/rendering/RenderTheme.h"
45 #include "platform/heap/Handle.h"
46 #include "wtf/text/StringBuilder.h"
50 using namespace HTMLNames;
53 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& doc, HTMLFormElement* form)
54 : HTMLFormControlElementWithState(tagName, doc, form)
55 , m_lastChangeWasUserEdit(false)
56 , m_cachedSelectionStart(0)
57 , m_cachedSelectionEnd(0)
58 , m_cachedSelectionDirection(SelectionHasNoDirection)
62 HTMLTextFormControlElement::~HTMLTextFormControlElement()
66 Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode* insertionPoint)
68 HTMLFormControlElementWithState::insertedInto(insertionPoint);
69 if (!insertionPoint->inDocument())
71 String initialValue = value();
72 setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
76 void HTMLTextFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
78 if (supportsPlaceholder())
79 updatePlaceholderVisibility(false);
80 handleFocusEvent(oldFocusedElement, type);
81 HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, type);
84 void HTMLTextFormControlElement::dispatchBlurEvent(Element* newFocusedElement)
86 if (supportsPlaceholder())
87 updatePlaceholderVisibility(false);
89 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
92 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
94 if (event->type() == EventTypeNames::webkitEditableContentChanged && renderer() && renderer()->isTextControl()) {
95 m_lastChangeWasUserEdit = true;
100 HTMLFormControlElementWithState::defaultEventHandler(event);
103 void HTMLTextFormControlElement::forwardEvent(Event* event)
105 if (event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)
107 innerTextElement()->defaultEventHandler(event);
110 String HTMLTextFormControlElement::strippedPlaceholder() const
112 // According to the HTML5 specification, we need to remove CR and LF from
113 // the attribute value.
114 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
115 if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
116 return attributeValue;
118 StringBuilder stripped;
119 unsigned length = attributeValue.length();
120 stripped.reserveCapacity(length);
121 for (unsigned i = 0; i < length; ++i) {
122 UChar character = attributeValue[i];
123 if (character == newlineCharacter || character == carriageReturn)
125 stripped.append(character);
127 return stripped.toString();
130 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
132 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
134 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
135 return attributeValue.string().find(isNotLineBreak) == kNotFound;
138 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
140 return supportsPlaceholder()
142 && isEmptySuggestedValue()
143 && !isPlaceholderEmpty()
144 && (document().focusedElement() != this || (RenderTheme::theme().shouldShowPlaceholderWhenFocused()))
145 && (!renderer() || renderer()->style()->visibility() == VISIBLE);
148 HTMLElement* HTMLTextFormControlElement::placeholderElement() const
150 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::placeholder()));
153 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
155 if (!supportsPlaceholder())
157 if (!placeholderElement() || placeholderValueChanged)
158 updatePlaceholderText();
159 HTMLElement* placeholder = placeholderElement();
162 placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? CSSValueVisible : CSSValueHidden);
165 void HTMLTextFormControlElement::setSelectionStart(int start)
167 setSelectionRange(start, max(start, selectionEnd()), selectionDirection());
170 void HTMLTextFormControlElement::setSelectionEnd(int end)
172 setSelectionRange(min(end, selectionStart()), end, selectionDirection());
175 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
177 setSelectionRange(selectionStart(), selectionEnd(), direction);
180 void HTMLTextFormControlElement::select()
182 setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection);
185 bool HTMLTextFormControlElement::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
187 return !equalIgnoringNullity(oldValue, newValue);
190 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
192 String newValue = value();
193 if (shouldDispatchFormControlChangeEvent(m_textAsOfLastFormControlChangeEvent, newValue)) {
194 dispatchChangeEvent();
195 setTextAsOfLastFormControlChangeEvent(newValue);
197 setChangedSinceLastFormControlChangeEvent(false);
200 static inline bool hasVisibleTextArea(RenderObject* renderer, HTMLElement* innerText)
203 return renderer->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height();
207 void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionState& exceptionState)
209 setRangeText(replacement, selectionStart(), selectionEnd(), String(), exceptionState);
212 void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState)
215 exceptionState.throwDOMException(IndexSizeError, "The provided start value (" + String::number(start) + ") is larger than the provided end value (" + String::number(end) + ").");
218 if (hasAuthorShadowRoot())
221 String text = innerTextValue();
222 unsigned textLength = text.length();
223 unsigned replacementLength = replacement.length();
224 unsigned newSelectionStart = selectionStart();
225 unsigned newSelectionEnd = selectionEnd();
227 start = std::min(start, textLength);
228 end = std::min(end, textLength);
231 text.replace(start, end - start, replacement);
233 text.insert(replacement, start);
235 setInnerTextValue(text);
237 // FIXME: What should happen to the value (as in value()) if there's no renderer?
243 if (equalIgnoringCase(selectionMode, "select")) {
244 newSelectionStart = start;
245 newSelectionEnd = start + replacementLength;
246 } else if (equalIgnoringCase(selectionMode, "start"))
247 newSelectionStart = newSelectionEnd = start;
248 else if (equalIgnoringCase(selectionMode, "end"))
249 newSelectionStart = newSelectionEnd = start + replacementLength;
251 // Default is "preserve".
252 long delta = replacementLength - (end - start);
254 if (newSelectionStart > end)
255 newSelectionStart += delta;
256 else if (newSelectionStart > start)
257 newSelectionStart = start;
259 if (newSelectionEnd > end)
260 newSelectionEnd += delta;
261 else if (newSelectionEnd > start)
262 newSelectionEnd = start + replacementLength;
265 setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
268 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
270 TextFieldSelectionDirection direction = SelectionHasNoDirection;
271 if (directionString == "forward")
272 direction = SelectionHasForwardDirection;
273 else if (directionString == "backward")
274 direction = SelectionHasBackwardDirection;
276 return setSelectionRange(start, end, direction);
279 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
281 document().updateLayoutIgnorePendingStylesheets();
283 if (!renderer() || !renderer()->isTextControl())
287 start = min(max(start, 0), end);
289 if (!hasVisibleTextArea(renderer(), innerTextElement())) {
290 cacheSelection(start, end, direction);
293 VisiblePosition startPosition = visiblePositionForIndex(start);
294 VisiblePosition endPosition;
296 endPosition = startPosition;
298 endPosition = visiblePositionForIndex(end);
300 // startPosition and endPosition can be null position for example when
301 // "-webkit-user-select: none" style attribute is specified.
302 if (startPosition.isNotNull() && endPosition.isNotNull()) {
303 ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowHost() == this
304 && endPosition.deepEquivalent().deprecatedNode()->shadowHost() == this);
306 VisibleSelection newSelection;
307 if (direction == SelectionHasBackwardDirection)
308 newSelection = VisibleSelection(endPosition, startPosition);
310 newSelection = VisibleSelection(startPosition, endPosition);
311 newSelection.setIsDirectional(direction != SelectionHasNoDirection);
313 if (LocalFrame* frame = document().frame())
314 frame->selection().setSelection(newSelection);
317 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
320 return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
321 RefPtrWillBeRawPtr<Range> range = Range::create(document());
322 range->selectNodeContents(innerTextElement(), ASSERT_NO_EXCEPTION);
323 CharacterIterator it(range.get());
324 it.advance(index - 1);
325 return VisiblePosition(it.range()->endPosition(), UPSTREAM);
328 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
330 Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
331 if (enclosingTextFormControl(indexPosition) != this)
333 ASSERT(indexPosition.document());
334 RefPtrWillBeRawPtr<Range> range = Range::create(*indexPosition.document());
335 range->setStart(innerTextElement(), 0, ASSERT_NO_EXCEPTION);
336 range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ASSERT_NO_EXCEPTION);
337 return TextIterator::rangeLength(range.get());
340 int HTMLTextFormControlElement::selectionStart() const
342 if (!isTextFormControl())
344 if (document().focusedElement() != this)
345 return m_cachedSelectionStart;
347 return computeSelectionStart();
350 int HTMLTextFormControlElement::computeSelectionStart() const
352 ASSERT(isTextFormControl());
353 LocalFrame* frame = document().frame();
357 return indexForVisiblePosition(VisiblePosition(frame->selection().start()));
360 int HTMLTextFormControlElement::selectionEnd() const
362 if (!isTextFormControl())
364 if (document().focusedElement() != this)
365 return m_cachedSelectionEnd;
366 return computeSelectionEnd();
369 int HTMLTextFormControlElement::computeSelectionEnd() const
371 ASSERT(isTextFormControl());
372 LocalFrame* frame = document().frame();
376 return indexForVisiblePosition(VisiblePosition(frame->selection().end()));
379 static const AtomicString& directionString(TextFieldSelectionDirection direction)
381 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral));
382 DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward", AtomicString::ConstructFromLiteral));
383 DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward", AtomicString::ConstructFromLiteral));
386 case SelectionHasNoDirection:
388 case SelectionHasForwardDirection:
390 case SelectionHasBackwardDirection:
394 ASSERT_NOT_REACHED();
398 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
400 if (!isTextFormControl())
401 return directionString(SelectionHasNoDirection);
402 if (document().focusedElement() != this)
403 return directionString(m_cachedSelectionDirection);
405 return directionString(computeSelectionDirection());
408 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
410 ASSERT(isTextFormControl());
411 LocalFrame* frame = document().frame();
413 return SelectionHasNoDirection;
415 const VisibleSelection& selection = frame->selection().selection();
416 return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
419 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
421 if (node->isTextNode()) {
422 containerNode = node;
423 offsetInContainer = offset;
425 containerNode = node->parentNode();
426 offsetInContainer = node->nodeIndex() + offset;
430 PassRefPtrWillBeRawPtr<Range> HTMLTextFormControlElement::selection() const
432 if (!renderer() || !isTextFormControl())
435 int start = m_cachedSelectionStart;
436 int end = m_cachedSelectionEnd;
438 ASSERT(start <= end);
439 HTMLElement* innerText = innerTextElement();
443 if (!innerText->firstChild())
444 return Range::create(document(), innerText, 0, innerText, 0);
449 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
450 ASSERT(!node->firstChild());
451 ASSERT(node->isTextNode() || isHTMLBRElement(*node));
452 int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
454 if (offset <= start && start <= offset + length)
455 setContainerAndOffsetForRange(node, start - offset, startNode, start);
457 if (offset <= end && end <= offset + length) {
458 setContainerAndOffsetForRange(node, end - offset, endNode, end);
465 if (!startNode || !endNode)
468 return Range::create(document(), startNode, start, endNode, end);
471 void HTMLTextFormControlElement::restoreCachedSelection()
473 setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
476 void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
478 if (!renderer() || !isTextFormControl())
481 // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
482 cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
484 if (LocalFrame* frame = document().frame()) {
485 if (frame->selection().isRange() && userTriggered)
486 dispatchEvent(Event::createBubble(EventTypeNames::select));
490 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
492 if (name == placeholderAttr) {
493 updatePlaceholderVisibility(true);
494 UseCounter::count(document(), UseCounter::PlaceholderAttribute);
496 HTMLFormControlElementWithState::parseAttribute(name, value);
499 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
501 if (!isTextFormControl())
503 return m_lastChangeWasUserEdit;
506 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
508 ASSERT(!hasAuthorShadowRoot());
509 if (!isTextFormControl() || hasAuthorShadowRoot())
512 bool textIsChanged = value != innerTextValue();
513 if (textIsChanged || !innerTextElement()->hasChildren()) {
514 if (textIsChanged && renderer()) {
515 if (AXObjectCache* cache = document().existingAXObjectCache())
516 cache->postNotification(this, AXObjectCache::AXValueChanged, false);
518 innerTextElement()->setInnerText(value, ASSERT_NO_EXCEPTION);
520 if (value.endsWith('\n') || value.endsWith('\r'))
521 innerTextElement()->appendChild(HTMLBRElement::create(document()));
525 static String finishText(StringBuilder& result)
527 // Remove one trailing newline; there's always one that's collapsed out by rendering.
528 size_t size = result.length();
529 if (size && result[size - 1] == '\n')
530 result.resize(--size);
531 return result.toString();
534 String HTMLTextFormControlElement::innerTextValue() const
536 ASSERT(!hasAuthorShadowRoot());
537 HTMLElement* innerText = innerTextElement();
538 if (!innerText || !isTextFormControl())
539 return emptyString();
541 StringBuilder result;
542 for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
543 if (isHTMLBRElement(*node))
544 result.append(newlineCharacter);
545 else if (node->isTextNode())
546 result.append(toText(node)->data());
548 return finishText(result);
551 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
554 for (; line; line = next) {
555 next = line->nextRootBox();
556 if (next && !line->endsWithBreak()) {
557 ASSERT(line->lineBreakObj());
558 breakNode = line->lineBreakObj()->node();
559 breakOffset = line->lineBreakPos();
568 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
570 // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
571 // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
572 HTMLElement* innerText = innerTextElement();
573 if (!innerText || !isTextFormControl())
576 RenderBlock* renderer = toRenderBlock(innerText->renderer());
581 unsigned breakOffset;
582 RootInlineBox* line = renderer->firstRootBox();
586 getNextSoftBreak(line, breakNode, breakOffset);
588 StringBuilder result;
589 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
590 if (isHTMLBRElement(*node))
591 result.append(newlineCharacter);
592 else if (node->isTextNode()) {
593 String data = toText(node)->data();
594 unsigned length = data.length();
595 unsigned position = 0;
596 while (breakNode == node && breakOffset <= length) {
597 if (breakOffset > position) {
598 result.append(data, position, breakOffset - position);
599 position = breakOffset;
600 result.append(newlineCharacter);
602 getNextSoftBreak(line, breakNode, breakOffset);
604 result.append(data, position, length - position);
606 while (breakNode == node)
607 getNextSoftBreak(line, breakNode, breakOffset);
609 return finishText(result);
612 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
614 ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
615 || position.containerNode() || !position.anchorNode()->shadowHost()
616 || (position.anchorNode()->parentNode() && position.anchorNode()->parentNode()->isShadowRoot()));
618 Node* container = position.containerNode();
621 Element* ancestor = container->shadowHost();
622 return ancestor && isHTMLTextFormControlElement(*ancestor) && container->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? toHTMLTextFormControlElement(ancestor) : 0;
625 static const HTMLElement* parentHTMLElement(const Element* element)
628 element = element->parentElement();
629 if (element && element->isHTMLElement())
630 return toHTMLElement(element);
635 String HTMLTextFormControlElement::directionForFormData() const
637 for (const HTMLElement* element = this; element; element = parentHTMLElement(element)) {
638 const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr);
639 if (dirAttributeValue.isNull())
642 if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr"))
643 return dirAttributeValue;
645 if (equalIgnoringCase(dirAttributeValue, "auto")) {
647 TextDirection textDirection = element->directionalityIfhasDirAutoAttribute(isAuto);
648 return textDirection == RTL ? "rtl" : "ltr";
655 HTMLElement* HTMLTextFormControlElement::innerTextElement() const
657 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor()));
660 } // namespace Webcore