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