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