Implement rendering support for the color-filter CSS property
[WebKit-https.git] / Source / WebCore / html / HTMLTextFormControlElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLTextFormControlElement.h"
27
28 #include "AXObjectCache.h"
29 #include "CSSPrimitiveValueMappings.h"
30 #include "ChromeClient.h"
31 #include "Document.h"
32 #include "Editing.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "Frame.h"
36 #include "FrameSelection.h"
37 #include "HTMLBRElement.h"
38 #include "HTMLFormElement.h"
39 #include "HTMLInputElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLParserIdioms.h"
42 #include "LayoutDisallowedScope.h"
43 #include "Logging.h"
44 #include "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 (!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 AtomicString& 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 AtomicString& 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     // FIXME: We should abstract the selection behavior into an EditingBehavior function instead
193     // of hardcoding the behavior using a macro define.
194 #if PLATFORM(IOS)
195     // 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.
196     setSelectionRange(std::numeric_limits<int>::max(), std::numeric_limits<int>::max(), SelectionHasForwardDirection, revealMode, intent);
197 #else
198     setSelectionRange(0, std::numeric_limits<int>::max(), SelectionHasNoDirection, revealMode, intent);
199 #endif
200 }
201
202 String HTMLTextFormControlElement::selectedText() const
203 {
204     if (!isTextField())
205         return String();
206     return value().substring(selectionStart(), selectionEnd() - selectionStart());
207 }
208
209 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
210 {
211     if (m_textAsOfLastFormControlChangeEvent != value()) {
212         dispatchChangeEvent();
213         setTextAsOfLastFormControlChangeEvent(value());
214     }
215     setChangedSinceLastFormControlChangeEvent(false);
216 }
217
218 ExceptionOr<void> HTMLTextFormControlElement::setRangeText(const String& replacement)
219 {
220     return setRangeText(replacement, selectionStart(), selectionEnd(), String());
221 }
222
223 ExceptionOr<void> HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode)
224 {
225     if (start > end)
226         return Exception { IndexSizeError };
227
228     String text = innerTextValue();
229     unsigned textLength = text.length();
230     unsigned replacementLength = replacement.length();
231     unsigned newSelectionStart = selectionStart();
232     unsigned newSelectionEnd = selectionEnd();
233
234     start = std::min(start, textLength);
235     end = std::min(end, textLength);
236
237     if (start < end)
238         text.replace(start, end - start, replacement);
239     else
240         text.insert(replacement, start);
241
242     setInnerTextValue(text);
243
244     // FIXME: What should happen to the value (as in value()) if there's no renderer?
245     if (!renderer())
246         return { };
247
248     subtreeHasChanged();
249
250     if (equalLettersIgnoringASCIICase(selectionMode, "select")) {
251         newSelectionStart = start;
252         newSelectionEnd = start + replacementLength;
253     } else if (equalLettersIgnoringASCIICase(selectionMode, "start"))
254         newSelectionStart = newSelectionEnd = start;
255     else if (equalLettersIgnoringASCIICase(selectionMode, "end"))
256         newSelectionStart = newSelectionEnd = start + replacementLength;
257     else {
258         // Default is "preserve".
259         long delta = replacementLength - (end - start);
260
261         if (newSelectionStart > end)
262             newSelectionStart += delta;
263         else if (newSelectionStart > start)
264             newSelectionStart = start;
265
266         if (newSelectionEnd > end)
267             newSelectionEnd += delta;
268         else if (newSelectionEnd > start)
269             newSelectionEnd = start + replacementLength;
270     }
271
272     setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
273
274     return { };
275 }
276
277 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString, const AXTextStateChangeIntent& intent)
278 {
279     TextFieldSelectionDirection direction = SelectionHasNoDirection;
280     if (directionString == "forward")
281         direction = SelectionHasForwardDirection;
282     else if (directionString == "backward")
283         direction = SelectionHasBackwardDirection;
284
285     return setSelectionRange(start, end, direction, SelectionRevealMode::DoNotReveal, intent);
286 }
287
288 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, SelectionRevealMode revealMode, const AXTextStateChangeIntent& intent)
289 {
290     if (!isTextField())
291         return;
292
293     end = std::max(end, 0);
294     start = std::min(std::max(start, 0), end);
295
296     auto innerText = innerTextElement();
297     bool hasFocus = document().focusedElement() == this;
298     if (!hasFocus && innerText) {
299         // FIXME: Removing this synchronous layout requires fixing setSelectionWithoutUpdatingAppearance not needing up-to-date style.
300         document().updateLayoutIgnorePendingStylesheets();
301
302         // Double-check the state of innerTextElement after the layout.
303         innerText = innerTextElement();
304         auto* rendererTextControl = renderer();
305
306         if (innerText && rendererTextControl) {
307             if (rendererTextControl->style().visibility() == HIDDEN || !innerText->renderBox() || !innerText->renderBox()->height()) {
308                 cacheSelection(start, end, direction);
309                 return;
310             }
311         }
312     }
313
314     Position startPosition = positionForIndex(innerText.get(), start);
315     Position endPosition;
316     if (start == end)
317         endPosition = startPosition;
318     else {
319         if (direction == SelectionHasBackwardDirection) {
320             endPosition = startPosition;
321             startPosition = positionForIndex(innerText.get(), end);
322         } else
323             endPosition = positionForIndex(innerText.get(), end);
324     }
325
326     if (RefPtr<Frame> frame = document().frame())
327         frame->selection().moveWithoutValidationTo(startPosition, endPosition, direction != SelectionHasNoDirection, !hasFocus, revealMode, intent);
328 }
329
330 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& position) const
331 {
332     auto innerText = innerTextElement();
333     if (!innerText || !innerText->contains(position.deepEquivalent().anchorNode()))
334         return 0;
335     unsigned index = indexForPosition(position.deepEquivalent());
336     ASSERT(VisiblePosition(positionForIndex(innerTextElement().get(), index)) == position);
337     return index;
338 }
339
340 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
341 {
342     VisiblePosition position = positionForIndex(innerTextElement().get(), index);
343     ASSERT(indexForVisiblePosition(position) == index);
344     return position;
345 }
346
347 int HTMLTextFormControlElement::selectionStart() const
348 {
349     if (!isTextField())
350         return 0;
351     if (document().focusedElement() != this && hasCachedSelection())
352         return m_cachedSelectionStart;
353
354     return computeSelectionStart();
355 }
356
357 int HTMLTextFormControlElement::computeSelectionStart() const
358 {
359     ASSERT(isTextField());
360     RefPtr<Frame> frame = document().frame();
361     if (!frame)
362         return 0;
363
364     return indexForPosition(frame->selection().selection().start());
365 }
366
367 int HTMLTextFormControlElement::selectionEnd() const
368 {
369     if (!isTextField())
370         return 0;
371     if (document().focusedElement() != this && hasCachedSelection())
372         return m_cachedSelectionEnd;
373     return computeSelectionEnd();
374 }
375
376 int HTMLTextFormControlElement::computeSelectionEnd() const
377 {
378     ASSERT(isTextField());
379     RefPtr<Frame> frame = document().frame();
380     if (!frame)
381         return 0;
382
383     return indexForPosition(frame->selection().selection().end());
384 }
385
386 static const AtomicString& directionString(TextFieldSelectionDirection direction)
387 {
388     static NeverDestroyed<const AtomicString> none("none", AtomicString::ConstructFromLiteral);
389     static NeverDestroyed<const AtomicString> forward("forward", AtomicString::ConstructFromLiteral);
390     static NeverDestroyed<const AtomicString> backward("backward", AtomicString::ConstructFromLiteral);
391
392     switch (direction) {
393     case SelectionHasNoDirection:
394         return none;
395     case SelectionHasForwardDirection:
396         return forward;
397     case SelectionHasBackwardDirection:
398         return backward;
399     }
400
401     ASSERT_NOT_REACHED();
402     return none;
403 }
404
405 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
406 {
407     if (!isTextField())
408         return directionString(SelectionHasNoDirection);
409     if (document().focusedElement() != this && hasCachedSelection())
410         return directionString(cachedSelectionDirection());
411
412     return directionString(computeSelectionDirection());
413 }
414
415 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
416 {
417     ASSERT(isTextField());
418     RefPtr<Frame> frame = document().frame();
419     if (!frame)
420         return SelectionHasNoDirection;
421
422     const VisibleSelection& selection = frame->selection().selection();
423     return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
424 }
425
426 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
427 {
428     if (node->isTextNode()) {
429         containerNode = node;
430         offsetInContainer = offset;
431     } else {
432         containerNode = node->parentNode();
433         offsetInContainer = node->computeNodeIndex() + offset;
434     }
435 }
436
437 RefPtr<Range> HTMLTextFormControlElement::selection() const
438 {
439     if (!renderer() || !isTextField() || !hasCachedSelection())
440         return nullptr;
441
442     int start = m_cachedSelectionStart;
443     int end = m_cachedSelectionEnd;
444
445     ASSERT(start <= end);
446     auto innerText = innerTextElement();
447     if (!innerText)
448         return nullptr;
449
450     if (!innerText->firstChild())
451         return Range::create(document(), innerText, 0, innerText, 0);
452
453     int offset = 0;
454     Node* startNode = nullptr;
455     Node* endNode = nullptr;
456     for (RefPtr<Node> node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText.get())) {
457         ASSERT(!node->firstChild());
458         ASSERT(node->isTextNode() || node->hasTagName(brTag));
459         int length = node->isTextNode() ? lastOffsetInNode(node.get()) : 1;
460
461         if (offset <= start && start <= offset + length)
462             setContainerAndOffsetForRange(node.get(), start - offset, startNode, start);
463
464         if (offset <= end && end <= offset + length) {
465             setContainerAndOffsetForRange(node.get(), end - offset, endNode, end);
466             break;
467         }
468
469         offset += length;
470     }
471
472     if (!startNode || !endNode)
473         return nullptr;
474
475     return Range::create(document(), startNode, start, endNode, end);
476 }
477
478 void HTMLTextFormControlElement::restoreCachedSelection(SelectionRevealMode revealMode, const AXTextStateChangeIntent& intent)
479 {
480     setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, cachedSelectionDirection(), revealMode, intent);
481 }
482
483 void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
484 {
485     if (!isTextField())
486         return;
487
488     // FIXME: Don't re-compute selection start and end if this function was called inside setSelectionRange.
489     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
490     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
491     
492     if (shouldFireSelectEvent && m_cachedSelectionStart != m_cachedSelectionEnd)
493         dispatchEvent(Event::create(eventNames().selectEvent, true, false));
494 }
495
496 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
497 {
498     if (name == placeholderAttr) {
499         updatePlaceholderText();
500         updatePlaceholderVisibility();
501     } else
502         HTMLFormControlElementWithState::parseAttribute(name, value);
503 }
504
505 void HTMLTextFormControlElement::disabledStateChanged()
506 {
507     HTMLFormControlElementWithState::disabledStateChanged();
508     updateInnerTextElementEditability();
509 }
510
511 void HTMLTextFormControlElement::readOnlyAttributeChanged()
512 {
513     HTMLFormControlElementWithState::disabledAttributeChanged();
514     updateInnerTextElementEditability();
515 }
516
517 bool HTMLTextFormControlElement::isInnerTextElementEditable() const
518 {
519     return !isDisabledOrReadOnly();
520 }
521
522 void HTMLTextFormControlElement::updateInnerTextElementEditability()
523 {
524     if (auto innerText = innerTextElement()) {
525         auto value = isInnerTextElementEditable() ? AtomicString { "plaintext-only", AtomicString::ConstructFromLiteral } : AtomicString { "false", AtomicString::ConstructFromLiteral };
526         innerText->setAttributeWithoutSynchronization(contenteditableAttr, value);
527     }
528 }
529
530 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
531 {
532     if (!isTextField())
533         return false;
534     return m_lastChangeWasUserEdit;
535 }
536
537 static void stripTrailingNewline(StringBuilder& result)
538 {
539     // Remove one trailing newline; there's always one that's collapsed out by rendering.
540     size_t size = result.length();
541     if (size && result[size - 1] == newlineCharacter)
542         result.resize(size - 1);
543 }
544
545 static String innerTextValueFrom(TextControlInnerTextElement& innerText)
546 {
547     StringBuilder result;
548     for (RefPtr<Node> node = innerText.firstChild(); node; node = NodeTraversal::next(*node, &innerText)) {
549         if (is<HTMLBRElement>(*node))
550             result.append(newlineCharacter);
551         else if (is<Text>(*node))
552             result.append(downcast<Text>(*node).data());
553     }
554     stripTrailingNewline(result);
555     return result.toString();
556 }
557
558 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
559 {
560     LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::PerformanceOptimization);
561     auto innerText = innerTextElement();
562     if (!innerText)
563         return;
564
565     ASSERT(isTextField());
566     String previousValue = innerTextValueFrom(*innerText);
567     bool textIsChanged = value != previousValue;
568     if (textIsChanged || !innerText->hasChildNodes()) {
569 #if HAVE(ACCESSIBILITY) && !PLATFORM(COCOA)
570         if (textIsChanged && renderer()) {
571             if (AXObjectCache* cache = document().existingAXObjectCache())
572                 cache->postNotification(this, AXObjectCache::AXValueChanged, TargetObservableParent);
573         }
574 #endif
575
576         {
577             // Events dispatched on the inner text element cannot execute arbitrary author scripts.
578             ScriptDisallowedScope::EventAllowedScope allowedScope(*userAgentShadowRoot());
579
580             innerText->setInnerText(value);
581
582             if (value.endsWith('\n') || value.endsWith('\r'))
583                 innerText->appendChild(HTMLBRElement::create(document()));
584         }
585
586 #if HAVE(ACCESSIBILITY) && PLATFORM(COCOA)
587         if (textIsChanged && renderer()) {
588             if (AXObjectCache* cache = document().existingAXObjectCache())
589                 cache->deferTextReplacementNotificationForTextControl(*this, previousValue);
590         }
591 #endif
592     }
593
594     setFormControlValueMatchesRenderer(true);
595 }
596
597 String HTMLTextFormControlElement::innerTextValue() const
598 {
599     auto innerText = innerTextElement();
600     return innerText ? innerTextValueFrom(*innerText) : emptyString();
601 }
602
603 static Position positionForIndex(TextControlInnerTextElement* innerText, unsigned index)
604 {
605     unsigned remainingCharactersToMoveForward = index;
606     RefPtr<Node> lastBrOrText = innerText;
607     for (RefPtr<Node> node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
608         if (node->hasTagName(brTag)) {
609             if (!remainingCharactersToMoveForward)
610                 return positionBeforeNode(node.get());
611             remainingCharactersToMoveForward--;
612             lastBrOrText = node;
613         } else if (is<Text>(*node)) {
614             Text& text = downcast<Text>(*node);
615             if (remainingCharactersToMoveForward < text.length())
616                 return Position(&text, remainingCharactersToMoveForward);
617             remainingCharactersToMoveForward -= text.length();
618             lastBrOrText = node;
619         }
620     }
621     return lastPositionInOrAfterNode(lastBrOrText.get());
622 }
623
624 unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosition) const
625 {
626     auto innerText = innerTextElement();
627     if (!innerText || !innerText->contains(passedPosition.anchorNode()) || passedPosition.isNull())
628         return 0;
629
630     if (positionBeforeNode(innerText.get()) == passedPosition)
631         return 0;
632
633     unsigned index = 0;
634     RefPtr<Node> startNode = passedPosition.computeNodeBeforePosition();
635     if (!startNode)
636         startNode = passedPosition.containerNode();
637     ASSERT(startNode);
638     ASSERT(innerText->contains(startNode.get()));
639
640     for (RefPtr<Node> node = startNode; node; node = NodeTraversal::previous(*node, innerText.get())) {
641         if (is<Text>(*node)) {
642             unsigned length = downcast<Text>(*node).length();
643             if (node == passedPosition.containerNode())
644                 index += std::min<unsigned>(length, passedPosition.offsetInContainerNode());
645             else
646                 index += length;
647         } else if (is<HTMLBRElement>(*node))
648             ++index;
649     }
650
651     unsigned length = innerTextValue().length();
652     index = std::min(index, length); // FIXME: We shouldn't have to call innerTextValue() just to ignore the last LF. See finishText.
653 #ifndef ASSERT_DISABLED
654     VisiblePosition visiblePosition = passedPosition;
655     unsigned indexComputedByVisiblePosition = 0;
656     if (visiblePosition.isNotNull())
657         indexComputedByVisiblePosition = WebCore::indexForVisiblePosition(innerText, visiblePosition, false /* forSelectionPreservation */);
658     ASSERT(index == indexComputedByVisiblePosition);
659 #endif
660     return index;
661 }
662
663 #if PLATFORM(IOS)
664 void HTMLTextFormControlElement::hidePlaceholder()
665 {
666     if (RefPtr<HTMLElement> placeholder = placeholderElement())
667         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden, true);
668 }
669
670 void HTMLTextFormControlElement::showPlaceholderIfNecessary()
671 {
672     if (RefPtr<HTMLElement> placeholder = placeholderElement())
673         placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, true);
674 }
675 #endif
676
677 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
678 {
679     RootInlineBox* next;
680     for (; line; line = next) {
681         next = line->nextRootBox();
682         if (next && !line->endsWithBreak()) {
683             ASSERT(line->lineBreakObj());
684             breakNode = line->lineBreakObj()->node();
685             breakOffset = line->lineBreakPos();
686             line = next;
687             return;
688         }
689     }
690     breakNode = 0;
691     breakOffset = 0;
692 }
693
694 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
695 {
696     // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
697     // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
698     if (!isTextField())
699         return value();
700
701     auto innerText = innerTextElement();
702     if (!innerText)
703         return value();
704
705     RenderTextControlInnerBlock* renderer = innerText->renderer();
706     if (!renderer)
707         return value();
708
709     Node* breakNode;
710     unsigned breakOffset;
711     RootInlineBox* line = renderer->firstRootBox();
712     if (!line)
713         return value();
714
715     getNextSoftBreak(line, breakNode, breakOffset);
716
717     StringBuilder result;
718     for (RefPtr<Node> node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText.get())) {
719         if (is<HTMLBRElement>(*node))
720             result.append(newlineCharacter);
721         else if (is<Text>(*node)) {
722             String data = downcast<Text>(*node).data();
723             unsigned length = data.length();
724             unsigned position = 0;
725             while (breakNode == node && breakOffset <= length) {
726                 if (breakOffset > position) {
727                     result.append(data, position, breakOffset - position);
728                     position = breakOffset;
729                     result.append(newlineCharacter);
730                 }
731                 getNextSoftBreak(line, breakNode, breakOffset);
732             }
733             result.append(data, position, length - position);
734         }
735         while (breakNode == node)
736             getNextSoftBreak(line, breakNode, breakOffset);
737     }
738     stripTrailingNewline(result);
739     return result.toString();
740 }
741
742 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
743 {
744     ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
745         || position.containerNode() || !position.anchorNode()->shadowHost()
746         || hasShadowRootParent(*position.anchorNode()));
747         
748     RefPtr<Node> container = position.containerNode();
749     if (!container)
750         return nullptr;
751     RefPtr<Element> ancestor = container->shadowHost();
752     return ancestor && ancestor->isTextField() ? downcast<HTMLTextFormControlElement>(ancestor.get()) : nullptr;
753 }
754
755 static const Element* parentHTMLElement(const Element* element)
756 {
757     while (element) {
758         element = element->parentElement();
759         if (element && element->isHTMLElement())
760             return element;
761     }
762     return 0;
763 }
764
765 String HTMLTextFormControlElement::directionForFormData() const
766 {
767     for (const Element* element = this; element; element = parentHTMLElement(element)) {
768         const AtomicString& dirAttributeValue = element->attributeWithoutSynchronization(dirAttr);
769         if (dirAttributeValue.isNull())
770             continue;
771
772         if (equalLettersIgnoringASCIICase(dirAttributeValue, "rtl") || equalLettersIgnoringASCIICase(dirAttributeValue, "ltr"))
773             return dirAttributeValue;
774
775         if (equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) {
776             bool isAuto;
777             TextDirection textDirection = static_cast<const HTMLElement*>(element)->directionalityIfhasDirAutoAttribute(isAuto);
778             return textDirection == RTL ? "rtl" : "ltr";
779         }
780     }
781
782     return "ltr";
783 }
784
785 ExceptionOr<void> HTMLTextFormControlElement::setMaxLength(int maxLength)
786 {
787     if (maxLength < 0 || (m_minLength >= 0 && maxLength < m_minLength))
788         return Exception { IndexSizeError };
789     setIntegralAttribute(maxlengthAttr, maxLength);
790     return { };
791 }
792
793 ExceptionOr<void> HTMLTextFormControlElement::setMinLength(int minLength)
794 {
795     if (minLength < 0 || (m_maxLength >= 0 && minLength > m_maxLength))
796         return Exception { IndexSizeError };
797     setIntegralAttribute(minlengthAttr, minLength);
798     return { };
799 }
800
801 void HTMLTextFormControlElement::adjustInnerTextStyle(const RenderStyle& parentStyle, RenderStyle& textBlockStyle) const
802 {
803     // The inner block, if present, always has its direction set to LTR,
804     // so we need to inherit the direction and unicode-bidi style from the element.
805     textBlockStyle.setDirection(parentStyle.direction());
806     textBlockStyle.setUnicodeBidi(parentStyle.unicodeBidi());
807
808     if (auto innerText = innerTextElement()) {
809         if (const StyleProperties* properties = innerText->presentationAttributeStyle()) {
810             RefPtr<CSSValue> value = properties->getPropertyCSSValue(CSSPropertyWebkitUserModify);
811             if (is<CSSPrimitiveValue>(value))
812                 textBlockStyle.setUserModify(downcast<CSSPrimitiveValue>(*value));
813         }
814     }
815
816     if (isDisabledFormControl())
817         textBlockStyle.setColor(RenderTheme::singleton().disabledTextColor(textBlockStyle.visitedDependentColorWithColorFilter(CSSPropertyColor), parentStyle.visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor)));
818 #if PLATFORM(IOS)
819     if (textBlockStyle.textSecurity() != TSNONE && !textBlockStyle.isLeftToRightDirection()) {
820         // Preserve the alignment but force the direction to LTR so that the last-typed, unmasked character
821         // (which cannot have RTL directionality) will appear to the right of the masked characters. See <rdar://problem/7024375>.
822         
823         switch (textBlockStyle.textAlign()) {
824         case TASTART:
825         case JUSTIFY:
826             textBlockStyle.setTextAlign(RIGHT);
827             break;
828         case TAEND:
829             textBlockStyle.setTextAlign(LEFT);
830             break;
831         case LEFT:
832         case RIGHT:
833         case CENTER:
834         case WEBKIT_LEFT:
835         case WEBKIT_RIGHT:
836         case WEBKIT_CENTER:
837             break;
838         }
839
840         textBlockStyle.setDirection(LTR);
841     }
842 #endif
843 }
844
845 } // namespace Webcore