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 "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/events/ThreadLocalEventNames.h" 40 #include "core/html/HTMLBRElement.h" 41 #include "core/html/shadow/ShadowElementNames.h" 42 #include "core/frame/Frame.h" 43 #include "core/frame/UseCounter.h" 44 #include "core/rendering/RenderBlock.h" 45 #include "core/rendering/RenderTheme.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(-1) 57 , m_cachedSelectionEnd(-1) 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, FocusDirection direction) 77 { 78 if (supportsPlaceholder()) 79 updatePlaceholderVisibility(false); 80 handleFocusEvent(oldFocusedElement, direction); 81 HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, direction); 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 String HTMLTextFormControlElement::selectedText() const 186 { 187 if (!isTextFormControl()) 188 return String(); 189 return value().substring(selectionStart(), selectionEnd() - selectionStart()); 190 } 191 192 void HTMLTextFormControlElement::dispatchFormControlChangeEvent() 193 { 194 if (m_textAsOfLastFormControlChangeEvent != value()) { 195 HTMLElement::dispatchChangeEvent(); 196 setTextAsOfLastFormControlChangeEvent(value()); 197 } 198 setChangedSinceLastFormControlChangeEvent(false); 199 } 200 201 static inline bool hasVisibleTextArea(RenderObject* renderer, HTMLElement* innerText) 202 { 203 ASSERT(renderer); 204 return renderer->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height(); 205 } 206 207 208 void HTMLTextFormControlElement::setRangeText(const String& replacement, ExceptionState& exceptionState) 209 { 210 setRangeText(replacement, selectionStart(), selectionEnd(), String(), exceptionState); 211 } 212 213 void HTMLTextFormControlElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState) 214 { 215 if (start > end) { 216 exceptionState.throwDOMException(IndexSizeError, "The provided start value (" + String::number(start) + ") is larger than the provided end value (" + String::number(end) + ")."); 217 return; 218 } 219 220 String text = innerTextValue(); 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 setInnerTextValue(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 = max(end, 0); 286 start = min(max(start, 0), end); 287 288 if (!hasVisibleTextArea(renderer(), innerTextElement())) { 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 (Frame* 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(innerTextElement()), DOWNSTREAM); 320 RefPtr<Range> range = Range::create(document()); 321 range->selectNodeContents(innerTextElement(), 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 RefPtr<Range> range = Range::create(*indexPosition.document()); 334 range->setStart(innerTextElement(), 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 && 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 indexForVisiblePosition(frame->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 indexForVisiblePosition(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 && hasCachedSelection()) 402 return directionString(m_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->nodeIndex() + offset; 426 } 427 } 428 429 PassRefPtr<Range> HTMLTextFormControlElement::selection() const 430 { 431 if (!renderer() || !isTextFormControl() || !hasCachedSelection()) 432 return 0; 433 434 int start = m_cachedSelectionStart; 435 int end = m_cachedSelectionEnd; 436 437 ASSERT(start <= end); 438 HTMLElement* innerText = innerTextElement(); 439 if (!innerText) 440 return 0; 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() || 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 0; 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 (Frame* 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::setInnerTextValue(const String& value) 506 { 507 if (!isTextFormControl()) 508 return; 509 510 bool textIsChanged = value != innerTextValue(); 511 if (textIsChanged || !innerTextElement()->hasChildNodes()) { 512 if (textIsChanged && renderer()) { 513 if (AXObjectCache* cache = document().existingAXObjectCache()) 514 cache->postNotification(this, AXObjectCache::AXValueChanged, false); 515 } 516 innerTextElement()->setInnerText(value, ASSERT_NO_EXCEPTION); 517 518 if (value.endsWith('\n') || value.endsWith('\r')) 519 innerTextElement()->appendChild(HTMLBRElement::create(document())); 520 } 521 522 setFormControlValueMatchesRenderer(true); 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 HTMLElement* innerText = innerTextElement(); 537 if (!innerText || !isTextFormControl()) 538 return emptyString(); 539 540 StringBuilder result; 541 for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) { 542 if (node->hasTagName(brTag)) 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 = innerTextElement(); 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 (node->hasTagName(brTag)) 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 617 Node* container = position.containerNode(); 618 if (!container) 619 return 0; 620 Element* ancestor = container->shadowHost(); 621 return ancestor && isHTMLTextFormControlElement(ancestor) ? toHTMLTextFormControlElement(ancestor) : 0; 622 } 623 624 static const HTMLElement* parentHTMLElement(const Element* element) 625 { 626 while (element) { 627 element = element->parentElement(); 628 if (element && element->isHTMLElement()) 629 return toHTMLElement(element); 630 } 631 return 0; 632 } 633 634 String HTMLTextFormControlElement::directionForFormData() const 635 { 636 for (const HTMLElement* element = this; element; element = parentHTMLElement(element)) { 637 const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr); 638 if (dirAttributeValue.isNull()) 639 continue; 640 641 if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr")) 642 return dirAttributeValue; 643 644 if (equalIgnoringCase(dirAttributeValue, "auto")) { 645 bool isAuto; 646 TextDirection textDirection = element->directionalityIfhasDirAutoAttribute(isAuto); 647 return textDirection == RTL ? "rtl" : "ltr"; 648 } 649 } 650 651 return "ltr"; 652 } 653 654 HTMLElement* HTMLTextFormControlElement::innerTextElement() const 655 { 656 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor())); 657 } 658 659 } // namespace Webcore 660