DOM event handling should pass Event around by reference.
[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, 2005, 2006, 2007, 2014 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 "Event.h"
33 #include "EventNames.h"
34 #include "Frame.h"
35 #include "FrameSelection.h"
36 #include "HTMLBRElement.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLParserIdioms.h"
41 #include "Logging.h"
42 #include "NodeTraversal.h"
43 #include "Page.h"
44 #include "RenderBlockFlow.h"
45 #include "RenderTextControlSingleLine.h"
46 #include "RenderTheme.h"
47 #include "ShadowRoot.h"
48 #include "Text.h"
49 #include "TextControlInnerElements.h"
50 #include "htmlediting.h"
51 #include <wtf/text/StringBuilder.h>
52
53 namespace WebCore {
54
55 using namespace HTMLNames;
56
57 static Position positionForIndex(TextControlInnerTextElement*, unsigned);
58
59 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
60     : HTMLFormControlElementWithState(tagName, document, form)
61     , m_cachedSelectionStart(-1)
62     , m_cachedSelectionEnd(-1)
63     , m_cachedSelectionDirection(SelectionHasNoDirection)
64     , m_lastChangeWasUserEdit(false)
65     , m_isPlaceholderVisible(false)
66 {
67 }
68
69 HTMLTextFormControlElement::~HTMLTextFormControlElement()
70 {
71 }
72
73 bool HTMLTextFormControlElement::childShouldCreateRenderer(const Node& child) const
74 {
75     // FIXME: We shouldn't force the pseudo elements down into the shadow, but
76     // this perserves the current behavior of WebKit.
77     if (child.isPseudoElement())
78         return HTMLFormControlElementWithState::childShouldCreateRenderer(child);
79     return hasShadowRootParent(child) && HTMLFormControlElementWithState::childShouldCreateRenderer(child);
80 }
81
82 Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode& insertionPoint)
83 {
84     InsertionNotificationRequest insertionNotificationRequest = HTMLFormControlElementWithState::insertedInto(insertionPoint);
85     if (!insertionPoint.inDocument())
86         return insertionNotificationRequest;
87     String initialValue = value();
88     setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
89     return insertionNotificationRequest;
90 }
91
92 void HTMLTextFormControlElement::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection direction)
93 {
94     if (supportsPlaceholder())
95         updatePlaceholderVisibility();
96     handleFocusEvent(oldFocusedElement.get(), direction);
97     HTMLFormControlElementWithState::dispatchFocusEvent(WTFMove(oldFocusedElement), direction);
98 }
99
100 void HTMLTextFormControlElement::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
101 {
102     if (supportsPlaceholder())
103         updatePlaceholderVisibility();
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     setNeedsStyleRecalc();
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 void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionCode& ec)
216 {
217     setRangeText(replacement, selectionStart(), selectionEnd(), String(), ec);
218 }
219
220 void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionCode& ec)
221 {
222     if (start > end) {
223         ec = INDEX_SIZE_ERR;
224         return;
225     }
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
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     TextControlInnerTextElement* 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         if (RenderElement* rendererTextControl = renderer()) {
299             if (rendererTextControl->style().visibility() == HIDDEN || !innerText->renderBox()->height()) {
300                 cacheSelection(start, end, direction);
301                 return;
302             }
303         }
304     }
305
306     Position startPosition = positionForIndex(innerText, start);
307     Position endPosition;
308     if (start == end)
309         endPosition = startPosition;
310     else {
311         if (direction == SelectionHasBackwardDirection) {
312             endPosition = startPosition;
313             startPosition = positionForIndex(innerText, end);
314         } else
315             endPosition = positionForIndex(innerText, end);
316     }
317
318     if (Frame* frame = document().frame())
319         frame->selection().moveWithoutValidationTo(startPosition, endPosition, direction != SelectionHasNoDirection, !hasFocus, intent);
320 }
321
322 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& position) const
323 {
324     TextControlInnerTextElement* innerText = innerTextElement();
325     if (!innerText || !innerText->contains(position.deepEquivalent().anchorNode()))
326         return 0;
327     unsigned index = indexForPosition(position.deepEquivalent());
328     ASSERT(VisiblePosition(positionForIndex(innerTextElement(), index)) == position);
329     return index;
330 }
331
332 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
333 {
334     VisiblePosition position = positionForIndex(innerTextElement(), index);
335     ASSERT(indexForVisiblePosition(position) == index);
336     return position;
337 }
338
339 int HTMLTextFormControlElement::selectionStart() const
340 {
341     if (!isTextFormControl())
342         return 0;
343     if (document().focusedElement() != this && hasCachedSelection())
344         return m_cachedSelectionStart;
345
346     return computeSelectionStart();
347 }
348
349 int HTMLTextFormControlElement::computeSelectionStart() const
350 {
351     ASSERT(isTextFormControl());
352     Frame* frame = document().frame();
353     if (!frame)
354         return 0;
355
356     return indexForPosition(frame->selection().selection().start());
357 }
358
359 int HTMLTextFormControlElement::selectionEnd() const
360 {
361     if (!isTextFormControl())
362         return 0;
363     if (document().focusedElement() != this && hasCachedSelection())
364         return m_cachedSelectionEnd;
365     return computeSelectionEnd();
366 }
367
368 int HTMLTextFormControlElement::computeSelectionEnd() const
369 {
370     ASSERT(isTextFormControl());
371     Frame* frame = document().frame();
372     if (!frame)
373         return 0;
374
375     return indexForPosition(frame->selection().selection().end());
376 }
377
378 static const AtomicString& directionString(TextFieldSelectionDirection direction)
379 {
380     static NeverDestroyed<const AtomicString> none("none", AtomicString::ConstructFromLiteral);
381     static NeverDestroyed<const AtomicString> forward("forward", AtomicString::ConstructFromLiteral);
382     static NeverDestroyed<const AtomicString> backward("backward", AtomicString::ConstructFromLiteral);
383
384     switch (direction) {
385     case SelectionHasNoDirection:
386         return none;
387     case SelectionHasForwardDirection:
388         return forward;
389     case SelectionHasBackwardDirection:
390         return backward;
391     }
392
393     ASSERT_NOT_REACHED();
394     return none;
395 }
396
397 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
398 {
399     if (!isTextFormControl())
400         return directionString(SelectionHasNoDirection);
401     if (document().focusedElement() != this && hasCachedSelection())
402         return directionString(cachedSelectionDirection());
403
404     return directionString(computeSelectionDirection());
405 }
406
407 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
408 {
409     ASSERT(isTextFormControl());
410     Frame* frame = document().frame();
411     if (!frame)
412         return SelectionHasNoDirection;
413
414     const VisibleSelection& selection = frame->selection().selection();
415     return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
416 }
417
418 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
419 {
420     if (node->isTextNode()) {
421         containerNode = node;
422         offsetInContainer = offset;
423     } else {
424         containerNode = node->parentNode();
425         offsetInContainer = node->computeNodeIndex() + offset;
426     }
427 }
428
429 PassRefPtr<Range> HTMLTextFormControlElement::selection() const
430 {
431     if (!renderer() || !isTextFormControl() || !hasCachedSelection())
432         return nullptr;
433
434     int start = m_cachedSelectionStart;
435     int end = m_cachedSelectionEnd;
436
437     ASSERT(start <= end);
438     TextControlInnerTextElement* innerText = innerTextElement();
439     if (!innerText)
440         return nullptr;
441
442     if (!innerText->firstChild())
443         return Range::create(document(), innerText, 0, innerText, 0);
444
445     int offset = 0;
446     Node* startNode = nullptr;
447     Node* endNode = nullptr;
448     for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
449         ASSERT(!node->firstChild());
450         ASSERT(node->isTextNode() || node->hasTagName(brTag));
451         int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
452
453         if (offset <= start && start <= offset + length)
454             setContainerAndOffsetForRange(node, start - offset, startNode, start);
455
456         if (offset <= end && end <= offset + length) {
457             setContainerAndOffsetForRange(node, end - offset, endNode, end);
458             break;
459         }
460
461         offset += length;
462     }
463
464     if (!startNode || !endNode)
465         return nullptr;
466
467     return Range::create(document(), startNode, start, endNode, end);
468 }
469
470 void HTMLTextFormControlElement::restoreCachedSelection(const AXTextStateChangeIntent& intent)
471 {
472     setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, cachedSelectionDirection(), intent);
473 }
474
475 void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
476 {
477     if (!isTextFormControl())
478         return;
479
480     // FIXME: Don't re-compute selection start and end if this function was called inside setSelectionRange.
481     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
482     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
483     
484     if (shouldFireSelectEvent && m_cachedSelectionStart != m_cachedSelectionEnd)
485         dispatchEvent(Event::create(eventNames().selectEvent, true, false));
486 }
487
488 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
489 {
490     if (name == placeholderAttr) {
491         updatePlaceholderText();
492         updatePlaceholderVisibility();
493     } else
494         HTMLFormControlElementWithState::parseAttribute(name, value);
495 }
496
497 void HTMLTextFormControlElement::disabledStateChanged()
498 {
499     HTMLFormControlElementWithState::disabledStateChanged();
500     updateInnerTextElementEditability();
501 }
502
503 void HTMLTextFormControlElement::readOnlyAttributeChanged()
504 {
505     HTMLFormControlElementWithState::disabledAttributeChanged();
506     updateInnerTextElementEditability();
507 }
508
509 void HTMLTextFormControlElement::updateInnerTextElementEditability()
510 {
511     if (TextControlInnerTextElement* innerText = innerTextElement())
512         innerText->setAttributeWithoutSynchronization(contenteditableAttr, isDisabledOrReadOnly() ? "false" : "plaintext-only");
513 }
514
515 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
516 {
517     if (!isTextFormControl())
518         return false;
519     return m_lastChangeWasUserEdit;
520 }
521
522 static void stripTrailingNewline(StringBuilder& result)
523 {
524     // Remove one trailing newline; there's always one that's collapsed out by rendering.
525     size_t size = result.length();
526     if (size && result[size - 1] == newlineCharacter)
527         result.resize(size - 1);
528 }
529
530 static String innerTextValueFrom(TextControlInnerTextElement& innerText)
531 {
532     StringBuilder result;
533     for (Node* node = innerText.firstChild(); node; node = NodeTraversal::next(*node, &innerText)) {
534         if (is<HTMLBRElement>(*node))
535             result.append(newlineCharacter);
536         else if (is<Text>(*node))
537             result.append(downcast<Text>(*node).data());
538     }
539     stripTrailingNewline(result);
540     return result.toString();
541 }
542
543 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
544 {
545     TextControlInnerTextElement* innerText = innerTextElement();
546     if (!innerText)
547         return;
548
549     ASSERT(isTextFormControl());
550     String previousValue = innerTextValueFrom(*innerText);
551     bool textIsChanged = value != previousValue;
552     if (textIsChanged || !innerText->hasChildNodes()) {
553 #if HAVE(ACCESSIBILITY) && !PLATFORM(COCOA)
554         if (textIsChanged && renderer()) {
555             if (AXObjectCache* cache = document().existingAXObjectCache())
556                 cache->postNotification(this, AXObjectCache::AXValueChanged, TargetObservableParent);
557         }
558 #endif
559         innerText->setInnerText(value, ASSERT_NO_EXCEPTION);
560
561         if (value.endsWith('\n') || value.endsWith('\r'))
562             innerText->appendChild(HTMLBRElement::create(document()), ASSERT_NO_EXCEPTION);
563
564 #if HAVE(ACCESSIBILITY) && PLATFORM(COCOA)
565         if (textIsChanged && renderer()) {
566             if (AXObjectCache* cache = document().existingAXObjectCache())
567                 cache->postTextReplacementNotification(this, AXTextEditTypeDelete, previousValue, AXTextEditTypeInsert, value, VisiblePosition(Position(this, Position::PositionIsBeforeAnchor)));
568         }
569 #endif
570     }
571
572     setFormControlValueMatchesRenderer(true);
573 }
574
575 String HTMLTextFormControlElement::innerTextValue() const
576 {
577     TextControlInnerTextElement* innerText = innerTextElement();
578     return innerText ? innerTextValueFrom(*innerText) : emptyString();
579 }
580
581 static Position positionForIndex(TextControlInnerTextElement* innerText, unsigned index)
582 {
583     unsigned remainingCharactersToMoveForward = index;
584     Node* lastBrOrText = innerText;
585     for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
586         if (node->hasTagName(brTag)) {
587             if (!remainingCharactersToMoveForward)
588                 return positionBeforeNode(node);
589             remainingCharactersToMoveForward--;
590             lastBrOrText = node;
591         } else if (is<Text>(*node)) {
592             Text& text = downcast<Text>(*node);
593             if (remainingCharactersToMoveForward < text.length())
594                 return Position(&text, remainingCharactersToMoveForward);
595             remainingCharactersToMoveForward -= text.length();
596             lastBrOrText = node;
597         }
598     }
599     return lastPositionInOrAfterNode(lastBrOrText);
600 }
601
602 unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosition) const
603 {
604     TextControlInnerTextElement* innerText = innerTextElement();
605     if (!innerText || !innerText->contains(passedPosition.anchorNode()) || passedPosition.isNull())
606         return 0;
607
608     if (positionBeforeNode(innerText) == passedPosition)
609         return 0;
610
611     unsigned index = 0;
612     Node* startNode = passedPosition.computeNodeBeforePosition();
613     if (!startNode)
614         startNode = passedPosition.containerNode();
615     ASSERT(startNode);
616     ASSERT(innerText->contains(startNode));
617
618     for (Node* node = startNode; node; node = NodeTraversal::previous(*node, innerText)) {
619         if (is<Text>(*node)) {
620             unsigned length = downcast<Text>(*node).length();
621             if (node == passedPosition.containerNode())
622                 index += std::min<unsigned>(length, passedPosition.offsetInContainerNode());
623             else
624                 index += length;
625         } else if (is<HTMLBRElement>(*node))
626             ++index;
627     }
628
629     unsigned length = innerTextValue().length();
630     index = std::min(index, length); // FIXME: We shouldn't have to call innerTextValue() just to ignore the last LF. See finishText.
631 #ifndef ASSERT_DISABLED
632     VisiblePosition visiblePosition = passedPosition;
633     unsigned indexComputedByVisiblePosition = 0;
634     if (visiblePosition.isNotNull())
635         indexComputedByVisiblePosition = WebCore::indexForVisiblePosition(innerText, visiblePosition, false /* forSelectionPreservation */);
636     ASSERT(index == indexComputedByVisiblePosition);
637 #endif
638     return index;
639 }
640
641 #if PLATFORM(IOS)
642 void HTMLTextFormControlElement::hidePlaceholder()
643 {
644     if (HTMLElement* placeholder = placeholderElement())
645         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden, true);
646 }
647
648 void HTMLTextFormControlElement::showPlaceholderIfNecessary()
649 {
650     if (HTMLElement* placeholder = placeholderElement())
651         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, true);
652 }
653 #endif
654
655 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
656 {
657     RootInlineBox* next;
658     for (; line; line = next) {
659         next = line->nextRootBox();
660         if (next && !line->endsWithBreak()) {
661             ASSERT(line->lineBreakObj());
662             breakNode = line->lineBreakObj()->node();
663             breakOffset = line->lineBreakPos();
664             line = next;
665             return;
666         }
667     }
668     breakNode = 0;
669     breakOffset = 0;
670 }
671
672 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
673 {
674     // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
675     // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
676     if (!isTextFormControl())
677         return value();
678
679     TextControlInnerTextElement* innerText = innerTextElement();
680     if (!innerText)
681         return value();
682
683     RenderTextControlInnerBlock* renderer = innerText->renderer();
684     if (!renderer)
685         return value();
686
687     Node* breakNode;
688     unsigned breakOffset;
689     RootInlineBox* line = renderer->firstRootBox();
690     if (!line)
691         return value();
692
693     getNextSoftBreak(line, breakNode, breakOffset);
694
695     StringBuilder result;
696     for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
697         if (is<HTMLBRElement>(*node))
698             result.append(newlineCharacter);
699         else if (is<Text>(*node)) {
700             String data = downcast<Text>(*node).data();
701             unsigned length = data.length();
702             unsigned position = 0;
703             while (breakNode == node && breakOffset <= length) {
704                 if (breakOffset > position) {
705                     result.append(data, position, breakOffset - position);
706                     position = breakOffset;
707                     result.append(newlineCharacter);
708                 }
709                 getNextSoftBreak(line, breakNode, breakOffset);
710             }
711             result.append(data, position, length - position);
712         }
713         while (breakNode == node)
714             getNextSoftBreak(line, breakNode, breakOffset);
715     }
716     stripTrailingNewline(result);
717     return result.toString();
718 }
719
720 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
721 {
722     ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
723         || position.containerNode() || !position.anchorNode()->shadowHost()
724         || hasShadowRootParent(*position.anchorNode()));
725         
726     Node* container = position.containerNode();
727     if (!container)
728         return nullptr;
729     Element* ancestor = container->shadowHost();
730     return ancestor && is<HTMLTextFormControlElement>(*ancestor) ? downcast<HTMLTextFormControlElement>(ancestor) : nullptr;
731 }
732
733 static const Element* parentHTMLElement(const Element* element)
734 {
735     while (element) {
736         element = element->parentElement();
737         if (element && element->isHTMLElement())
738             return element;
739     }
740     return 0;
741 }
742
743 String HTMLTextFormControlElement::directionForFormData() const
744 {
745     for (const Element* element = this; element; element = parentHTMLElement(element)) {
746         const AtomicString& dirAttributeValue = element->attributeWithoutSynchronization(dirAttr);
747         if (dirAttributeValue.isNull())
748             continue;
749
750         if (equalLettersIgnoringASCIICase(dirAttributeValue, "rtl") || equalLettersIgnoringASCIICase(dirAttributeValue, "ltr"))
751             return dirAttributeValue;
752
753         if (equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) {
754             bool isAuto;
755             TextDirection textDirection = static_cast<const HTMLElement*>(element)->directionalityIfhasDirAutoAttribute(isAuto);
756             return textDirection == RTL ? "rtl" : "ltr";
757         }
758     }
759
760     return "ltr";
761 }
762
763 void HTMLTextFormControlElement::setMaxLengthForBindings(int maxLength, ExceptionCode& ec)
764 {
765     if (maxLength < 0)
766         ec = INDEX_SIZE_ERR;
767     else
768         setIntegralAttribute(maxlengthAttr, maxLength);
769 }
770
771 void HTMLTextFormControlElement::adjustInnerTextStyle(const RenderStyle& parentStyle, RenderStyle& textBlockStyle) const
772 {
773     // The inner block, if present, always has its direction set to LTR,
774     // so we need to inherit the direction and unicode-bidi style from the element.
775     textBlockStyle.setDirection(parentStyle.direction());
776     textBlockStyle.setUnicodeBidi(parentStyle.unicodeBidi());
777
778     if (HTMLElement* innerText = innerTextElement()) {
779         if (const StyleProperties* properties = innerText->presentationAttributeStyle()) {
780             RefPtr<CSSValue> value = properties->getPropertyCSSValue(CSSPropertyWebkitUserModify);
781             if (is<CSSPrimitiveValue>(value.get()))
782                 textBlockStyle.setUserModify(downcast<CSSPrimitiveValue>(*value));
783         }
784     }
785
786     if (isDisabledFormControl())
787         textBlockStyle.setColor(document().page()->theme().disabledTextColor(textBlockStyle.visitedDependentColor(CSSPropertyColor), parentStyle.visitedDependentColor(CSSPropertyBackgroundColor)));
788 #if PLATFORM(IOS)
789     if (textBlockStyle.textSecurity() != TSNONE && !textBlockStyle.isLeftToRightDirection()) {
790         // Preserve the alignment but force the direction to LTR so that the last-typed, unmasked character
791         // (which cannot have RTL directionality) will appear to the right of the masked characters. See <rdar://problem/7024375>.
792         
793         switch (textBlockStyle.textAlign()) {
794         case TASTART:
795         case JUSTIFY:
796             textBlockStyle.setTextAlign(RIGHT);
797             break;
798         case TAEND:
799             textBlockStyle.setTextAlign(LEFT);
800             break;
801         case LEFT:
802         case RIGHT:
803         case CENTER:
804         case WEBKIT_LEFT:
805         case WEBKIT_RIGHT:
806         case WEBKIT_CENTER:
807             break;
808         }
809
810         textBlockStyle.setDirection(LTR);
811     }
812 #endif
813 }
814
815 } // namespace Webcore