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/core/v8/ExceptionState.h" 29 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 30 #include "core/HTMLNames.h" 31 #include "core/accessibility/AXObjectCache.h" 32 #include "core/dom/Document.h" 33 #include "core/dom/NodeList.h" 34 #include "core/dom/NodeTraversal.h" 35 #include "core/dom/Text.h" 36 #include "core/dom/shadow/ShadowRoot.h" 37 #include "core/editing/Editor.h" 38 #include "core/editing/FrameSelection.h" 39 #include "core/editing/TextIterator.h" 40 #include "core/editing/htmlediting.h" 41 #include "core/events/Event.h" 42 #include "core/frame/LocalFrame.h" 43 #include "core/frame/UseCounter.h" 44 #include "core/html/HTMLBRElement.h" 45 #include "core/html/shadow/ShadowElementNames.h" 46 #include "core/page/FocusController.h" 47 #include "core/page/Page.h" 48 #include "core/rendering/RenderBlock.h" 49 #include "core/rendering/RenderBlockFlow.h" 50 #include "core/rendering/RenderTheme.h" 51 #include "platform/heap/Handle.h" 52 #include "platform/text/TextBoundaries.h" 53 #include "wtf/text/StringBuilder.h" 54 55 namespace blink { 56 57 using namespace HTMLNames; 58 59 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& doc, HTMLFormElement* form) 60 : HTMLFormControlElementWithState(tagName, doc, form) 61 , m_lastChangeWasUserEdit(false) 62 , m_cachedSelectionStart(0) 63 , m_cachedSelectionEnd(0) 64 , m_cachedSelectionDirection(SelectionHasNoDirection) 65 { 66 } 67 68 HTMLTextFormControlElement::~HTMLTextFormControlElement() 69 { 70 } 71 72 Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(ContainerNode* insertionPoint) 73 { 74 HTMLFormControlElementWithState::insertedInto(insertionPoint); 75 if (!insertionPoint->inDocument()) 76 return InsertionDone; 77 String initialValue = value(); 78 setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue); 79 return InsertionDone; 80 } 81 82 void HTMLTextFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type) 83 { 84 if (supportsPlaceholder()) 85 updatePlaceholderVisibility(false); 86 handleFocusEvent(oldFocusedElement, type); 87 HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, type); 88 } 89 90 void HTMLTextFormControlElement::dispatchBlurEvent(Element* newFocusedElement) 91 { 92 if (supportsPlaceholder()) 93 updatePlaceholderVisibility(false); 94 handleBlurEvent(); 95 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement); 96 } 97 98 void HTMLTextFormControlElement::defaultEventHandler(Event* event) 99 { 100 if (event->type() == EventTypeNames::webkitEditableContentChanged && renderer() && renderer()->isTextControl()) { 101 m_lastChangeWasUserEdit = true; 102 subtreeHasChanged(); 103 return; 104 } 105 106 HTMLFormControlElementWithState::defaultEventHandler(event); 107 } 108 109 void HTMLTextFormControlElement::forwardEvent(Event* event) 110 { 111 if (event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus) 112 return; 113 innerEditorElement()->defaultEventHandler(event); 114 } 115 116 String HTMLTextFormControlElement::strippedPlaceholder() const 117 { 118 // According to the HTML5 specification, we need to remove CR and LF from 119 // the attribute value. 120 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr); 121 if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn)) 122 return attributeValue; 123 124 StringBuilder stripped; 125 unsigned length = attributeValue.length(); 126 stripped.reserveCapacity(length); 127 for (unsigned i = 0; i < length; ++i) { 128 UChar character = attributeValue[i]; 129 if (character == newlineCharacter || character == carriageReturn) 130 continue; 131 stripped.append(character); 132 } 133 return stripped.toString(); 134 } 135 136 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; } 137 138 bool HTMLTextFormControlElement::isPlaceholderEmpty() const 139 { 140 const AtomicString& attributeValue = fastGetAttribute(placeholderAttr); 141 return attributeValue.string().find(isNotLineBreak) == kNotFound; 142 } 143 144 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const 145 { 146 return supportsPlaceholder() 147 && isEmptyValue() 148 && isEmptySuggestedValue() 149 && !isPlaceholderEmpty() 150 && (document().focusedElement() != this || (RenderTheme::theme().shouldShowPlaceholderWhenFocused())) 151 && (!renderer() || renderer()->style()->visibility() == VISIBLE); 152 } 153 154 HTMLElement* HTMLTextFormControlElement::placeholderElement() const 155 { 156 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::placeholder())); 157 } 158 159 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged) 160 { 161 if (!supportsPlaceholder()) 162 return; 163 if (!placeholderElement() || placeholderValueChanged) 164 updatePlaceholderText(); 165 HTMLElement* placeholder = placeholderElement(); 166 if (!placeholder) 167 return; 168 placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? CSSValueVisible : CSSValueHidden); 169 } 170 171 void HTMLTextFormControlElement::setSelectionStart(int start) 172 { 173 setSelectionRange(start, std::max(start, selectionEnd()), selectionDirection()); 174 } 175 176 void HTMLTextFormControlElement::setSelectionEnd(int end) 177 { 178 setSelectionRange(std::min(end, selectionStart()), end, selectionDirection()); 179 } 180 181 void HTMLTextFormControlElement::setSelectionDirection(const String& direction) 182 { 183 setSelectionRange(selectionStart(), selectionEnd(), direction); 184 } 185 186 void HTMLTextFormControlElement::select() 187 { 188 document().updateLayoutIgnorePendingStylesheets(); 189 setSelectionRange(0, std::numeric_limits<int>::max(), SelectionHasNoDirection, isFocusable() ? ChangeSelectionAndFocus : NotChangeSelection); 190 } 191 192 bool HTMLTextFormControlElement::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue) 193 { 194 return !equalIgnoringNullity(oldValue, newValue); 195 } 196 197 void HTMLTextFormControlElement::dispatchFormControlChangeEvent() 198 { 199 String newValue = value(); 200 if (shouldDispatchFormControlChangeEvent(m_textAsOfLastFormControlChangeEvent, newValue)) { 201 setTextAsOfLastFormControlChangeEvent(newValue); 202 dispatchChangeEvent(); 203 } 204 setChangedSinceLastFormControlChangeEvent(false); 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 = innerEditorValue(); 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 setInnerEditorValue(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 if (direction == SelectionHasNoDirection && document().frame() && document().frame()->editor().behavior().shouldConsiderSelectionAsDirectional()) 277 direction = SelectionHasForwardDirection; 278 279 return setSelectionRange(start, end, direction); 280 } 281 282 static Position positionForIndex(HTMLElement* innerEditor, int index) 283 { 284 ASSERT(index >= 0); 285 if (index == 0) { 286 Node* node = NodeTraversal::next(*innerEditor, innerEditor); 287 if (node && node->isTextNode()) 288 return Position(node, 0, Position::PositionIsOffsetInAnchor); 289 return Position(innerEditor, 0, Position::PositionIsOffsetInAnchor); 290 } 291 int remainingCharactersToMoveForward = index; 292 Node* lastBrOrText = innerEditor; 293 for (Node* node = NodeTraversal::next(*innerEditor, innerEditor); node; node = NodeTraversal::next(*node, innerEditor)) { 294 ASSERT(remainingCharactersToMoveForward >= 0); 295 if (node->hasTagName(brTag)) { 296 if (remainingCharactersToMoveForward == 0) 297 return positionBeforeNode(node); 298 --remainingCharactersToMoveForward; 299 lastBrOrText = node; 300 continue; 301 } 302 303 if (node->isTextNode()) { 304 Text& text = toText(*node); 305 if (remainingCharactersToMoveForward < static_cast<int>(text.length())) 306 return Position(&text, remainingCharactersToMoveForward); 307 remainingCharactersToMoveForward -= text.length(); 308 lastBrOrText = node; 309 continue; 310 } 311 312 ASSERT_NOT_REACHED(); 313 } 314 return lastPositionInOrAfterNode(lastBrOrText); 315 } 316 317 static int indexForPosition(HTMLElement* innerEditor, const Position& passedPosition) 318 { 319 if (!innerEditor || !innerEditor->contains(passedPosition.anchorNode()) || passedPosition.isNull()) 320 return 0; 321 322 if (positionBeforeNode(innerEditor) == passedPosition) 323 return 0; 324 325 int index = 0; 326 Node* startNode = passedPosition.computeNodeBeforePosition(); 327 if (!startNode) 328 startNode = passedPosition.containerNode(); 329 ASSERT(startNode); 330 ASSERT(innerEditor->contains(startNode)); 331 332 for (Node* node = startNode; node; node = NodeTraversal::previous(*node, innerEditor)) { 333 if (node->isTextNode()) { 334 int length = toText(*node).length(); 335 if (node == passedPosition.containerNode()) 336 index += std::min(length, passedPosition.offsetInContainerNode()); 337 else 338 index += length; 339 } else if (node->hasTagName(brTag)) { 340 ++index; 341 } 342 } 343 344 ASSERT(index >= 0); 345 return index; 346 } 347 348 void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, SelectionOption selectionOption) 349 { 350 if (hasAuthorShadowRoot() || !isTextFormControl()) 351 return; 352 353 const int editorValueLength = static_cast<int>(innerEditorValue().length()); 354 ASSERT(editorValueLength >= 0); 355 end = std::max(std::min(end, editorValueLength), 0); 356 start = std::min(std::max(start, 0), end); 357 cacheSelection(start, end, direction); 358 359 if (selectionOption == NotChangeSelection || (selectionOption == ChangeSelectionIfFocused && document().focusedElement() != this)) 360 return; 361 362 LocalFrame* frame = document().frame(); 363 HTMLElement* innerEditor = innerEditorElement(); 364 if (!frame || !innerEditor) 365 return; 366 367 Position startPosition = positionForIndex(innerEditor, start); 368 Position endPosition = start == end ? startPosition : positionForIndex(innerEditor, end); 369 370 ASSERT(start == indexForPosition(innerEditor, startPosition)); 371 ASSERT(end == indexForPosition(innerEditor, endPosition)); 372 373 // startPosition and endPosition can be null position for example when 374 // "-webkit-user-select: none" style attribute is specified. 375 if (startPosition.isNotNull() && endPosition.isNotNull()) { 376 ASSERT(startPosition.anchorNode()->shadowHost() == this 377 && endPosition.anchorNode()->shadowHost() == this); 378 } 379 VisibleSelection newSelection; 380 if (direction == SelectionHasBackwardDirection) 381 newSelection.setWithoutValidation(endPosition, startPosition); 382 else 383 newSelection.setWithoutValidation(startPosition, endPosition); 384 newSelection.setIsDirectional(direction != SelectionHasNoDirection); 385 386 frame->selection().setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | (selectionOption == ChangeSelectionAndFocus ? 0 : FrameSelection::DoNotSetFocus)); 387 } 388 389 VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const 390 { 391 if (index <= 0) 392 return VisiblePosition(firstPositionInNode(innerEditorElement()), DOWNSTREAM); 393 Position start, end; 394 bool selected = Range::selectNodeContents(innerEditorElement(), start, end); 395 if (!selected) 396 return VisiblePosition(); 397 CharacterIterator it(start, end); 398 it.advance(index - 1); 399 return VisiblePosition(it.endPosition(), UPSTREAM); 400 } 401 402 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const 403 { 404 Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent(); 405 if (enclosingTextFormControl(indexPosition) != this) 406 return 0; 407 ASSERT(indexPosition.document()); 408 RefPtrWillBeRawPtr<Range> range = Range::create(*indexPosition.document()); 409 range->setStart(innerEditorElement(), 0, ASSERT_NO_EXCEPTION); 410 range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ASSERT_NO_EXCEPTION); 411 return TextIterator::rangeLength(range.get()); 412 } 413 414 int HTMLTextFormControlElement::selectionStart() const 415 { 416 if (!isTextFormControl()) 417 return 0; 418 if (document().focusedElement() != this) 419 return m_cachedSelectionStart; 420 421 return computeSelectionStart(); 422 } 423 424 int HTMLTextFormControlElement::computeSelectionStart() const 425 { 426 ASSERT(isTextFormControl()); 427 LocalFrame* frame = document().frame(); 428 if (!frame) 429 return 0; 430 431 return indexForPosition(innerEditorElement(), frame->selection().start()); 432 } 433 434 int HTMLTextFormControlElement::selectionEnd() const 435 { 436 if (!isTextFormControl()) 437 return 0; 438 if (document().focusedElement() != this) 439 return m_cachedSelectionEnd; 440 return computeSelectionEnd(); 441 } 442 443 int HTMLTextFormControlElement::computeSelectionEnd() const 444 { 445 ASSERT(isTextFormControl()); 446 LocalFrame* frame = document().frame(); 447 if (!frame) 448 return 0; 449 450 return indexForPosition(innerEditorElement(), frame->selection().end()); 451 } 452 453 static const AtomicString& directionString(TextFieldSelectionDirection direction) 454 { 455 DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral)); 456 DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward", AtomicString::ConstructFromLiteral)); 457 DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward", AtomicString::ConstructFromLiteral)); 458 459 switch (direction) { 460 case SelectionHasNoDirection: 461 return none; 462 case SelectionHasForwardDirection: 463 return forward; 464 case SelectionHasBackwardDirection: 465 return backward; 466 } 467 468 ASSERT_NOT_REACHED(); 469 return none; 470 } 471 472 const AtomicString& HTMLTextFormControlElement::selectionDirection() const 473 { 474 if (!isTextFormControl()) 475 return directionString(SelectionHasNoDirection); 476 if (document().focusedElement() != this) 477 return directionString(m_cachedSelectionDirection); 478 479 return directionString(computeSelectionDirection()); 480 } 481 482 TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const 483 { 484 ASSERT(isTextFormControl()); 485 LocalFrame* frame = document().frame(); 486 if (!frame) 487 return SelectionHasNoDirection; 488 489 const VisibleSelection& selection = frame->selection().selection(); 490 return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection; 491 } 492 493 static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer) 494 { 495 if (node->isTextNode()) { 496 containerNode = node; 497 offsetInContainer = offset; 498 } else { 499 containerNode = node->parentNode(); 500 offsetInContainer = node->nodeIndex() + offset; 501 } 502 } 503 504 PassRefPtrWillBeRawPtr<Range> HTMLTextFormControlElement::selection() const 505 { 506 if (!renderer() || !isTextFormControl()) 507 return nullptr; 508 509 int start = m_cachedSelectionStart; 510 int end = m_cachedSelectionEnd; 511 512 ASSERT(start <= end); 513 HTMLElement* innerText = innerEditorElement(); 514 if (!innerText) 515 return nullptr; 516 517 if (!innerText->hasChildren()) 518 return Range::create(document(), innerText, 0, innerText, 0); 519 520 int offset = 0; 521 Node* startNode = 0; 522 Node* endNode = 0; 523 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) { 524 ASSERT(!node->hasChildren()); 525 ASSERT(node->isTextNode() || isHTMLBRElement(*node)); 526 int length = node->isTextNode() ? lastOffsetInNode(node) : 1; 527 528 if (offset <= start && start <= offset + length) 529 setContainerAndOffsetForRange(node, start - offset, startNode, start); 530 531 if (offset <= end && end <= offset + length) { 532 setContainerAndOffsetForRange(node, end - offset, endNode, end); 533 break; 534 } 535 536 offset += length; 537 } 538 539 if (!startNode || !endNode) 540 return nullptr; 541 542 return Range::create(document(), startNode, start, endNode, end); 543 } 544 545 void HTMLTextFormControlElement::restoreCachedSelection() 546 { 547 setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection); 548 } 549 550 void HTMLTextFormControlElement::selectionChanged(bool userTriggered) 551 { 552 if (!renderer() || !isTextFormControl()) 553 return; 554 555 // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus 556 cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection()); 557 558 if (LocalFrame* frame = document().frame()) { 559 if (frame->selection().isRange() && userTriggered) 560 dispatchEvent(Event::createBubble(EventTypeNames::select)); 561 } 562 } 563 564 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 565 { 566 if (name == placeholderAttr) { 567 updatePlaceholderVisibility(true); 568 UseCounter::count(document(), UseCounter::PlaceholderAttribute); 569 } else 570 HTMLFormControlElementWithState::parseAttribute(name, value); 571 } 572 573 bool HTMLTextFormControlElement::lastChangeWasUserEdit() const 574 { 575 if (!isTextFormControl()) 576 return false; 577 return m_lastChangeWasUserEdit; 578 } 579 580 void HTMLTextFormControlElement::setInnerEditorValue(const String& value) 581 { 582 ASSERT(!hasAuthorShadowRoot()); 583 if (!isTextFormControl() || hasAuthorShadowRoot()) 584 return; 585 586 bool textIsChanged = value != innerEditorValue(); 587 if (textIsChanged || !innerEditorElement()->hasChildren()) { 588 if (textIsChanged && renderer()) { 589 if (AXObjectCache* cache = document().existingAXObjectCache()) 590 cache->postNotification(this, AXObjectCache::AXValueChanged, false); 591 } 592 innerEditorElement()->setInnerText(value, ASSERT_NO_EXCEPTION); 593 594 if (value.endsWith('\n') || value.endsWith('\r')) 595 innerEditorElement()->appendChild(HTMLBRElement::create(document())); 596 } 597 } 598 599 static String finishText(StringBuilder& result) 600 { 601 // Remove one trailing newline; there's always one that's collapsed out by rendering. 602 size_t size = result.length(); 603 if (size && result[size - 1] == '\n') 604 result.resize(--size); 605 return result.toString(); 606 } 607 608 String HTMLTextFormControlElement::innerEditorValue() const 609 { 610 ASSERT(!hasAuthorShadowRoot()); 611 HTMLElement* innerEditor = innerEditorElement(); 612 if (!innerEditor || !isTextFormControl()) 613 return emptyString(); 614 615 StringBuilder result; 616 for (Node* node = innerEditor; node; node = NodeTraversal::next(*node, innerEditor)) { 617 if (isHTMLBRElement(*node)) 618 result.append(newlineCharacter); 619 else if (node->isTextNode()) 620 result.append(toText(node)->data()); 621 } 622 return finishText(result); 623 } 624 625 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset) 626 { 627 RootInlineBox* next; 628 for (; line; line = next) { 629 next = line->nextRootBox(); 630 if (next && !line->endsWithBreak()) { 631 ASSERT(line->lineBreakObj()); 632 breakNode = line->lineBreakObj()->node(); 633 breakOffset = line->lineBreakPos(); 634 line = next; 635 return; 636 } 637 } 638 breakNode = 0; 639 breakOffset = 0; 640 } 641 642 String HTMLTextFormControlElement::valueWithHardLineBreaks() const 643 { 644 // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer. 645 // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. 646 HTMLElement* innerText = innerEditorElement(); 647 if (!innerText || !isTextFormControl()) 648 return value(); 649 650 RenderBlockFlow* renderer = toRenderBlockFlow(innerText->renderer()); 651 if (!renderer) 652 return value(); 653 654 Node* breakNode; 655 unsigned breakOffset; 656 RootInlineBox* line = renderer->firstRootBox(); 657 if (!line) 658 return value(); 659 660 getNextSoftBreak(line, breakNode, breakOffset); 661 662 StringBuilder result; 663 for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) { 664 if (isHTMLBRElement(*node)) 665 result.append(newlineCharacter); 666 else if (node->isTextNode()) { 667 String data = toText(node)->data(); 668 unsigned length = data.length(); 669 unsigned position = 0; 670 while (breakNode == node && breakOffset <= length) { 671 if (breakOffset > position) { 672 result.append(data, position, breakOffset - position); 673 position = breakOffset; 674 result.append(newlineCharacter); 675 } 676 getNextSoftBreak(line, breakNode, breakOffset); 677 } 678 result.append(data, position, length - position); 679 } 680 while (breakNode == node) 681 getNextSoftBreak(line, breakNode, breakOffset); 682 } 683 return finishText(result); 684 } 685 686 HTMLTextFormControlElement* enclosingTextFormControl(const Position& position) 687 { 688 ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor 689 || position.containerNode() || !position.anchorNode()->shadowHost() 690 || (position.anchorNode()->parentNode() && position.anchorNode()->parentNode()->isShadowRoot())); 691 return enclosingTextFormControl(position.containerNode()); 692 } 693 694 HTMLTextFormControlElement* enclosingTextFormControl(Node* container) 695 { 696 if (!container) 697 return 0; 698 Element* ancestor = container->shadowHost(); 699 return ancestor && isHTMLTextFormControlElement(*ancestor) && container->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? toHTMLTextFormControlElement(ancestor) : 0; 700 } 701 702 String HTMLTextFormControlElement::directionForFormData() const 703 { 704 for (const HTMLElement* element = this; element; element = Traversal<HTMLElement>::firstAncestor(*element)) { 705 const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr); 706 if (dirAttributeValue.isNull()) 707 continue; 708 709 if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr")) 710 return dirAttributeValue; 711 712 if (equalIgnoringCase(dirAttributeValue, "auto")) { 713 bool isAuto; 714 TextDirection textDirection = element->directionalityIfhasDirAutoAttribute(isAuto); 715 return textDirection == RTL ? "rtl" : "ltr"; 716 } 717 } 718 719 return "ltr"; 720 } 721 722 HTMLElement* HTMLTextFormControlElement::innerEditorElement() const 723 { 724 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor())); 725 } 726 727 static Position innerNodePosition(const Position& innerPosition) 728 { 729 ASSERT(innerPosition.anchorType() != Position::PositionIsBeforeAnchor); 730 ASSERT(innerPosition.anchorType() != Position::PositionIsAfterAnchor); 731 HTMLElement* element = toHTMLElement(innerPosition.anchorNode()); 732 ASSERT(element); 733 RefPtrWillBeRawPtr<NodeList> childNodes = element->childNodes(); 734 if (!childNodes->length()) 735 return Position(element, 0, Position::PositionIsOffsetInAnchor); 736 737 unsigned offset = 0; 738 739 switch (innerPosition.anchorType()) { 740 case Position::PositionIsOffsetInAnchor: 741 offset = std::max(0, std::min(innerPosition.offsetInContainerNode(), static_cast<int>(childNodes->length()))); 742 break; 743 case Position::PositionIsAfterChildren: 744 offset = childNodes->length(); 745 break; 746 default: 747 break; 748 } 749 750 if (offset == childNodes->length()) 751 return Position(element->lastChild(), Position::PositionIsAfterAnchor); 752 753 Node* node = childNodes->item(offset); 754 if (node->isTextNode()) 755 return Position(toText(node), 0); 756 757 return Position(node, Position::PositionIsBeforeAnchor); 758 } 759 760 enum FindOption { 761 FindStart, 762 FindEnd 763 }; 764 765 static Position findWordBoundary(const HTMLElement* innerEditor, const Position& startPosition, const Position& endPosition, FindOption findOption) 766 { 767 StringBuilder concatTexts; 768 Vector<unsigned> lengthList; 769 Vector<Text*> textList; 770 771 if (startPosition.anchorNode()->isTextNode()) 772 ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor); 773 if (endPosition.anchorNode()->isTextNode()) 774 ASSERT(endPosition.anchorType() == Position::PositionIsOffsetInAnchor); 775 776 // Traverse text nodes. 777 for (Node* node = startPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) { 778 bool isStartNode = node == startPosition.anchorNode(); 779 bool isEndNode = node == endPosition.anchorNode(); 780 if (node->isTextNode()) { 781 Text* text = toText(node); 782 const unsigned start = isStartNode ? startPosition.offsetInContainerNode() : 0; 783 const unsigned end = isEndNode ? endPosition.offsetInContainerNode() : text->data().length(); 784 const unsigned length = end - start; 785 786 concatTexts.append(text->data(), start, length); 787 lengthList.append(length); 788 textList.append(text); 789 } 790 791 if (isEndNode) 792 break; 793 } 794 795 if (concatTexts.length() == 0) 796 return startPosition; 797 798 int start, end; 799 if (findOption == FindEnd && concatTexts[0] == '\n') { 800 // findWordBoundary("\ntext", 0, &start, &end) assigns 1 to |end| but 801 // we expect 0 at the case. 802 start = 0; 803 end = 0; 804 } else { 805 Vector<UChar> characters; 806 concatTexts.toString().appendTo(characters); 807 findWordBoundary(characters.data(), characters.size(), findOption == FindStart ? characters.size() : 0, &start, &end); 808 } 809 ASSERT(start >= 0); 810 ASSERT(end >= 0); 811 unsigned remainingOffset = findOption == FindStart ? start : end; 812 // Find position. 813 for (unsigned i = 0; i < lengthList.size(); ++i) { 814 if (remainingOffset <= lengthList[i]) 815 return Position(textList[i], (textList[i] == startPosition.anchorNode()) ? remainingOffset + startPosition.offsetInContainerNode() : remainingOffset); 816 remainingOffset -= lengthList[i]; 817 } 818 819 ASSERT_NOT_REACHED(); 820 return Position(); 821 } 822 823 Position HTMLTextFormControlElement::startOfWord(const Position& position) 824 { 825 const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position); 826 ASSERT(textFormControl); 827 HTMLElement* innerEditor = textFormControl->innerEditorElement(); 828 829 const Position startPosition = startOfSentence(position); 830 if (startPosition == position) 831 return position; 832 const Position endPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position; 833 834 return findWordBoundary(innerEditor, startPosition, endPosition, FindStart); 835 } 836 837 Position HTMLTextFormControlElement::endOfWord(const Position& position) 838 { 839 const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position); 840 ASSERT(textFormControl); 841 HTMLElement* innerEditor = textFormControl->innerEditorElement(); 842 843 const Position endPosition = endOfSentence(position); 844 if (endPosition == position) 845 return position; 846 const Position startPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position; 847 848 return findWordBoundary(innerEditor, startPosition, endPosition, FindEnd); 849 } 850 851 static Position endOfPrevious(const Node& node, HTMLElement* innerEditor) 852 { 853 Node* previousNode = NodeTraversal::previous(node, innerEditor); 854 if (!previousNode) 855 return Position(); 856 857 if (isHTMLBRElement(previousNode)) 858 return Position(previousNode, Position::PositionIsAfterAnchor); 859 860 if (previousNode->isTextNode()) 861 return Position(toText(previousNode), toText(previousNode)->length()); 862 863 return Position(); 864 } 865 866 static Position previousIfPositionIsAfterLineBreak(const Position& position, HTMLElement* innerEditor) 867 { 868 if (position.isNull()) 869 return Position(); 870 871 // Move back if position is just after line break. 872 if (isHTMLBRElement(*position.anchorNode())) { 873 switch (position.anchorType()) { 874 case Position::PositionIsAfterAnchor: 875 return Position(position.anchorNode(), Position::PositionIsBeforeAnchor); 876 case Position::PositionIsBeforeAnchor: 877 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor); 878 default: 879 ASSERT_NOT_REACHED(); 880 } 881 } else if (position.anchorNode()->isTextNode()) { 882 Text* textNode = toText(position.anchorNode()); 883 unsigned offset = position.offsetInContainerNode(); 884 if (textNode->length() == 0 || offset == 0) { 885 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor); 886 } 887 888 if (offset <= textNode->length() && textNode->data()[offset - 1] == '\n') { 889 return Position(textNode, offset - 1); 890 } 891 } 892 893 return position; 894 } 895 896 static inline Position startOfInnerText(const HTMLTextFormControlElement* textFormControl) 897 { 898 return Position(textFormControl->innerEditorElement(), 0, Position::PositionIsOffsetInAnchor); 899 } 900 901 Position HTMLTextFormControlElement::startOfSentence(const Position& position) 902 { 903 HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position); 904 ASSERT(textFormControl); 905 906 HTMLElement* innerEditor = textFormControl->innerEditorElement(); 907 if (!innerEditor->childNodes()->length()) 908 return startOfInnerText(textFormControl); 909 910 const Position innerPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position; 911 const Position pivotPosition = previousIfPositionIsAfterLineBreak(innerPosition, innerEditor); 912 if (pivotPosition.isNull()) 913 return startOfInnerText(textFormControl); 914 915 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::previous(*node, innerEditor)) { 916 bool isPivotNode = (node == pivotPosition.anchorNode()); 917 918 if (isHTMLBRElement(node) && (!isPivotNode || pivotPosition.anchorType() == Position::PositionIsAfterAnchor)) 919 return Position(node, Position::PositionIsAfterAnchor); 920 921 if (node->isTextNode()) { 922 Text* textNode = toText(node); 923 size_t lastLineBreak = textNode->data().substring(0, isPivotNode ? pivotPosition.offsetInContainerNode() : textNode->length()).reverseFind('\n'); 924 if (lastLineBreak != kNotFound) 925 return Position(textNode, lastLineBreak + 1); 926 } 927 } 928 return startOfInnerText(textFormControl); 929 } 930 931 static Position endOfInnerText(const HTMLTextFormControlElement* textFormControl) 932 { 933 HTMLElement* innerEditor = textFormControl->innerEditorElement(); 934 return Position(innerEditor, innerEditor->childNodes()->length(), Position::PositionIsOffsetInAnchor); 935 } 936 937 Position HTMLTextFormControlElement::endOfSentence(const Position& position) 938 { 939 HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position); 940 ASSERT(textFormControl); 941 942 HTMLElement* innerEditor = textFormControl->innerEditorElement(); 943 if (innerEditor->childNodes()->length() == 0) 944 return startOfInnerText(textFormControl); 945 946 const Position pivotPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position; 947 if (pivotPosition.isNull()) 948 return startOfInnerText(textFormControl); 949 950 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) { 951 bool isPivotNode = node == pivotPosition.anchorNode(); 952 953 if (isHTMLBRElement(node)) 954 return Position(node, Position::PositionIsAfterAnchor); 955 956 if (node->isTextNode()) { 957 Text* textNode = toText(node); 958 size_t firstLineBreak = textNode->data().find('\n', isPivotNode ? pivotPosition.offsetInContainerNode() : 0); 959 if (firstLineBreak != kNotFound) 960 return Position(textNode, firstLineBreak + 1); 961 } 962 } 963 return endOfInnerText(textFormControl); 964 } 965 966 } // namespace blink 967