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"
30
#include "ChromeClient.h"
33
#include "EventNames.h"
35
#include "HTMLBRElement.h"
36
#include "HTMLFormElement.h"
37
#include "HTMLInputElement.h"
38
#include "HTMLNames.h"
39
#include "NodeRenderingContext.h"
40
#include "RenderBox.h"
41
#include "RenderTextControl.h"
42
#include "RenderTheme.h"
43
#include "ScriptEventListener.h"
45
#include "TextIterator.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(-1)
57
, m_cachedSelectionEnd(-1)
58
, m_cachedSelectionDirection(SelectionHasNoDirection)
62
HTMLTextFormControlElement::~HTMLTextFormControlElement()
66
bool HTMLTextFormControlElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
68
return childContext.isOnEncapsulationBoundary() && HTMLFormControlElementWithState::childShouldCreateRenderer(childContext);
71
Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode* insertionPoint)
73
HTMLFormControlElement::insertedInto(insertionPoint);
74
if (!insertionPoint->inDocument())
76
String initialValue = value();
77
setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
81
void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
83
if (supportsPlaceholder())
84
updatePlaceholderVisibility(false);
86
HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
89
void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
91
if (supportsPlaceholder())
92
updatePlaceholderVisibility(false);
94
HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
97
void HTMLTextFormControlElement::defaultEventHandler(Event* event)
99
if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
100
m_lastChangeWasUserEdit = true;
105
HTMLFormControlElementWithState::defaultEventHandler(event);
108
void HTMLTextFormControlElement::forwardEvent(Event* event)
110
if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
112
innerTextElement()->defaultEventHandler(event);
115
String HTMLTextFormControlElement::strippedPlaceholder() const
117
// According to the HTML5 specification, we need to remove CR and LF from
118
// the attribute value.
119
const AtomicString& attributeValue = getAttribute(placeholderAttr);
120
if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
121
return attributeValue;
123
StringBuilder stripped;
124
unsigned length = attributeValue.length();
125
stripped.reserveCapacity(length);
126
for (unsigned i = 0; i < length; ++i) {
127
UChar character = attributeValue[i];
128
if (character == newlineCharacter || character == carriageReturn)
130
stripped.append(character);
132
return stripped.toString();
135
static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
137
bool HTMLTextFormControlElement::isPlaceholderEmpty() const
139
const AtomicString& attributeValue = getAttribute(placeholderAttr);
140
return attributeValue.string().find(isNotLineBreak) == notFound;
143
bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
145
return supportsPlaceholder()
147
&& isEmptySuggestedValue()
148
&& !isPlaceholderEmpty()
149
&& (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()))
150
&& (!renderer() || renderer()->style()->visibility() == VISIBLE);
153
void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
155
if (!supportsPlaceholder())
157
if (!placeholderElement() || placeholderValueChanged)
158
updatePlaceholderText();
159
HTMLElement* placeholder = placeholderElement();
162
placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? "visible" : "hidden");
165
void HTMLTextFormControlElement::fixPlaceholderRenderer(HTMLElement* placeholder, HTMLElement* siblingElement)
167
// FIXME: We should change the order of DOM nodes. But it makes an assertion
168
// failure in editing code.
169
if (!placeholder || !placeholder->renderer())
171
RenderObject* placeholderRenderer = placeholder->renderer();
172
RenderObject* siblingRenderer = siblingElement->renderer();
173
ASSERT(siblingRenderer);
174
if (placeholderRenderer->nextSibling() == siblingRenderer)
176
RenderObject* parentRenderer = placeholderRenderer->parent();
177
ASSERT(parentRenderer == siblingRenderer->parent());
178
parentRenderer->removeChild(placeholderRenderer);
179
parentRenderer->addChild(placeholderRenderer, siblingRenderer);
182
RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
184
if (!isTextFormControl())
186
document()->updateLayoutIgnorePendingStylesheets();
187
return toRenderTextControl(renderer());
190
void HTMLTextFormControlElement::setSelectionStart(int start)
192
setSelectionRange(start, max(start, selectionEnd()), selectionDirection());
195
void HTMLTextFormControlElement::setSelectionEnd(int end)
197
setSelectionRange(min(end, selectionStart()), end, selectionDirection());
200
void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
202
setSelectionRange(selectionStart(), selectionEnd(), direction);
205
void HTMLTextFormControlElement::select()
207
setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection);
210
String HTMLTextFormControlElement::selectedText() const
212
if (!isTextFormControl())
214
return value().substring(selectionStart(), selectionEnd() - selectionStart());
217
void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
219
if (m_textAsOfLastFormControlChangeEvent != value()) {
220
HTMLElement::dispatchChangeEvent();
221
setTextAsOfLastFormControlChangeEvent(value());
223
setChangedSinceLastFormControlChangeEvent(false);
226
static inline bool hasVisibleTextArea(RenderTextControl* textControl, HTMLElement* innerText)
229
return textControl->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height();
233
void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionCode& ec)
235
setRangeText(replacement, selectionStart(), selectionEnd(), String(), ec);
238
void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionCode& ec)
245
String text = innerTextValue();
246
unsigned textLength = text.length();
247
unsigned replacementLength = replacement.length();
248
unsigned newSelectionStart = selectionStart();
249
unsigned newSelectionEnd = selectionEnd();
251
start = std::min(start, textLength);
252
end = std::min(end, textLength);
255
text.replace(start, end - start, replacement);
257
text.insert(replacement, start);
259
setInnerTextValue(text);
261
// FIXME: What should happen to the value (as in value()) if there's no renderer?
267
if (equalIgnoringCase(selectionMode, "select")) {
268
newSelectionStart = start;
269
newSelectionEnd = start + replacementLength;
270
} else if (equalIgnoringCase(selectionMode, "start"))
271
newSelectionStart = newSelectionEnd = start;
272
else if (equalIgnoringCase(selectionMode, "end"))
273
newSelectionStart = newSelectionEnd = start + replacementLength;
275
// Default is "preserve".
276
long delta = replacementLength - (end - start);
278
if (newSelectionStart > end)
279
newSelectionStart += delta;
280
else if (newSelectionStart > start)
281
newSelectionStart = start;
283
if (newSelectionEnd > end)
284
newSelectionEnd += delta;
285
else if (newSelectionEnd > start)
286
newSelectionEnd = start + replacementLength;
289
setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
292
void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
294
TextFieldSelectionDirection direction = SelectionHasNoDirection;
295
if (directionString == "forward")
296
direction = SelectionHasForwardDirection;
297
else if (directionString == "backward")
298
direction = SelectionHasBackwardDirection;
300
return setSelectionRange(start, end, direction);
303
void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
305
document()->updateLayoutIgnorePendingStylesheets();
307
if (!renderer() || !renderer()->isTextControl())
311
start = min(max(start, 0), end);
313
RenderTextControl* control = toRenderTextControl(renderer());
314
if (!hasVisibleTextArea(control, innerTextElement())) {
315
cacheSelection(start, end, direction);
318
VisiblePosition startPosition = control->visiblePositionForIndex(start);
319
VisiblePosition endPosition;
321
endPosition = startPosition;
323
endPosition = control->visiblePositionForIndex(end);
325
// startPosition and endPosition can be null position for example when
326
// "-webkit-user-select: none" style attribute is specified.
327
if (startPosition.isNotNull() && endPosition.isNotNull()) {
328
ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this
329
&& endPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this);
331
VisibleSelection newSelection;
332
if (direction == SelectionHasBackwardDirection)
333
newSelection = VisibleSelection(endPosition, startPosition);
335
newSelection = VisibleSelection(startPosition, endPosition);
336
newSelection.setIsDirectional(direction != SelectionHasNoDirection);
338
if (Frame* frame = document()->frame())
339
frame->selection()->setSelection(newSelection);
342
int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
344
Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
345
if (enclosingTextFormControl(indexPosition) != this)
347
ExceptionCode ec = 0;
348
RefPtr<Range> range = Range::create(indexPosition.document());
349
range->setStart(innerTextElement(), 0, ec);
351
range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ec);
353
return TextIterator::rangeLength(range.get());
356
int HTMLTextFormControlElement::selectionStart() const
358
if (!isTextFormControl())
360
if (document()->focusedNode() != this && hasCachedSelection())
361
return m_cachedSelectionStart;
363
return computeSelectionStart();
366
int HTMLTextFormControlElement::computeSelectionStart() const
368
ASSERT(isTextFormControl());
369
Frame* frame = document()->frame();
373
return indexForVisiblePosition(frame->selection()->start());
376
int HTMLTextFormControlElement::selectionEnd() const
378
if (!isTextFormControl())
380
if (document()->focusedNode() != this && hasCachedSelection())
381
return m_cachedSelectionEnd;
382
return computeSelectionEnd();
385
int HTMLTextFormControlElement::computeSelectionEnd() const
387
ASSERT(isTextFormControl());
388
Frame* frame = document()->frame();
392
return indexForVisiblePosition(frame->selection()->end());
395
static const AtomicString& directionString(TextFieldSelectionDirection direction)
397
DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral));
398
DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward", AtomicString::ConstructFromLiteral));
399
DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward", AtomicString::ConstructFromLiteral));
402
case SelectionHasNoDirection:
404
case SelectionHasForwardDirection:
406
case SelectionHasBackwardDirection:
410
ASSERT_NOT_REACHED();
414
const AtomicString& HTMLTextFormControlElement::selectionDirection() const
416
if (!isTextFormControl())
417
return directionString(SelectionHasNoDirection);
418
if (document()->focusedNode() != this && hasCachedSelection())
419
return directionString(m_cachedSelectionDirection);
421
return directionString(computeSelectionDirection());
424
TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
426
ASSERT(isTextFormControl());
427
Frame* frame = document()->frame();
429
return SelectionHasNoDirection;
431
const VisibleSelection& selection = frame->selection()->selection();
432
return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
435
static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
437
if (node->isTextNode()) {
438
containerNode = node;
439
offsetInContainer = offset;
441
containerNode = node->parentNode();
442
offsetInContainer = node->nodeIndex() + offset;
446
PassRefPtr<Range> HTMLTextFormControlElement::selection() const
448
if (!renderer() || !isTextFormControl() || !hasCachedSelection())
451
int start = m_cachedSelectionStart;
452
int end = m_cachedSelectionEnd;
454
ASSERT(start <= end);
455
HTMLElement* innerText = innerTextElement();
459
if (!innerText->firstChild())
460
return Range::create(document(), innerText, 0, innerText, 0);
465
for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) {
466
ASSERT(!node->firstChild());
467
ASSERT(node->isTextNode() || node->hasTagName(brTag));
468
int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
470
if (offset <= start && start <= offset + length)
471
setContainerAndOffsetForRange(node, start - offset, startNode, start);
473
if (offset <= end && end <= offset + length) {
474
setContainerAndOffsetForRange(node, end - offset, endNode, end);
481
if (!startNode || !endNode)
484
return Range::create(document(), startNode, start, endNode, end);
487
void HTMLTextFormControlElement::restoreCachedSelection()
489
setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
492
void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
494
if (!renderer() || !isTextFormControl())
497
// selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
498
cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
500
if (Frame* frame = document()->frame()) {
501
if (frame->selection()->isRange() && userTriggered)
502
dispatchEvent(Event::create(eventNames().selectEvent, true, false));
506
void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
508
if (name == placeholderAttr)
509
updatePlaceholderVisibility(true);
510
else if (name == onselectAttr)
511
setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, name, value));
512
else if (name == onchangeAttr)
513
setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, name, value));
515
HTMLFormControlElementWithState::parseAttribute(name, value);
518
bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
520
if (!isTextFormControl())
522
return m_lastChangeWasUserEdit;
525
void HTMLTextFormControlElement::setInnerTextValue(const String& value)
527
if (!isTextFormControl())
530
bool textIsChanged = value != innerTextValue();
531
if (textIsChanged || !innerTextElement()->hasChildNodes()) {
532
if (textIsChanged && document() && renderer() && AXObjectCache::accessibilityEnabled())
533
document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, false);
535
ExceptionCode ec = 0;
536
innerTextElement()->setInnerText(value, ec);
539
if (value.endsWith('\n') || value.endsWith('\r')) {
540
innerTextElement()->appendChild(HTMLBRElement::create(document()), ec);
545
setFormControlValueMatchesRenderer(true);
548
static String finishText(StringBuilder& result)
550
// Remove one trailing newline; there's always one that's collapsed out by rendering.
551
size_t size = result.length();
552
if (size && result[size - 1] == '\n')
553
result.resize(--size);
554
return result.toString();
557
String HTMLTextFormControlElement::innerTextValue() const
559
HTMLElement* innerText = innerTextElement();
560
if (!innerText || !isTextFormControl())
561
return emptyString();
563
StringBuilder result;
564
for (Node* node = innerText; node; node = node->traverseNextNode(innerText)) {
565
if (node->hasTagName(brTag))
566
result.append(newlineCharacter);
567
else if (node->isTextNode())
568
result.append(toText(node)->data());
570
return finishText(result);
573
static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
576
for (; line; line = next) {
577
next = line->nextRootBox();
578
if (next && !line->endsWithBreak()) {
579
ASSERT(line->lineBreakObj());
580
breakNode = line->lineBreakObj()->node();
581
breakOffset = line->lineBreakPos();
590
String HTMLTextFormControlElement::valueWithHardLineBreaks() const
592
// FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
593
// While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
594
HTMLElement* innerText = innerTextElement();
595
if (!innerText || !isTextFormControl())
598
RenderBlock* renderer = toRenderBlock(innerText->renderer());
603
unsigned breakOffset;
604
RootInlineBox* line = renderer->firstRootBox();
608
getNextSoftBreak(line, breakNode, breakOffset);
610
StringBuilder result;
611
for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) {
612
if (node->hasTagName(brTag))
613
result.append(newlineCharacter);
614
else if (node->isTextNode()) {
615
String data = toText(node)->data();
616
unsigned length = data.length();
617
unsigned position = 0;
618
while (breakNode == node && breakOffset <= length) {
619
if (breakOffset > position) {
620
result.append(data.characters() + position, breakOffset - position);
621
position = breakOffset;
622
result.append(newlineCharacter);
624
getNextSoftBreak(line, breakNode, breakOffset);
626
result.append(data.characters() + position, length - position);
628
while (breakNode == node)
629
getNextSoftBreak(line, breakNode, breakOffset);
631
return finishText(result);
634
HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
636
ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
637
|| position.containerNode() || !position.anchorNode()->shadowAncestorNode());
638
Node* container = position.containerNode();
641
Node* ancestor = container->shadowAncestorNode();
642
return ancestor != container ? toTextFormControl(ancestor) : 0;
645
static const Element* parentHTMLElement(const Element* element)
648
element = element->parentElement();
649
if (element && element->isHTMLElement())
655
String HTMLTextFormControlElement::directionForFormData() const
657
for (const Element* element = this; element; element = parentHTMLElement(element)) {
658
const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr);
659
if (dirAttributeValue.isNull())
662
if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr"))
663
return dirAttributeValue;
665
if (equalIgnoringCase(dirAttributeValue, "auto")) {
667
TextDirection textDirection = static_cast<const HTMLElement*>(element)->directionalityIfhasDirAutoAttribute(isAuto);
668
return textDirection == RTL ? "rtl" : "ltr";
675
void HTMLTextFormControlElement::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
677
MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
678
HTMLFormControlElementWithState::reportMemoryUsage(memoryObjectInfo);
679
info.addMember(m_textAsOfLastFormControlChangeEvent);
682
} // namespace Webcore