352043930902ec529b4c01ab2f306a03f870a39b
[WebKit-https.git] / Source / WebCore / html / HTMLTextFormControlElement.cpp
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-2017 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 "HTMLTextFormControlElement.h"
27
28 #include "AXObjectCache.h"
29 #include "CSSPrimitiveValueMappings.h"
30 #include "ChromeClient.h"
31 #include "Document.h"
32 #include "Editing.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "Frame.h"
36 #include "FrameSelection.h"
37 #include "HTMLBRElement.h"
38 #include "HTMLFormElement.h"
39 #include "HTMLInputElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLParserIdioms.h"
42 #include "LayoutDisallowedScope.h"
43 #include "Logging.h"
44 #include "NoEventDispatchAssertion.h"
45 #include "NodeTraversal.h"
46 #include "Page.h"
47 #include "RenderTextControlSingleLine.h"
48 #include "RenderTheme.h"
49 #include "ShadowRoot.h"
50 #include "Text.h"
51 #include "TextControlInnerElements.h"
52 #include <wtf/text/StringBuilder.h>
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 static Position positionForIndex(TextControlInnerTextElement*, unsigned);
59
60 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
61     : HTMLFormControlElementWithState(tagName, document, form)
62     , m_cachedSelectionDirection(SelectionHasNoDirection)
63     , m_lastChangeWasUserEdit(false)
64     , m_isPlaceholderVisible(false)
65     , m_cachedSelectionStart(-1)
66     , m_cachedSelectionEnd(-1)
67 {
68 }
69
70 HTMLTextFormControlElement::~HTMLTextFormControlElement() = default;
71
72 bool HTMLTextFormControlElement::childShouldCreateRenderer(const Node& child) const
73 {
74     // FIXME: We shouldn't force the pseudo elements down into the shadow, but
75     // this perserves the current behavior of WebKit.
76     if (child.isPseudoElement())
77         return HTMLFormControlElementWithState::childShouldCreateRenderer(child);
78     return hasShadowRootParent(child) && HTMLFormControlElementWithState::childShouldCreateRenderer(child);
79 }
80
81 Node::InsertedIntoResult HTMLTextFormControlElement::insertedInto(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
82 {
83     InsertedIntoResult InsertedIntoResult = HTMLFormControlElementWithState::insertedInto(insertionType, parentOfInsertedTree);
84     if (insertionType.connectedToDocument) {
85         String initialValue = value();
86         setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
87     }
88     return InsertedIntoResult;
89 }
90
91 void HTMLTextFormControlElement::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection direction)
92 {
93     if (supportsPlaceholder())
94         updatePlaceholderVisibility();
95     handleFocusEvent(oldFocusedElement.get(), direction);
96     HTMLFormControlElementWithState::dispatchFocusEvent(WTFMove(oldFocusedElement), direction);
97 }
98
99 void HTMLTextFormControlElement::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
100 {
101     if (supportsPlaceholder())
102         updatePlaceholderVisibility();
103     // Match the order in Document::setFocusedElement.
104     handleBlurEvent();
105     HTMLFormControlElementWithState::dispatchBlurEvent(WTFMove(newFocusedElement));
106 }
107
108 void HTMLTextFormControlElement::didEditInnerTextValue()
109 {
110     if (!isTextFormControl())
111         return;
112
113     LOG(Editing, "HTMLTextFormControlElement %p didEditInnerTextValue", this);
114
115     m_lastChangeWasUserEdit = true;
116     subtreeHasChanged();
117 }
118
119 void HTMLTextFormControlElement::forwardEvent(Event& event)
120 {
121     if (event.type() == eventNames().blurEvent || event.type() == eventNames().focusEvent)
122         return;
123     innerTextElement()->defaultEventHandler(event);
124 }
125
126 String HTMLTextFormControlElement::strippedPlaceholder() const
127 {
128     // According to the HTML5 specification, we need to remove CR and LF from
129     // the attribute value.
130     const AtomicString& attributeValue = attributeWithoutSynchronization(placeholderAttr);
131     if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
132         return attributeValue;
133
134     StringBuilder stripped;
135     unsigned length = attributeValue.length();
136     stripped.reserveCapacity(length);
137     for (unsigned i = 0; i < length; ++i) {
138         UChar character = attributeValue[i];
139         if (character == newlineCharacter || character == carriageReturn)
140             continue;
141         stripped.append(character);
142     }
143     return stripped.toString();
144 }
145
146 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
147
148 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
149 {
150     const AtomicString& attributeValue = attributeWithoutSynchronization(placeholderAttr);
151     return attributeValue.string().find(isNotLineBreak) == notFound;
152 }
153
154 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
155 {
156     // This function is used by the style resolver to match the :placeholder-shown pseudo class.
157     // Since it is used for styling, it must not use any value depending on the style.
158     return supportsPlaceholder() && isEmptyValue() && !isPlaceholderEmpty();
159 }
160
161 void HTMLTextFormControlElement::updatePlaceholderVisibility()
162 {
163     bool placeHolderWasVisible = m_isPlaceholderVisible;
164     m_isPlaceholderVisible = placeholderShouldBeVisible();
165
166     if (placeHolderWasVisible == m_isPlaceholderVisible)
167         return;
168
169     invalidateStyleForSubtree();
170 }
171
172 void HTMLTextFormControlElement::setSelectionStart(int start)
173 {
174     setSelectionRange(start, std::max(start, selectionEnd()), selectionDirection());
175 }
176
177 void HTMLTextFormControlElement::setSelectionEnd(int end)
178 {
179     setSelectionRange(std::min(end, selectionStart()), end, selectionDirection());
180 }
181
182 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
183 {
184     setSelectionRange(selectionStart(), selectionEnd(), direction);
185 }
186
187 void HTMLTextFormControlElement::select(const AXTextStateChangeIntent& intent)
188 {
189     // FIXME: We should abstract the selection behavior into an EditingBehavior function instead
190     // of hardcoding the behavior using a macro define.
191 #if PLATFORM(IOS)
192     // We don't want to select all the text on iOS. Instead use the standard textfield behavior of going to the end of the line.
193     setSelectionRange(std::numeric_limits<int>::max(), std::numeric_limits<int>::max(), SelectionHasForwardDirection, intent);
194 #else
195     setSelectionRange(0, std::numeric_limits<int>::max(), SelectionHasNoDirection, intent);
196 #endif
197 }
198
199 String HTMLTextFormControlElement::selectedText() const
200 {
201     if (!isTextFormControl())
202         return String();
203     return value().substring(selectionStart(), selectionEnd() - selectionStart());
204 }
205
206 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
207 {
208     if (m_textAsOfLastFormControlChangeEvent != value()) {
209         dispatchChangeEvent();
210         setTextAsOfLastFormControlChangeEvent(value());
211     }
212     setChangedSinceLastFormControlChangeEvent(false);
213 }
214
215 ExceptionOr<void> HTMLTextFormControlElement::setRangeText(const String& replacement)
216 {
217     return setRangeText(replacement, selectionStart(), selectionEnd(), String());
218 }
219
220 ExceptionOr<void> HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode)
221 {
222     if (start > end)
223         return Exception { IndexSizeError };
224
225     String text = innerTextValue();
226     unsigned textLength = text.length();
227     unsigned replacementLength = replacement.length();
228     unsigned newSelectionStart = selectionStart();
229     unsigned newSelectionEnd = selectionEnd();
230
231     start = std::min(start, textLength);
232     end = std::min(end, textLength);
233
234     if (start < end)
235         text.replace(start, end - start, replacement);
236     else
237         text.insert(replacement, start);
238
239     setInnerTextValue(text);
240
241     // FIXME: What should happen to the value (as in value()) if there's no renderer?
242     if (!renderer())
243         return { };
244
245     subtreeHasChanged();
246
247     if (equalLettersIgnoringASCIICase(selectionMode, "select")) {
248         newSelectionStart = start;
249         newSelectionEnd = start + replacementLength;
250     } else if (equalLettersIgnoringASCIICase(selectionMode, "start"))
251         newSelectionStart = newSelectionEnd = start;
252     else if (equalLettersIgnoringASCIICase(selectionMode, "end"))
253         newSelectionStart = newSelectionEnd = start + replacementLength;
254     else {
255         // Default is "preserve".
256         long delta = replacementLength - (end - start);
257
258         if (newSelectionStart > end)
259             newSelectionStart += delta;
260         else if (newSelectionStart > start)
261             newSelectionStart = start;
262
263         if (newSelectionEnd > end)
264             newSelectionEnd += delta;
265         else if (newSelectionEnd > start)
266             newSelectionEnd = start + replacementLength;
267     }
268
269     setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
270
271     return { };
272 }
273
274 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString, const AXTextStateChangeIntent& intent)
275 {
276     TextFieldSelectionDirection direction = SelectionHasNoDirection;
277     if (directionString == "forward")
278         direction = SelectionHasForwardDirection;
279     else if (directionString == "backward")
280         direction = SelectionHasBackwardDirection;
281
282     return setSelectionRange(start, end, direction, intent);
283 }
284
285 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, const AXTextStateChangeIntent& intent)
286 {
287     if (!isTextFormControl())
288         return;
289
290     end = std::max(end, 0);
291     start = std::min(std::max(start, 0), end);
292
293     auto innerText = innerTextElement();
294     bool hasFocus = document().focusedElement() == this;
295     if (!hasFocus && innerText) {
296         // FIXME: Removing this synchronous layout requires fixing <https://webkit.org/b/128797>
297         document().updateLayoutIgnorePendingStylesheets();
298
299         // Double-check the state of innerTextElement after the layout.
300         innerText = innerTextElement();
301         auto* rendererTextControl = renderer();
302
303         if (innerText && rendererTextControl) {
304             if (rendererTextControl->style().visibility() == HIDDEN || !innerText->renderBox()->height()) {
305                 cacheSelection(start, end, direction);
306                 return;
307             }
308         }
309     }
310
311     Position startPosition = positionForIndex(innerText.get(), start);
312     Position endPosition;
313     if (start == end)
314         endPosition = startPosition;
315     else {
316         if (direction == SelectionHasBackwardDirection) {
317             endPosition = startPosition;
318             startPosition = positionForIndex(innerText.get(), end);
319         } else
320             endPosition = positionForIndex(innerText.get(), end);
321     }
322
323     if (RefPtr<Frame> frame = document().frame())
324         frame->selection().moveWithoutValidationTo(startPosition, endPosition, direction != SelectionHasNoDirection, !hasFocus, intent);
325 }
326
327 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& position) const
328 {
329     auto innerText = innerTextElement();
330     if (!innerText || !innerText->contains(position.deepEquivalent().anchorNode()))
331         return 0;
332     unsigned index = indexForPosition(position.deepEquivalent());
333     ASSERT(VisiblePosition(positionForIndex(innerTextElement().get(), index)) == position);
334     return index;
335 }
336
337 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
338 {
339     VisiblePosition position = positionForIndex(innerTextElement().get(), index);
340     ASSERT(indexForVisiblePosition(position) == index);
341     return position;
342 }
343
344 int HTMLTextFormControlElement::selectionStart() const
345 {
346     if (!isTextFormControl())
347         return 0;
348     if (document().focusedElement() != this && hasCachedSelection())
349         return m_cachedSelectionStart;
350
351     return computeSelectionStart();
352 }
353
354 int HTMLTextFormControlElement::computeSelectionStart() const
355 {
356     ASSERT(isTextFormControl());
357     RefPtr<Frame> frame = document().frame();
358     if (!frame)
359         return 0;
360
361     return indexForPosition(frame->selection().selection().start());
362 }
363
364 int HTMLTextFormControlElement::selectionEnd() const
365 {
366     if (!isTextFormControl())
367         return 0;
368     if (document().focusedElement() != this && hasCachedSelection())
369         return m_cachedSelectionEnd;
370     return computeSelectionEnd();
371 }
372
373 int HTMLTextFormControlElement::computeSelectionEnd() const
374 {
375     ASSERT(isTextFormControl());
376     RefPtr<Frame> frame = document().frame();
377     if (!frame)
378         return 0;
379
380     return indexForPosition(frame->selection().selection().end());
381 }
382
383 static const AtomicString& directionString(TextFieldSelectionDirection direction)
384 {
385     static NeverDestroyed<const AtomicString> none("none", AtomicString::ConstructFromLiteral);
386     static NeverDestroyed<const AtomicString> forward("forward", AtomicString::ConstructFromLiteral);
387     static NeverDestroyed<const AtomicString> backward("backward", AtomicString::ConstructFromLiteral);
388
389     switch (direction) {
390     case SelectionHasNoDirection:
391         return none;
392     case SelectionHasForwardDirection:
393         return forward;
394     case SelectionHasBackwardDirection:
395         return backward;
396     }
397
398     ASSERT_NOT_REACHED();
399     return none;
400 }
401
402 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
403 {
404     if (!isTextFormControl())
405         return directionString(SelectionHasNoDirection);
406     if (document().focusedElement() != this && hasCachedSelection())
407         return directionString(cachedSelectionDirection());
408
409     return directionString(computeSelectionDirection());
410 }
411
412 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
413 {
414     ASSERT(isTextFormControl());
415     RefPtr<Frame> frame = document().frame();
416     if (!frame)
417         return SelectionHasNoDirection;
418
419     const VisibleSelection& selection = frame->selection().selection();
420     return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
421 }
422
423 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
424 {
425     if (node->isTextNode()) {
426         containerNode = node;
427         offsetInContainer = offset;
428     } else {
429         containerNode = node->parentNode();
430         offsetInContainer = node->computeNodeIndex() + offset;
431     }
432 }
433
434 RefPtr<Range> HTMLTextFormControlElement::selection() const
435 {
436     if (!renderer() || !isTextFormControl() || !hasCachedSelection())
437         return nullptr;
438
439     int start = m_cachedSelectionStart;
440     int end = m_cachedSelectionEnd;
441
442     ASSERT(start <= end);
443     auto innerText = innerTextElement();
444     if (!innerText)
445         return nullptr;
446
447     if (!innerText->firstChild())
448         return Range::create(document(), innerText, 0, innerText, 0);
449
450     int offset = 0;
451     Node* startNode = nullptr;
452     Node* endNode = nullptr;
453     for (RefPtr<Node> node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText.get())) {
454         ASSERT(!node->firstChild());
455         ASSERT(node->isTextNode() || node->hasTagName(brTag));
456         int length = node->isTextNode() ? lastOffsetInNode(node.get()) : 1;
457
458         if (offset <= start && start <= offset + length)
459             setContainerAndOffsetForRange(node.get(), start - offset, startNode, start);
460
461         if (offset <= end && end <= offset + length) {
462             setContainerAndOffsetForRange(node.get(), end - offset, endNode, end);
463             break;
464         }
465
466         offset += length;
467     }
468
469     if (!startNode || !endNode)
470         return nullptr;
471
472     return Range::create(document(), startNode, start, endNode, end);
473 }
474
475 void HTMLTextFormControlElement::restoreCachedSelection(const AXTextStateChangeIntent& intent)
476 {
477     setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, cachedSelectionDirection(), intent);
478 }
479
480 void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
481 {
482     if (!isTextFormControl())
483         return;
484
485     // FIXME: Don't re-compute selection start and end if this function was called inside setSelectionRange.
486     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
487     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
488     
489     if (shouldFireSelectEvent && m_cachedSelectionStart != m_cachedSelectionEnd)
490         dispatchEvent(Event::create(eventNames().selectEvent, true, false));
491 }
492
493 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
494 {
495     if (name == placeholderAttr) {
496         updatePlaceholderText();
497         updatePlaceholderVisibility();
498     } else
499         HTMLFormControlElementWithState::parseAttribute(name, value);
500 }
501
502 void HTMLTextFormControlElement::disabledStateChanged()
503 {
504     HTMLFormControlElementWithState::disabledStateChanged();
505     updateInnerTextElementEditability();
506 }
507
508 void HTMLTextFormControlElement::readOnlyAttributeChanged()
509 {
510     HTMLFormControlElementWithState::disabledAttributeChanged();
511     updateInnerTextElementEditability();
512 }
513
514 void HTMLTextFormControlElement::updateInnerTextElementEditability()
515 {
516     if (auto innerText = innerTextElement())
517         innerText->setAttributeWithoutSynchronization(contenteditableAttr, isDisabledOrReadOnly() ? "false" : "plaintext-only");
518 }
519
520 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
521 {
522     if (!isTextFormControl())
523         return false;
524     return m_lastChangeWasUserEdit;
525 }
526
527 static void stripTrailingNewline(StringBuilder& result)
528 {
529     // Remove one trailing newline; there's always one that's collapsed out by rendering.
530     size_t size = result.length();
531     if (size && result[size - 1] == newlineCharacter)
532         result.resize(size - 1);
533 }
534
535 static String innerTextValueFrom(TextControlInnerTextElement& innerText)
536 {
537     StringBuilder result;
538     for (RefPtr<Node> node = innerText.firstChild(); node; node = NodeTraversal::next(*node, &innerText)) {
539         if (is<HTMLBRElement>(*node))
540             result.append(newlineCharacter);
541         else if (is<Text>(*node))
542             result.append(downcast<Text>(*node).data());
543     }
544     stripTrailingNewline(result);
545     return result.toString();
546 }
547
548 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
549 {
550     LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::PerformanceOptimization);
551     auto innerText = innerTextElement();
552     if (!innerText)
553         return;
554
555     ASSERT(isTextFormControl());
556     String previousValue = innerTextValueFrom(*innerText);
557     bool textIsChanged = value != previousValue;
558     if (textIsChanged || !innerText->hasChildNodes()) {
559 #if HAVE(ACCESSIBILITY) && !PLATFORM(COCOA)
560         if (textIsChanged && renderer()) {
561             if (AXObjectCache* cache = document().existingAXObjectCache())
562                 cache->postNotification(this, AXObjectCache::AXValueChanged, TargetObservableParent);
563         }
564 #endif
565
566         {
567             // Events dispatched on the inner text element cannot execute arbitrary author scripts.
568             NoEventDispatchAssertion::EventAllowedScope allowedScope(*userAgentShadowRoot());
569
570             innerText->setInnerText(value);
571
572             if (value.endsWith('\n') || value.endsWith('\r'))
573                 innerText->appendChild(HTMLBRElement::create(document()));
574         }
575
576 #if HAVE(ACCESSIBILITY) && PLATFORM(COCOA)
577         if (textIsChanged && renderer()) {
578             if (AXObjectCache* cache = document().existingAXObjectCache())
579                 cache->postTextReplacementNotificationForTextControl(*this, previousValue, value);
580         }
581 #endif
582     }
583
584     setFormControlValueMatchesRenderer(true);
585 }
586
587 String HTMLTextFormControlElement::innerTextValue() const
588 {
589     auto innerText = innerTextElement();
590     return innerText ? innerTextValueFrom(*innerText) : emptyString();
591 }
592
593 static Position positionForIndex(TextControlInnerTextElement* innerText, unsigned index)
594 {
595     unsigned remainingCharactersToMoveForward = index;
596     RefPtr<Node> lastBrOrText = innerText;
597     for (RefPtr<Node> node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
598         if (node->hasTagName(brTag)) {
599             if (!remainingCharactersToMoveForward)
600                 return positionBeforeNode(node.get());
601             remainingCharactersToMoveForward--;
602             lastBrOrText = node;
603         } else if (is<Text>(*node)) {
604             Text& text = downcast<Text>(*node);
605             if (remainingCharactersToMoveForward < text.length())
606                 return Position(&text, remainingCharactersToMoveForward);
607             remainingCharactersToMoveForward -= text.length();
608             lastBrOrText = node;
609         }
610     }
611     return lastPositionInOrAfterNode(lastBrOrText.get());
612 }
613
614 unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosition) const
615 {
616     auto innerText = innerTextElement();
617     if (!innerText || !innerText->contains(passedPosition.anchorNode()) || passedPosition.isNull())
618         return 0;
619
620     if (positionBeforeNode(innerText.get()) == passedPosition)
621         return 0;
622
623     unsigned index = 0;
624     RefPtr<Node> startNode = passedPosition.computeNodeBeforePosition();
625     if (!startNode)
626         startNode = passedPosition.containerNode();
627     ASSERT(startNode);
628     ASSERT(innerText->contains(startNode.get()));
629
630     for (RefPtr<Node> node = startNode; node; node = NodeTraversal::previous(*node, innerText.get())) {
631         if (is<Text>(*node)) {
632             unsigned length = downcast<Text>(*node).length();
633             if (node == passedPosition.containerNode())
634                 index += std::min<unsigned>(length, passedPosition.offsetInContainerNode());
635             else
636                 index += length;
637         } else if (is<HTMLBRElement>(*node))
638             ++index;
639     }
640
641     unsigned length = innerTextValue().length();
642     index = std::min(index, length); // FIXME: We shouldn't have to call innerTextValue() just to ignore the last LF. See finishText.
643 #ifndef ASSERT_DISABLED
644     VisiblePosition visiblePosition = passedPosition;
645     unsigned indexComputedByVisiblePosition = 0;
646     if (visiblePosition.isNotNull())
647         indexComputedByVisiblePosition = WebCore::indexForVisiblePosition(innerText, visiblePosition, false /* forSelectionPreservation */);
648     ASSERT(index == indexComputedByVisiblePosition);
649 #endif
650     return index;
651 }
652
653 #if PLATFORM(IOS)
654 void HTMLTextFormControlElement::hidePlaceholder()
655 {
656     if (RefPtr<HTMLElement> placeholder = placeholderElement())
657         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden, true);
658 }
659
660 void HTMLTextFormControlElement::showPlaceholderIfNecessary()
661 {
662     if (RefPtr<HTMLElement> placeholder = placeholderElement())
663         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, true);
664 }
665 #endif
666
667 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
668 {
669     RootInlineBox* next;
670     for (; line; line = next) {
671         next = line->nextRootBox();
672         if (next && !line->endsWithBreak()) {
673             ASSERT(line->lineBreakObj());
674             breakNode = line->lineBreakObj()->node();
675             breakOffset = line->lineBreakPos();
676             line = next;
677             return;
678         }
679     }
680     breakNode = 0;
681     breakOffset = 0;
682 }
683
684 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
685 {
686     // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
687     // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
688     if (!isTextFormControl())
689         return value();
690
691     auto innerText = innerTextElement();
692     if (!innerText)
693         return value();
694
695     RenderTextControlInnerBlock* renderer = innerText->renderer();
696     if (!renderer)
697         return value();
698
699     Node* breakNode;
700     unsigned breakOffset;
701     RootInlineBox* line = renderer->firstRootBox();
702     if (!line)
703         return value();
704
705     getNextSoftBreak(line, breakNode, breakOffset);
706
707     StringBuilder result;
708     for (RefPtr<Node> node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText.get())) {
709         if (is<HTMLBRElement>(*node))
710             result.append(newlineCharacter);
711         else if (is<Text>(*node)) {
712             String data = downcast<Text>(*node).data();
713             unsigned length = data.length();
714             unsigned position = 0;
715             while (breakNode == node && breakOffset <= length) {
716                 if (breakOffset > position) {
717                     result.append(data, position, breakOffset - position);
718                     position = breakOffset;
719                     result.append(newlineCharacter);
720                 }
721                 getNextSoftBreak(line, breakNode, breakOffset);
722             }
723             result.append(data, position, length - position);
724         }
725         while (breakNode == node)
726             getNextSoftBreak(line, breakNode, breakOffset);
727     }
728     stripTrailingNewline(result);
729     return result.toString();
730 }
731
732 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
733 {
734     ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
735         || position.containerNode() || !position.anchorNode()->shadowHost()
736         || hasShadowRootParent(*position.anchorNode()));
737         
738     RefPtr<Node> container = position.containerNode();
739     if (!container)
740         return nullptr;
741     RefPtr<Element> ancestor = container->shadowHost();
742     return ancestor && is<HTMLTextFormControlElement>(*ancestor) ? downcast<HTMLTextFormControlElement>(ancestor.get()) : nullptr;
743 }
744
745 static const Element* parentHTMLElement(const Element* element)
746 {
747     while (element) {
748         element = element->parentElement();
749         if (element && element->isHTMLElement())
750             return element;
751     }
752     return 0;
753 }
754
755 String HTMLTextFormControlElement::directionForFormData() const
756 {
757     for (const Element* element = this; element; element = parentHTMLElement(element)) {
758         const AtomicString& dirAttributeValue = element->attributeWithoutSynchronization(dirAttr);
759         if (dirAttributeValue.isNull())
760             continue;
761
762         if (equalLettersIgnoringASCIICase(dirAttributeValue, "rtl") || equalLettersIgnoringASCIICase(dirAttributeValue, "ltr"))
763             return dirAttributeValue;
764
765         if (equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) {
766             bool isAuto;
767             TextDirection textDirection = static_cast<const HTMLElement*>(element)->directionalityIfhasDirAutoAttribute(isAuto);
768             return textDirection == RTL ? "rtl" : "ltr";
769         }
770     }
771
772     return "ltr";
773 }
774
775 ExceptionOr<void> HTMLTextFormControlElement::setMaxLength(int maxLength)
776 {
777     if (maxLength < 0 || (m_minLength >= 0 && maxLength < m_minLength))
778         return Exception { IndexSizeError };
779     setIntegralAttribute(maxlengthAttr, maxLength);
780     return { };
781 }
782
783 ExceptionOr<void> HTMLTextFormControlElement::setMinLength(int minLength)
784 {
785     if (minLength < 0 || (m_maxLength >= 0 && minLength > m_maxLength))
786         return Exception { IndexSizeError };
787     setIntegralAttribute(minlengthAttr, minLength);
788     return { };
789 }
790
791 void HTMLTextFormControlElement::adjustInnerTextStyle(const RenderStyle& parentStyle, RenderStyle& textBlockStyle) const
792 {
793     // The inner block, if present, always has its direction set to LTR,
794     // so we need to inherit the direction and unicode-bidi style from the element.
795     textBlockStyle.setDirection(parentStyle.direction());
796     textBlockStyle.setUnicodeBidi(parentStyle.unicodeBidi());
797
798     if (auto innerText = innerTextElement()) {
799         if (const StyleProperties* properties = innerText->presentationAttributeStyle()) {
800             RefPtr<CSSValue> value = properties->getPropertyCSSValue(CSSPropertyWebkitUserModify);
801             if (is<CSSPrimitiveValue>(value.get()))
802                 textBlockStyle.setUserModify(downcast<CSSPrimitiveValue>(*value));
803         }
804     }
805
806     if (isDisabledFormControl())
807         textBlockStyle.setColor(RenderTheme::singleton().disabledTextColor(textBlockStyle.visitedDependentColor(CSSPropertyColor), parentStyle.visitedDependentColor(CSSPropertyBackgroundColor)));
808 #if PLATFORM(IOS)
809     if (textBlockStyle.textSecurity() != TSNONE && !textBlockStyle.isLeftToRightDirection()) {
810         // Preserve the alignment but force the direction to LTR so that the last-typed, unmasked character
811         // (which cannot have RTL directionality) will appear to the right of the masked characters. See <rdar://problem/7024375>.
812         
813         switch (textBlockStyle.textAlign()) {
814         case TASTART:
815         case JUSTIFY:
816             textBlockStyle.setTextAlign(RIGHT);
817             break;
818         case TAEND:
819             textBlockStyle.setTextAlign(LEFT);
820             break;
821         case LEFT:
822         case RIGHT:
823         case CENTER:
824         case WEBKIT_LEFT:
825         case WEBKIT_RIGHT:
826         case WEBKIT_CENTER:
827             break;
828         }
829
830         textBlockStyle.setDirection(LTR);
831     }
832 #endif
833 }
834
835 } // namespace Webcore