Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 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 "core/html/HTMLTextFormControlElement.h"
27
28 #include "HTMLNames.h"
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/accessibility/AXObjectCache.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/dom/Text.h"
35 #include "core/dom/shadow/ShadowRoot.h"
36 #include "core/editing/FrameSelection.h"
37 #include "core/editing/TextIterator.h"
38 #include "core/events/Event.h"
39 #include "core/frame/LocalFrame.h"
40 #include "core/frame/UseCounter.h"
41 #include "core/html/HTMLBRElement.h"
42 #include "core/html/shadow/ShadowElementNames.h"
43 #include "core/rendering/RenderBlock.h"
44 #include "core/rendering/RenderTheme.h"
45 #include "platform/heap/Handle.h"
46 #include "wtf/text/StringBuilder.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51 using namespace std;
52
53 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& doc, HTMLFormElement* form)
54     : HTMLFormControlElementWithState(tagName, doc, form)
55     , m_lastChangeWasUserEdit(false)
56     , m_cachedSelectionStart(0)
57     , m_cachedSelectionEnd(0)
58     , m_cachedSelectionDirection(SelectionHasNoDirection)
59 {
60 }
61
62 HTMLTextFormControlElement::~HTMLTextFormControlElement()
63 {
64 }
65
66 Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode* insertionPoint)
67 {
68     HTMLFormControlElementWithState::insertedInto(insertionPoint);
69     if (!insertionPoint->inDocument())
70         return InsertionDone;
71     String initialValue = value();
72     setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
73     return InsertionDone;
74 }
75
76 void HTMLTextFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
77 {
78     if (supportsPlaceholder())
79         updatePlaceholderVisibility(false);
80     handleFocusEvent(oldFocusedElement, type);
81     HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, type);
82 }
83
84 void HTMLTextFormControlElement::dispatchBlurEvent(Element* newFocusedElement)
85 {
86     if (supportsPlaceholder())
87         updatePlaceholderVisibility(false);
88     handleBlurEvent();
89     HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
90 }
91
92 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
93 {
94     if (event->type() == EventTypeNames::webkitEditableContentChanged && renderer() && renderer()->isTextControl()) {
95         m_lastChangeWasUserEdit = true;
96         subtreeHasChanged();
97         return;
98     }
99
100     HTMLFormControlElementWithState::defaultEventHandler(event);
101 }
102
103 void HTMLTextFormControlElement::forwardEvent(Event* event)
104 {
105     if (event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)
106         return;
107     innerTextElement()->defaultEventHandler(event);
108 }
109
110 String HTMLTextFormControlElement::strippedPlaceholder() const
111 {
112     // According to the HTML5 specification, we need to remove CR and LF from
113     // the attribute value.
114     const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
115     if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
116         return attributeValue;
117
118     StringBuilder stripped;
119     unsigned length = attributeValue.length();
120     stripped.reserveCapacity(length);
121     for (unsigned i = 0; i < length; ++i) {
122         UChar character = attributeValue[i];
123         if (character == newlineCharacter || character == carriageReturn)
124             continue;
125         stripped.append(character);
126     }
127     return stripped.toString();
128 }
129
130 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
131
132 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
133 {
134     const AtomicString& attributeValue = fastGetAttribute(placeholderAttr);
135     return attributeValue.string().find(isNotLineBreak) == kNotFound;
136 }
137
138 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
139 {
140     return supportsPlaceholder()
141         && isEmptyValue()
142         && isEmptySuggestedValue()
143         && !isPlaceholderEmpty()
144         && (document().focusedElement() != this || (RenderTheme::theme().shouldShowPlaceholderWhenFocused()))
145         && (!renderer() || renderer()->style()->visibility() == VISIBLE);
146 }
147
148 HTMLElement* HTMLTextFormControlElement::placeholderElement() const
149 {
150     return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::placeholder()));
151 }
152
153 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
154 {
155     if (!supportsPlaceholder())
156         return;
157     if (!placeholderElement() || placeholderValueChanged)
158         updatePlaceholderText();
159     HTMLElement* placeholder = placeholderElement();
160     if (!placeholder)
161         return;
162     placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? CSSValueVisible : CSSValueHidden);
163 }
164
165 void HTMLTextFormControlElement::setSelectionStart(int start)
166 {
167     setSelectionRange(start, max(start, selectionEnd()), selectionDirection());
168 }
169
170 void HTMLTextFormControlElement::setSelectionEnd(int end)
171 {
172     setSelectionRange(min(end, selectionStart()), end, selectionDirection());
173 }
174
175 void HTMLTextFormControlElement::setSelectionDirection(const String& direction)
176 {
177     setSelectionRange(selectionStart(), selectionEnd(), direction);
178 }
179
180 void HTMLTextFormControlElement::select()
181 {
182     setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection);
183 }
184
185 bool HTMLTextFormControlElement::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
186 {
187     return !equalIgnoringNullity(oldValue, newValue);
188 }
189
190 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
191 {
192     String newValue = value();
193     if (shouldDispatchFormControlChangeEvent(m_textAsOfLastFormControlChangeEvent, newValue)) {
194         dispatchChangeEvent();
195         setTextAsOfLastFormControlChangeEvent(newValue);
196     }
197     setChangedSinceLastFormControlChangeEvent(false);
198 }
199
200 static inline bool hasVisibleTextArea(RenderObject* renderer, HTMLElement* innerText)
201 {
202     ASSERT(renderer);
203     return renderer->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height();
204 }
205
206
207 void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionState& exceptionState)
208 {
209     setRangeText(replacement, selectionStart(), selectionEnd(), String(), exceptionState);
210 }
211
212 void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState)
213 {
214     if (start > end) {
215         exceptionState.throwDOMException(IndexSizeError, "The provided start value (" + String::number(start) + ") is larger than the provided end value (" + String::number(end) + ").");
216         return;
217     }
218     if (hasAuthorShadowRoot())
219         return;
220
221     String text = innerTextValue();
222     unsigned textLength = text.length();
223     unsigned replacementLength = replacement.length();
224     unsigned newSelectionStart = selectionStart();
225     unsigned newSelectionEnd = selectionEnd();
226
227     start = std::min(start, textLength);
228     end = std::min(end, textLength);
229
230     if (start < end)
231         text.replace(start, end - start, replacement);
232     else
233         text.insert(replacement, start);
234
235     setInnerTextValue(text);
236
237     // FIXME: What should happen to the value (as in value()) if there's no renderer?
238     if (!renderer())
239         return;
240
241     subtreeHasChanged();
242
243     if (equalIgnoringCase(selectionMode, "select")) {
244         newSelectionStart = start;
245         newSelectionEnd = start + replacementLength;
246     } else if (equalIgnoringCase(selectionMode, "start"))
247         newSelectionStart = newSelectionEnd = start;
248     else if (equalIgnoringCase(selectionMode, "end"))
249         newSelectionStart = newSelectionEnd = start + replacementLength;
250     else {
251         // Default is "preserve".
252         long delta = replacementLength - (end - start);
253
254         if (newSelectionStart > end)
255             newSelectionStart += delta;
256         else if (newSelectionStart > start)
257             newSelectionStart = start;
258
259         if (newSelectionEnd > end)
260             newSelectionEnd += delta;
261         else if (newSelectionEnd > start)
262             newSelectionEnd = start + replacementLength;
263     }
264
265     setSelectionRange(newSelectionStart, newSelectionEnd, SelectionHasNoDirection);
266 }
267
268 void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString)
269 {
270     TextFieldSelectionDirection direction = SelectionHasNoDirection;
271     if (directionString == "forward")
272         direction = SelectionHasForwardDirection;
273     else if (directionString == "backward")
274         direction = SelectionHasBackwardDirection;
275
276     return setSelectionRange(start, end, direction);
277 }
278
279 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction)
280 {
281     document().updateLayoutIgnorePendingStylesheets();
282
283     if (!renderer() || !renderer()->isTextControl())
284         return;
285
286     end = max(end, 0);
287     start = min(max(start, 0), end);
288
289     if (!hasVisibleTextArea(renderer(), innerTextElement())) {
290         cacheSelection(start, end, direction);
291         return;
292     }
293     VisiblePosition startPosition = visiblePositionForIndex(start);
294     VisiblePosition endPosition;
295     if (start == end)
296         endPosition = startPosition;
297     else
298         endPosition = visiblePositionForIndex(end);
299
300     // startPosition and endPosition can be null position for example when
301     // "-webkit-user-select: none" style attribute is specified.
302     if (startPosition.isNotNull() && endPosition.isNotNull()) {
303         ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowHost() == this
304             && endPosition.deepEquivalent().deprecatedNode()->shadowHost() == this);
305     }
306     VisibleSelection newSelection;
307     if (direction == SelectionHasBackwardDirection)
308         newSelection = VisibleSelection(endPosition, startPosition);
309     else
310         newSelection = VisibleSelection(startPosition, endPosition);
311     newSelection.setIsDirectional(direction != SelectionHasNoDirection);
312
313     if (LocalFrame* frame = document().frame())
314         frame->selection().setSelection(newSelection);
315 }
316
317 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
318 {
319     if (index <= 0)
320         return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
321     RefPtrWillBeRawPtr<Range> range = Range::create(document());
322     range->selectNodeContents(innerTextElement(), ASSERT_NO_EXCEPTION);
323     CharacterIterator it(range.get());
324     it.advance(index - 1);
325     return VisiblePosition(it.range()->endPosition(), UPSTREAM);
326 }
327
328 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const
329 {
330     Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent();
331     if (enclosingTextFormControl(indexPosition) != this)
332         return 0;
333     ASSERT(indexPosition.document());
334     RefPtrWillBeRawPtr<Range> range = Range::create(*indexPosition.document());
335     range->setStart(innerTextElement(), 0, ASSERT_NO_EXCEPTION);
336     range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ASSERT_NO_EXCEPTION);
337     return TextIterator::rangeLength(range.get());
338 }
339
340 int HTMLTextFormControlElement::selectionStart() const
341 {
342     if (!isTextFormControl())
343         return 0;
344     if (document().focusedElement() != this)
345         return m_cachedSelectionStart;
346
347     return computeSelectionStart();
348 }
349
350 int HTMLTextFormControlElement::computeSelectionStart() const
351 {
352     ASSERT(isTextFormControl());
353     LocalFrame* frame = document().frame();
354     if (!frame)
355         return 0;
356
357     return indexForVisiblePosition(VisiblePosition(frame->selection().start()));
358 }
359
360 int HTMLTextFormControlElement::selectionEnd() const
361 {
362     if (!isTextFormControl())
363         return 0;
364     if (document().focusedElement() != this)
365         return m_cachedSelectionEnd;
366     return computeSelectionEnd();
367 }
368
369 int HTMLTextFormControlElement::computeSelectionEnd() const
370 {
371     ASSERT(isTextFormControl());
372     LocalFrame* frame = document().frame();
373     if (!frame)
374         return 0;
375
376     return indexForVisiblePosition(VisiblePosition(frame->selection().end()));
377 }
378
379 static const AtomicString& directionString(TextFieldSelectionDirection direction)
380 {
381     DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral));
382     DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward", AtomicString::ConstructFromLiteral));
383     DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward", AtomicString::ConstructFromLiteral));
384
385     switch (direction) {
386     case SelectionHasNoDirection:
387         return none;
388     case SelectionHasForwardDirection:
389         return forward;
390     case SelectionHasBackwardDirection:
391         return backward;
392     }
393
394     ASSERT_NOT_REACHED();
395     return none;
396 }
397
398 const AtomicString& HTMLTextFormControlElement::selectionDirection() const
399 {
400     if (!isTextFormControl())
401         return directionString(SelectionHasNoDirection);
402     if (document().focusedElement() != this)
403         return directionString(m_cachedSelectionDirection);
404
405     return directionString(computeSelectionDirection());
406 }
407
408 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const
409 {
410     ASSERT(isTextFormControl());
411     LocalFrame* frame = document().frame();
412     if (!frame)
413         return SelectionHasNoDirection;
414
415     const VisibleSelection& selection = frame->selection().selection();
416     return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection;
417 }
418
419 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer)
420 {
421     if (node->isTextNode()) {
422         containerNode = node;
423         offsetInContainer = offset;
424     } else {
425         containerNode = node->parentNode();
426         offsetInContainer = node->nodeIndex() + offset;
427     }
428 }
429
430 PassRefPtrWillBeRawPtr<Range> HTMLTextFormControlElement::selection() const
431 {
432     if (!renderer() || !isTextFormControl())
433         return nullptr;
434
435     int start = m_cachedSelectionStart;
436     int end = m_cachedSelectionEnd;
437
438     ASSERT(start <= end);
439     HTMLElement* innerText = innerTextElement();
440     if (!innerText)
441         return nullptr;
442
443     if (!innerText->firstChild())
444         return Range::create(document(), innerText, 0, innerText, 0);
445
446     int offset = 0;
447     Node* startNode = 0;
448     Node* endNode = 0;
449     for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
450         ASSERT(!node->firstChild());
451         ASSERT(node->isTextNode() || isHTMLBRElement(*node));
452         int length = node->isTextNode() ? lastOffsetInNode(node) : 1;
453
454         if (offset <= start && start <= offset + length)
455             setContainerAndOffsetForRange(node, start - offset, startNode, start);
456
457         if (offset <= end && end <= offset + length) {
458             setContainerAndOffsetForRange(node, end - offset, endNode, end);
459             break;
460         }
461
462         offset += length;
463     }
464
465     if (!startNode || !endNode)
466         return nullptr;
467
468     return Range::create(document(), startNode, start, endNode, end);
469 }
470
471 void HTMLTextFormControlElement::restoreCachedSelection()
472 {
473     setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
474 }
475
476 void HTMLTextFormControlElement::selectionChanged(bool userTriggered)
477 {
478     if (!renderer() || !isTextFormControl())
479         return;
480
481     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
482     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
483
484     if (LocalFrame* frame = document().frame()) {
485         if (frame->selection().isRange() && userTriggered)
486             dispatchEvent(Event::createBubble(EventTypeNames::select));
487     }
488 }
489
490 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
491 {
492     if (name == placeholderAttr) {
493         updatePlaceholderVisibility(true);
494         UseCounter::count(document(), UseCounter::PlaceholderAttribute);
495     } else
496         HTMLFormControlElementWithState::parseAttribute(name, value);
497 }
498
499 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const
500 {
501     if (!isTextFormControl())
502         return false;
503     return m_lastChangeWasUserEdit;
504 }
505
506 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
507 {
508     ASSERT(!hasAuthorShadowRoot());
509     if (!isTextFormControl() || hasAuthorShadowRoot())
510         return;
511
512     bool textIsChanged = value != innerTextValue();
513     if (textIsChanged || !innerTextElement()->hasChildren()) {
514         if (textIsChanged && renderer()) {
515             if (AXObjectCache* cache = document().existingAXObjectCache())
516                 cache->postNotification(this, AXObjectCache::AXValueChanged, false);
517         }
518         innerTextElement()->setInnerText(value, ASSERT_NO_EXCEPTION);
519
520         if (value.endsWith('\n') || value.endsWith('\r'))
521             innerTextElement()->appendChild(HTMLBRElement::create(document()));
522     }
523 }
524
525 static String finishText(StringBuilder& result)
526 {
527     // Remove one trailing newline; there's always one that's collapsed out by rendering.
528     size_t size = result.length();
529     if (size && result[size - 1] == '\n')
530         result.resize(--size);
531     return result.toString();
532 }
533
534 String HTMLTextFormControlElement::innerTextValue() const
535 {
536     ASSERT(!hasAuthorShadowRoot());
537     HTMLElement* innerText = innerTextElement();
538     if (!innerText || !isTextFormControl())
539         return emptyString();
540
541     StringBuilder result;
542     for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
543         if (isHTMLBRElement(*node))
544             result.append(newlineCharacter);
545         else if (node->isTextNode())
546             result.append(toText(node)->data());
547     }
548     return finishText(result);
549 }
550
551 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
552 {
553     RootInlineBox* next;
554     for (; line; line = next) {
555         next = line->nextRootBox();
556         if (next && !line->endsWithBreak()) {
557             ASSERT(line->lineBreakObj());
558             breakNode = line->lineBreakObj()->node();
559             breakOffset = line->lineBreakPos();
560             line = next;
561             return;
562         }
563     }
564     breakNode = 0;
565     breakOffset = 0;
566 }
567
568 String HTMLTextFormControlElement::valueWithHardLineBreaks() const
569 {
570     // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer.
571     // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
572     HTMLElement* innerText = innerTextElement();
573     if (!innerText || !isTextFormControl())
574         return value();
575
576     RenderBlock* renderer = toRenderBlock(innerText->renderer());
577     if (!renderer)
578         return value();
579
580     Node* breakNode;
581     unsigned breakOffset;
582     RootInlineBox* line = renderer->firstRootBox();
583     if (!line)
584         return value();
585
586     getNextSoftBreak(line, breakNode, breakOffset);
587
588     StringBuilder result;
589     for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) {
590         if (isHTMLBRElement(*node))
591             result.append(newlineCharacter);
592         else if (node->isTextNode()) {
593             String data = toText(node)->data();
594             unsigned length = data.length();
595             unsigned position = 0;
596             while (breakNode == node && breakOffset <= length) {
597                 if (breakOffset > position) {
598                     result.append(data, position, breakOffset - position);
599                     position = breakOffset;
600                     result.append(newlineCharacter);
601                 }
602                 getNextSoftBreak(line, breakNode, breakOffset);
603             }
604             result.append(data, position, length - position);
605         }
606         while (breakNode == node)
607             getNextSoftBreak(line, breakNode, breakOffset);
608     }
609     return finishText(result);
610 }
611
612 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position)
613 {
614     ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor
615         || position.containerNode() || !position.anchorNode()->shadowHost()
616         || (position.anchorNode()->parentNode() && position.anchorNode()->parentNode()->isShadowRoot()));
617
618     Node* container = position.containerNode();
619     if (!container)
620         return 0;
621     Element* ancestor = container->shadowHost();
622     return ancestor && isHTMLTextFormControlElement(*ancestor) && container->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? toHTMLTextFormControlElement(ancestor) : 0;
623 }
624
625 static const HTMLElement* parentHTMLElement(const Element* element)
626 {
627     while (element) {
628         element = element->parentElement();
629         if (element && element->isHTMLElement())
630             return toHTMLElement(element);
631     }
632     return 0;
633 }
634
635 String HTMLTextFormControlElement::directionForFormData() const
636 {
637     for (const HTMLElement* element = this; element; element = parentHTMLElement(element)) {
638         const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr);
639         if (dirAttributeValue.isNull())
640             continue;
641
642         if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr"))
643             return dirAttributeValue;
644
645         if (equalIgnoringCase(dirAttributeValue, "auto")) {
646             bool isAuto;
647             TextDirection textDirection = element->directionalityIfhasDirAutoAttribute(isAuto);
648             return textDirection == RTL ? "rtl" : "ltr";
649         }
650     }
651
652     return "ltr";
653 }
654
655 HTMLElement* HTMLTextFormControlElement::innerTextElement() const
656 {
657     return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor()));
658 }
659
660 } // namespace Webcore