• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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