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