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 "core/html/HTMLTextFormControlElement.h"
27
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/HTMLNames.h"
31 #include "core/accessibility/AXObjectCache.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/NodeList.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/Text.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/editing/Editor.h"
38 #include "core/editing/FrameSelection.h"
39 #include "core/editing/TextIterator.h"
40 #include "core/editing/htmlediting.h"
41 #include "core/events/Event.h"
42 #include "core/frame/LocalFrame.h"
43 #include "core/frame/UseCounter.h"
44 #include "core/html/HTMLBRElement.h"
45 #include "core/html/shadow/ShadowElementNames.h"
46 #include "core/page/FocusController.h"
47 #include "core/page/Page.h"
48 #include "core/rendering/RenderBlock.h"
49 #include "core/rendering/RenderBlockFlow.h"
50 #include "core/rendering/RenderTheme.h"
51 #include "platform/heap/Handle.h"
52 #include "platform/text/TextBoundaries.h"
53 #include "wtf/text/StringBuilder.h"
54
55 namespace blink {
56
57 using namespace HTMLNames;
58
HTMLTextFormControlElement(const QualifiedName & tagName,Document & doc,HTMLFormElement * form)59 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& doc, HTMLFormElement* form)
60 : HTMLFormControlElementWithState(tagName, doc, form)
61 , m_lastChangeWasUserEdit(false)
62 , m_cachedSelectionStart(0)
63 , m_cachedSelectionEnd(0)
64 , m_cachedSelectionDirection(SelectionHasNoDirection)
65 {
66 }
67
~HTMLTextFormControlElement()68 HTMLTextFormControlElement::~HTMLTextFormControlElement()
69 {
70 }
71
insertedInto(ContainerNode * insertionPoint)72 Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode* insertionPoint)
73 {
74 HTMLFormControlElementWithState::insertedInto(insertionPoint);
75 if (!insertionPoint->inDocument())
76 return InsertionDone;
77 String initialValue = value();
78 setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
79 return InsertionDone;
80 }
81
dispatchFocusEvent(Element * oldFocusedElement,FocusType type)82 void HTMLTextFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
83 {
84 if (supportsPlaceholder())
85 updatePlaceholderVisibility(false);
86 handleFocusEvent(oldFocusedElement, type);
87 HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, type);
88 }
89
dispatchBlurEvent(Element * newFocusedElement)90 void HTMLTextFormControlElement::dispatchBlurEvent(Element* newFocusedElement)
91 {
92 if (supportsPlaceholder())
93 updatePlaceholderVisibility(false);
94 handleBlurEvent();
95 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
96 }
97
defaultEventHandler(Event * event)98 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
99 {
100 if (event->type() == EventTypeNames::webkitEditableContentChanged && renderer() && renderer()->isTextControl()) {
101 m_lastChangeWasUserEdit = true;
102 subtreeHasChanged();
103 return;
104 }
105
106 HTMLFormControlElementWithState::defaultEventHandler(event);
107 }
108
forwardEvent(Event * event)109 void HTMLTextFormControlElement::forwardEvent(Event* event)
110 {
111 if (event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)
112 return;
113 innerEditorElement()->defaultEventHandler(event);
114 }
115
strippedPlaceholder() const116 String HTMLTextFormControlElement::strippedPlaceholder() const
117 {
118 // According to the HTML5 specification, we need to remove CR and LF from
119 // the attribute value.
120 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
121 if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
122 return attributeValue;
123
124 StringBuilder stripped;
125 unsigned length = attributeValue.length();
126 stripped.reserveCapacity(length);
127 for (unsigned i = 0; i < length; ++i) {
128 UChar character = attributeValue[i];
129 if (character == newlineCharacter || character == carriageReturn)
130 continue;
131 stripped.append(character);
132 }
133 return stripped.toString();
134 }
135
isNotLineBreak(UChar ch)136 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
137
isPlaceholderEmpty() const138 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
139 {
140 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
141 return attributeValue.string().find(isNotLineBreak) == kNotFound;
142 }
143
placeholderShouldBeVisible() const144 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
145 {
146 return supportsPlaceholder()
147 && isEmptyValue()
148 && isEmptySuggestedValue()
149 && !isPlaceholderEmpty()
150 && (document().focusedElement() != this || (RenderTheme::theme().shouldShowPlaceholderWhenFocused()))
151 && (!renderer() || renderer()->style()->visibility() == VISIBLE);
152 }
153
placeholderElement() const154 HTMLElement* HTMLTextFormControlElement::placeholderElement() const
155 {
156 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::placeholder()));
157 }
158
updatePlaceholderVisibility(bool placeholderValueChanged)159 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
160 {
161 if (!supportsPlaceholder())
162 return;
163 if (!placeholderElement() || placeholderValueChanged)
164 updatePlaceholderText();
165 HTMLElement* placeholder = placeholderElement();
166 if (!placeholder)
167 return;
168 placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? CSSValueVisible : CSSValueHidden);
169 }
170
setSelectionStart(int start)171 void HTMLTextFormControlElement::setSelectionStart(int start)
172 {
173 setSelectionRange(start, std::max(start, selectionEnd()), selectionDirection());
174 }
175
setSelectionEnd(int end)176 void HTMLTextFormControlElement::setSelectionEnd(int end)
177 {
178 setSelectionRange(std::min(end, selectionStart()), end, selectionDirection());
179 }
180
setSelectionDirection(const String & direction)181 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
182 {
183 setSelectionRange(selectionStart(), selectionEnd(), direction);
184 }
185
select()186 void HTMLTextFormControlElement::select()
187 {
188 document().updateLayoutIgnorePendingStylesheets();
189 setSelectionRange(0, std::numeric_limits<int>::max(), SelectionHasNoDirection, isFocusable() ? ChangeSelectionAndFocus : NotChangeSelection);
190 }
191
shouldDispatchFormControlChangeEvent(String & oldValue,String & newValue)192 bool HTMLTextFormControlElement::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
193 {
194 return !equalIgnoringNullity(oldValue, newValue);
195 }
196
dispatchFormControlChangeEvent()197 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
198 {
199 String newValue = value();
200 if (shouldDispatchFormControlChangeEvent(m_textAsOfLastFormControlChangeEvent, newValue)) {
201 setTextAsOfLastFormControlChangeEvent(newValue);
202 dispatchChangeEvent();
203 }
204 setChangedSinceLastFormControlChangeEvent(false);
205 }
206
setRangeText(const String & replacement,ExceptionState & exceptionState)207 void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionState& exceptionState)
208 {
209 setRangeText(replacement, selectionStart(), selectionEnd(), String(), exceptionState);
210 }
211
setRangeText(const String & replacement,unsigned start,unsigned end,const String & selectionMode,ExceptionState & exceptionState)212 void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState)
213 {
214 if (start > end) {
215 exceptionState.throwDOMException(IndexSizeError, "The provided start value (" + String::number(start) + ") is larger than the provided end value (" + String::number(end) + ").");
216 return;
217 }
218 if (hasAuthorShadowRoot())
219 return;
220
221 String text = innerEditorValue();
222 unsigned textLength = text.length();
223 unsigned replacementLength = replacement.length();
224 unsigned newSelectionStart = selectionStart();
225 unsigned newSelectionEnd = selectionEnd();
226
227 start = std::min(start, textLength);
228 end = std::min(end, textLength);
229
230 if (start < end)
231 text.replace(start, end - start, replacement);
232 else
233 text.insert(replacement, start);
234
235 setInnerEditorValue(text);
236
237 // FIXME: What should happen to the value (as in value()) if there's no renderer?
238 if (!renderer())
239 return;
240
241 subtreeHasChanged();
242
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;
250 else {
251 // Default is "preserve".
252 long delta = replacementLength - (end - start);
253
254 if (newSelectionStart > end)
255 newSelectionStart += delta;
256 else if (newSelectionStart > start)
257 newSelectionStart = start;
258
259 if (newSelectionEnd > end)
260 newSelectionEnd += delta;
261 else if (newSelectionEnd > start)
262 newSelectionEnd = start + replacementLength;
263 }
264
265 setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
266 }
267
setSelectionRange(int start,int end,const String & directionString)268 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
269 {
270 TextFieldSelectionDirection direction = SelectionHasNoDirection;
271 if (directionString == "forward")
272 direction = SelectionHasForwardDirection;
273 else if (directionString == "backward")
274 direction = SelectionHasBackwardDirection;
275
276 if (direction == SelectionHasNoDirection && document().frame() && document().frame()->editor().behavior().shouldConsiderSelectionAsDirectional())
277 direction = SelectionHasForwardDirection;
278
279 return setSelectionRange(start, end, direction);
280 }
281
positionForIndex(HTMLElement * innerEditor,int index)282 static Position positionForIndex(HTMLElement* innerEditor, int index)
283 {
284 ASSERT(index >= 0);
285 if (index == 0) {
286 Node* node = NodeTraversal::next(*innerEditor, innerEditor);
287 if (node && node->isTextNode())
288 return Position(node, 0, Position::PositionIsOffsetInAnchor);
289 return Position(innerEditor, 0, Position::PositionIsOffsetInAnchor);
290 }
291 int remainingCharactersToMoveForward = index;
292 Node* lastBrOrText = innerEditor;
293 for (Node* node = NodeTraversal::next(*innerEditor, innerEditor); node; node = NodeTraversal::next(*node, innerEditor)) {
294 ASSERT(remainingCharactersToMoveForward >= 0);
295 if (node->hasTagName(brTag)) {
296 if (remainingCharactersToMoveForward == 0)
297 return positionBeforeNode(node);
298 --remainingCharactersToMoveForward;
299 lastBrOrText = node;
300 continue;
301 }
302
303 if (node->isTextNode()) {
304 Text& text = toText(*node);
305 if (remainingCharactersToMoveForward < static_cast<int>(text.length()))
306 return Position(&text, remainingCharactersToMoveForward);
307 remainingCharactersToMoveForward -= text.length();
308 lastBrOrText = node;
309 continue;
310 }
311
312 ASSERT_NOT_REACHED();
313 }
314 return lastPositionInOrAfterNode(lastBrOrText);
315 }
316
indexForPosition(HTMLElement * innerEditor,const Position & passedPosition)317 static int indexForPosition(HTMLElement* innerEditor, const Position& passedPosition)
318 {
319 if (!innerEditor || !innerEditor->contains(passedPosition.anchorNode()) || passedPosition.isNull())
320 return 0;
321
322 if (positionBeforeNode(innerEditor) == passedPosition)
323 return 0;
324
325 int index = 0;
326 Node* startNode = passedPosition.computeNodeBeforePosition();
327 if (!startNode)
328 startNode = passedPosition.containerNode();
329 ASSERT(startNode);
330 ASSERT(innerEditor->contains(startNode));
331
332 for (Node* node = startNode; node; node = NodeTraversal::previous(*node, innerEditor)) {
333 if (node->isTextNode()) {
334 int length = toText(*node).length();
335 if (node == passedPosition.containerNode())
336 index += std::min(length, passedPosition.offsetInContainerNode());
337 else
338 index += length;
339 } else if (node->hasTagName(brTag)) {
340 ++index;
341 }
342 }
343
344 ASSERT(index >= 0);
345 return index;
346 }
347
setSelectionRange(int start,int end,TextFieldSelectionDirection direction,SelectionOption selectionOption)348 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, SelectionOption selectionOption)
349 {
350 if (hasAuthorShadowRoot() || !isTextFormControl())
351 return;
352
353 const int editorValueLength = static_cast<int>(innerEditorValue().length());
354 ASSERT(editorValueLength >= 0);
355 end = std::max(std::min(end, editorValueLength), 0);
356 start = std::min(std::max(start, 0), end);
357 cacheSelection(start, end, direction);
358
359 if (selectionOption == NotChangeSelection || (selectionOption == ChangeSelectionIfFocused && document().focusedElement() != this))
360 return;
361
362 LocalFrame* frame = document().frame();
363 HTMLElement* innerEditor = innerEditorElement();
364 if (!frame || !innerEditor)
365 return;
366
367 Position startPosition = positionForIndex(innerEditor, start);
368 Position endPosition = start == end ? startPosition : positionForIndex(innerEditor, end);
369
370 ASSERT(start == indexForPosition(innerEditor, startPosition));
371 ASSERT(end == indexForPosition(innerEditor, endPosition));
372
373 // startPosition and endPosition can be null position for example when
374 // "-webkit-user-select: none" style attribute is specified.
375 if (startPosition.isNotNull() && endPosition.isNotNull()) {
376 ASSERT(startPosition.anchorNode()->shadowHost() == this
377 && endPosition.anchorNode()->shadowHost() == this);
378 }
379 VisibleSelection newSelection;
380 if (direction == SelectionHasBackwardDirection)
381 newSelection.setWithoutValidation(endPosition, startPosition);
382 else
383 newSelection.setWithoutValidation(startPosition, endPosition);
384 newSelection.setIsDirectional(direction != SelectionHasNoDirection);
385
386 frame->selection().setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | (selectionOption == ChangeSelectionAndFocus ? 0 : FrameSelection::DoNotSetFocus));
387 }
388
visiblePositionForIndex(int index) const389 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
390 {
391 if (index <= 0)
392 return VisiblePosition(firstPositionInNode(innerEditorElement()), DOWNSTREAM);
393 Position start, end;
394 bool selected = Range::selectNodeContents(innerEditorElement(), start, end);
395 if (!selected)
396 return VisiblePosition();
397 CharacterIterator it(start, end);
398 it.advance(index - 1);
399 return VisiblePosition(it.endPosition(), UPSTREAM);
400 }
401
indexForVisiblePosition(const VisiblePosition & pos) const402 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
403 {
404 Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
405 if (enclosingTextFormControl(indexPosition) != this)
406 return 0;
407 ASSERT(indexPosition.document());
408 RefPtrWillBeRawPtr<Range> range = Range::create(*indexPosition.document());
409 range->setStart(innerEditorElement(), 0, ASSERT_NO_EXCEPTION);
410 range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ASSERT_NO_EXCEPTION);
411 return TextIterator::rangeLength(range.get());
412 }
413
selectionStart() const414 int HTMLTextFormControlElement::selectionStart() const
415 {
416 if (!isTextFormControl())
417 return 0;
418 if (document().focusedElement() != this)
419 return m_cachedSelectionStart;
420
421 return computeSelectionStart();
422 }
423
computeSelectionStart() const424 int HTMLTextFormControlElement::computeSelectionStart() const
425 {
426 ASSERT(isTextFormControl());
427 LocalFrame* frame = document().frame();
428 if (!frame)
429 return 0;
430
431 return indexForPosition(innerEditorElement(), frame->selection().start());
432 }
433
selectionEnd() const434 int HTMLTextFormControlElement::selectionEnd() const
435 {
436 if (!isTextFormControl())
437 return 0;
438 if (document().focusedElement() != this)
439 return m_cachedSelectionEnd;
440 return computeSelectionEnd();
441 }
442
computeSelectionEnd() const443 int HTMLTextFormControlElement::computeSelectionEnd() const
444 {
445 ASSERT(isTextFormControl());
446 LocalFrame* frame = document().frame();
447 if (!frame)
448 return 0;
449
450 return indexForPosition(innerEditorElement(), frame->selection().end());
451 }
452
directionString(TextFieldSelectionDirection direction)453 static const AtomicString& directionString(TextFieldSelectionDirection direction)
454 {
455 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral));
456 DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward", AtomicString::ConstructFromLiteral));
457 DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward", AtomicString::ConstructFromLiteral));
458
459 switch (direction) {
460 case SelectionHasNoDirection:
461 return none;
462 case SelectionHasForwardDirection:
463 return forward;
464 case SelectionHasBackwardDirection:
465 return backward;
466 }
467
468 ASSERT_NOT_REACHED();
469 return none;
470 }
471
selectionDirection() const472 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
473 {
474 if (!isTextFormControl())
475 return directionString(SelectionHasNoDirection);
476 if (document().focusedElement() != this)
477 return directionString(m_cachedSelectionDirection);
478
479 return directionString(computeSelectionDirection());
480 }
481
computeSelectionDirection() const482 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
483 {
484 ASSERT(isTextFormControl());
485 LocalFrame* frame = document().frame();
486 if (!frame)
487 return SelectionHasNoDirection;
488
489 const VisibleSelection& selection = frame->selection().selection();
490 return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
491 }
492
setContainerAndOffsetForRange(Node * node,int offset,Node * & containerNode,int & offsetInContainer)493 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
494 {
495 if (node->isTextNode()) {
496 containerNode = node;
497 offsetInContainer = offset;
498 } else {
499 containerNode = node->parentNode();
500 offsetInContainer = node->nodeIndex() + offset;
501 }
502 }
503
selection() const504 PassRefPtrWillBeRawPtr<Range> HTMLTextFormControlElement::selection() const
505 {
506 if (!renderer() || !isTextFormControl())
507 return nullptr;
508
509 int start = m_cachedSelectionStart;
510 int end = m_cachedSelectionEnd;
511
512 ASSERT(start <= end);
513 HTMLElement* innerText = innerEditorElement();
514 if (!innerText)
515 return nullptr;
516
517 if (!innerText->hasChildren())
518 return Range::create(document(), innerText, 0, innerText, 0);
519
520 int offset = 0;
521 Node* startNode = 0;
522 Node* endNode = 0;
523 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
524 ASSERT(!node->hasChildren());
525 ASSERT(node->isTextNode() || isHTMLBRElement(*node));
526 int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
527
528 if (offset <= start && start <= offset + length)
529 setContainerAndOffsetForRange(node, start - offset, startNode, start);
530
531 if (offset <= end && end <= offset + length) {
532 setContainerAndOffsetForRange(node, end - offset, endNode, end);
533 break;
534 }
535
536 offset += length;
537 }
538
539 if (!startNode || !endNode)
540 return nullptr;
541
542 return Range::create(document(), startNode, start, endNode, end);
543 }
544
restoreCachedSelection()545 void HTMLTextFormControlElement::restoreCachedSelection()
546 {
547 setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
548 }
549
selectionChanged(bool userTriggered)550 void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
551 {
552 if (!renderer() || !isTextFormControl())
553 return;
554
555 // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
556 cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
557
558 if (LocalFrame* frame = document().frame()) {
559 if (frame->selection().isRange() && userTriggered)
560 dispatchEvent(Event::createBubble(EventTypeNames::select));
561 }
562 }
563
parseAttribute(const QualifiedName & name,const AtomicString & value)564 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
565 {
566 if (name == placeholderAttr) {
567 updatePlaceholderVisibility(true);
568 UseCounter::count(document(), UseCounter::PlaceholderAttribute);
569 } else
570 HTMLFormControlElementWithState::parseAttribute(name, value);
571 }
572
lastChangeWasUserEdit() const573 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
574 {
575 if (!isTextFormControl())
576 return false;
577 return m_lastChangeWasUserEdit;
578 }
579
setInnerEditorValue(const String & value)580 void HTMLTextFormControlElement::setInnerEditorValue(const String& value)
581 {
582 ASSERT(!hasAuthorShadowRoot());
583 if (!isTextFormControl() || hasAuthorShadowRoot())
584 return;
585
586 bool textIsChanged = value != innerEditorValue();
587 if (textIsChanged || !innerEditorElement()->hasChildren()) {
588 if (textIsChanged && renderer()) {
589 if (AXObjectCache* cache = document().existingAXObjectCache())
590 cache->postNotification(this, AXObjectCache::AXValueChanged, false);
591 }
592 innerEditorElement()->setInnerText(value, ASSERT_NO_EXCEPTION);
593
594 if (value.endsWith('\n') || value.endsWith('\r'))
595 innerEditorElement()->appendChild(HTMLBRElement::create(document()));
596 }
597 }
598
finishText(StringBuilder & result)599 static String finishText(StringBuilder& result)
600 {
601 // Remove one trailing newline; there's always one that's collapsed out by rendering.
602 size_t size = result.length();
603 if (size && result[size - 1] == '\n')
604 result.resize(--size);
605 return result.toString();
606 }
607
innerEditorValue() const608 String HTMLTextFormControlElement::innerEditorValue() const
609 {
610 ASSERT(!hasAuthorShadowRoot());
611 HTMLElement* innerEditor = innerEditorElement();
612 if (!innerEditor || !isTextFormControl())
613 return emptyString();
614
615 StringBuilder result;
616 for (Node* node = innerEditor; node; node = NodeTraversal::next(*node, innerEditor)) {
617 if (isHTMLBRElement(*node))
618 result.append(newlineCharacter);
619 else if (node->isTextNode())
620 result.append(toText(node)->data());
621 }
622 return finishText(result);
623 }
624
getNextSoftBreak(RootInlineBox * & line,Node * & breakNode,unsigned & breakOffset)625 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
626 {
627 RootInlineBox* next;
628 for (; line; line = next) {
629 next = line->nextRootBox();
630 if (next && !line->endsWithBreak()) {
631 ASSERT(line->lineBreakObj());
632 breakNode = line->lineBreakObj()->node();
633 breakOffset = line->lineBreakPos();
634 line = next;
635 return;
636 }
637 }
638 breakNode = 0;
639 breakOffset = 0;
640 }
641
valueWithHardLineBreaks() const642 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
643 {
644 // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
645 // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
646 HTMLElement* innerText = innerEditorElement();
647 if (!innerText || !isTextFormControl())
648 return value();
649
650 RenderBlockFlow* renderer = toRenderBlockFlow(innerText->renderer());
651 if (!renderer)
652 return value();
653
654 Node* breakNode;
655 unsigned breakOffset;
656 RootInlineBox* line = renderer->firstRootBox();
657 if (!line)
658 return value();
659
660 getNextSoftBreak(line, breakNode, breakOffset);
661
662 StringBuilder result;
663 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
664 if (isHTMLBRElement(*node))
665 result.append(newlineCharacter);
666 else if (node->isTextNode()) {
667 String data = toText(node)->data();
668 unsigned length = data.length();
669 unsigned position = 0;
670 while (breakNode == node && breakOffset <= length) {
671 if (breakOffset > position) {
672 result.append(data, position, breakOffset - position);
673 position = breakOffset;
674 result.append(newlineCharacter);
675 }
676 getNextSoftBreak(line, breakNode, breakOffset);
677 }
678 result.append(data, position, length - position);
679 }
680 while (breakNode == node)
681 getNextSoftBreak(line, breakNode, breakOffset);
682 }
683 return finishText(result);
684 }
685
enclosingTextFormControl(const Position & position)686 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
687 {
688 ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
689 || position.containerNode() || !position.anchorNode()->shadowHost()
690 || (position.anchorNode()->parentNode() && position.anchorNode()->parentNode()->isShadowRoot()));
691 return enclosingTextFormControl(position.containerNode());
692 }
693
enclosingTextFormControl(Node * container)694 HTMLTextFormControlElement* enclosingTextFormControl(Node* container)
695 {
696 if (!container)
697 return 0;
698 Element* ancestor = container->shadowHost();
699 return ancestor && isHTMLTextFormControlElement(*ancestor) && container->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? toHTMLTextFormControlElement(ancestor) : 0;
700 }
701
directionForFormData() const702 String HTMLTextFormControlElement::directionForFormData() const
703 {
704 for (const HTMLElement* element = this; element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
705 const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr);
706 if (dirAttributeValue.isNull())
707 continue;
708
709 if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr"))
710 return dirAttributeValue;
711
712 if (equalIgnoringCase(dirAttributeValue, "auto")) {
713 bool isAuto;
714 TextDirection textDirection = element->directionalityIfhasDirAutoAttribute(isAuto);
715 return textDirection == RTL ? "rtl" : "ltr";
716 }
717 }
718
719 return "ltr";
720 }
721
innerEditorElement() const722 HTMLElement* HTMLTextFormControlElement::innerEditorElement() const
723 {
724 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor()));
725 }
726
innerNodePosition(const Position & innerPosition)727 static Position innerNodePosition(const Position& innerPosition)
728 {
729 ASSERT(innerPosition.anchorType() != Position::PositionIsBeforeAnchor);
730 ASSERT(innerPosition.anchorType() != Position::PositionIsAfterAnchor);
731 HTMLElement* element = toHTMLElement(innerPosition.anchorNode());
732 ASSERT(element);
733 RefPtrWillBeRawPtr<NodeList> childNodes = element->childNodes();
734 if (!childNodes->length())
735 return Position(element, 0, Position::PositionIsOffsetInAnchor);
736
737 unsigned offset = 0;
738
739 switch (innerPosition.anchorType()) {
740 case Position::PositionIsOffsetInAnchor:
741 offset = std::max(0, std::min(innerPosition.offsetInContainerNode(), static_cast<int>(childNodes->length())));
742 break;
743 case Position::PositionIsAfterChildren:
744 offset = childNodes->length();
745 break;
746 default:
747 break;
748 }
749
750 if (offset == childNodes->length())
751 return Position(element->lastChild(), Position::PositionIsAfterAnchor);
752
753 Node* node = childNodes->item(offset);
754 if (node->isTextNode())
755 return Position(toText(node), 0);
756
757 return Position(node, Position::PositionIsBeforeAnchor);
758 }
759
760 enum FindOption {
761 FindStart,
762 FindEnd
763 };
764
findWordBoundary(const HTMLElement * innerEditor,const Position & startPosition,const Position & endPosition,FindOption findOption)765 static Position findWordBoundary(const HTMLElement* innerEditor, const Position& startPosition, const Position& endPosition, FindOption findOption)
766 {
767 StringBuilder concatTexts;
768 Vector<unsigned> lengthList;
769 Vector<Text*> textList;
770
771 if (startPosition.anchorNode()->isTextNode())
772 ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor);
773 if (endPosition.anchorNode()->isTextNode())
774 ASSERT(endPosition.anchorType() == Position::PositionIsOffsetInAnchor);
775
776 // Traverse text nodes.
777 for (Node* node = startPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) {
778 bool isStartNode = node == startPosition.anchorNode();
779 bool isEndNode = node == endPosition.anchorNode();
780 if (node->isTextNode()) {
781 Text* text = toText(node);
782 const unsigned start = isStartNode ? startPosition.offsetInContainerNode() : 0;
783 const unsigned end = isEndNode ? endPosition.offsetInContainerNode() : text->data().length();
784 const unsigned length = end - start;
785
786 concatTexts.append(text->data(), start, length);
787 lengthList.append(length);
788 textList.append(text);
789 }
790
791 if (isEndNode)
792 break;
793 }
794
795 if (concatTexts.length() == 0)
796 return startPosition;
797
798 int start, end;
799 if (findOption == FindEnd && concatTexts[0] == '\n') {
800 // findWordBoundary("\ntext", 0, &start, &end) assigns 1 to |end| but
801 // we expect 0 at the case.
802 start = 0;
803 end = 0;
804 } else {
805 Vector<UChar> characters;
806 concatTexts.toString().appendTo(characters);
807 findWordBoundary(characters.data(), characters.size(), findOption == FindStart ? characters.size() : 0, &start, &end);
808 }
809 ASSERT(start >= 0);
810 ASSERT(end >= 0);
811 unsigned remainingOffset = findOption == FindStart ? start : end;
812 // Find position.
813 for (unsigned i = 0; i < lengthList.size(); ++i) {
814 if (remainingOffset <= lengthList[i])
815 return Position(textList[i], (textList[i] == startPosition.anchorNode()) ? remainingOffset + startPosition.offsetInContainerNode() : remainingOffset);
816 remainingOffset -= lengthList[i];
817 }
818
819 ASSERT_NOT_REACHED();
820 return Position();
821 }
822
startOfWord(const Position & position)823 Position HTMLTextFormControlElement::startOfWord(const Position& position)
824 {
825 const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
826 ASSERT(textFormControl);
827 HTMLElement* innerEditor = textFormControl->innerEditorElement();
828
829 const Position startPosition = startOfSentence(position);
830 if (startPosition == position)
831 return position;
832 const Position endPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position;
833
834 return findWordBoundary(innerEditor, startPosition, endPosition, FindStart);
835 }
836
endOfWord(const Position & position)837 Position HTMLTextFormControlElement::endOfWord(const Position& position)
838 {
839 const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
840 ASSERT(textFormControl);
841 HTMLElement* innerEditor = textFormControl->innerEditorElement();
842
843 const Position endPosition = endOfSentence(position);
844 if (endPosition == position)
845 return position;
846 const Position startPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position;
847
848 return findWordBoundary(innerEditor, startPosition, endPosition, FindEnd);
849 }
850
endOfPrevious(const Node & node,HTMLElement * innerEditor)851 static Position endOfPrevious(const Node& node, HTMLElement* innerEditor)
852 {
853 Node* previousNode = NodeTraversal::previous(node, innerEditor);
854 if (!previousNode)
855 return Position();
856
857 if (isHTMLBRElement(previousNode))
858 return Position(previousNode, Position::PositionIsAfterAnchor);
859
860 if (previousNode->isTextNode())
861 return Position(toText(previousNode), toText(previousNode)->length());
862
863 return Position();
864 }
865
previousIfPositionIsAfterLineBreak(const Position & position,HTMLElement * innerEditor)866 static Position previousIfPositionIsAfterLineBreak(const Position& position, HTMLElement* innerEditor)
867 {
868 if (position.isNull())
869 return Position();
870
871 // Move back if position is just after line break.
872 if (isHTMLBRElement(*position.anchorNode())) {
873 switch (position.anchorType()) {
874 case Position::PositionIsAfterAnchor:
875 return Position(position.anchorNode(), Position::PositionIsBeforeAnchor);
876 case Position::PositionIsBeforeAnchor:
877 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor);
878 default:
879 ASSERT_NOT_REACHED();
880 }
881 } else if (position.anchorNode()->isTextNode()) {
882 Text* textNode = toText(position.anchorNode());
883 unsigned offset = position.offsetInContainerNode();
884 if (textNode->length() == 0 || offset == 0) {
885 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor);
886 }
887
888 if (offset <= textNode->length() && textNode->data()[offset - 1] == '\n') {
889 return Position(textNode, offset - 1);
890 }
891 }
892
893 return position;
894 }
895
startOfInnerText(const HTMLTextFormControlElement * textFormControl)896 static inline Position startOfInnerText(const HTMLTextFormControlElement* textFormControl)
897 {
898 return Position(textFormControl->innerEditorElement(), 0, Position::PositionIsOffsetInAnchor);
899 }
900
startOfSentence(const Position & position)901 Position HTMLTextFormControlElement::startOfSentence(const Position& position)
902 {
903 HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
904 ASSERT(textFormControl);
905
906 HTMLElement* innerEditor = textFormControl->innerEditorElement();
907 if (!innerEditor->childNodes()->length())
908 return startOfInnerText(textFormControl);
909
910 const Position innerPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position;
911 const Position pivotPosition = previousIfPositionIsAfterLineBreak(innerPosition, innerEditor);
912 if (pivotPosition.isNull())
913 return startOfInnerText(textFormControl);
914
915 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::previous(*node, innerEditor)) {
916 bool isPivotNode = (node == pivotPosition.anchorNode());
917
918 if (isHTMLBRElement(node) && (!isPivotNode || pivotPosition.anchorType() == Position::PositionIsAfterAnchor))
919 return Position(node, Position::PositionIsAfterAnchor);
920
921 if (node->isTextNode()) {
922 Text* textNode = toText(node);
923 size_t lastLineBreak = textNode->data().substring(0, isPivotNode ? pivotPosition.offsetInContainerNode() : textNode->length()).reverseFind('\n');
924 if (lastLineBreak != kNotFound)
925 return Position(textNode, lastLineBreak + 1);
926 }
927 }
928 return startOfInnerText(textFormControl);
929 }
930
endOfInnerText(const HTMLTextFormControlElement * textFormControl)931 static Position endOfInnerText(const HTMLTextFormControlElement* textFormControl)
932 {
933 HTMLElement* innerEditor = textFormControl->innerEditorElement();
934 return Position(innerEditor, innerEditor->childNodes()->length(), Position::PositionIsOffsetInAnchor);
935 }
936
endOfSentence(const Position & position)937 Position HTMLTextFormControlElement::endOfSentence(const Position& position)
938 {
939 HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
940 ASSERT(textFormControl);
941
942 HTMLElement* innerEditor = textFormControl->innerEditorElement();
943 if (innerEditor->childNodes()->length() == 0)
944 return startOfInnerText(textFormControl);
945
946 const Position pivotPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position;
947 if (pivotPosition.isNull())
948 return startOfInnerText(textFormControl);
949
950 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) {
951 bool isPivotNode = node == pivotPosition.anchorNode();
952
953 if (isHTMLBRElement(node))
954 return Position(node, Position::PositionIsAfterAnchor);
955
956 if (node->isTextNode()) {
957 Text* textNode = toText(node);
958 size_t firstLineBreak = textNode->data().find('\n', isPivotNode ? pivotPosition.offsetInContainerNode() : 0);
959 if (firstLineBreak != kNotFound)
960 return Position(textNode, firstLineBreak + 1);
961 }
962 }
963 return endOfInnerText(textFormControl);
964 }
965
966 } // namespace blink
967