1/* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "Editor.h" 29 30#include "AXObjectCache.h" 31#include "ApplyStyleCommand.h" 32#include "CSSComputedStyleDeclaration.h" 33#include "CSSMutableStyleDeclaration.h" 34#include "CSSProperty.h" 35#include "CSSPropertyNames.h" 36#include "CSSStyleSelector.h" 37#include "CSSValueKeywords.h" 38#include "CachedResourceLoader.h" 39#include "ClipboardEvent.h" 40#include "CompositionEvent.h" 41#include "SpellingCorrectionController.h" 42#include "CreateLinkCommand.h" 43#include "DeleteButtonController.h" 44#include "DeleteSelectionCommand.h" 45#include "DocumentFragment.h" 46#include "DocumentMarkerController.h" 47#include "EditingText.h" 48#include "EditorClient.h" 49#include "EventHandler.h" 50#include "EventNames.h" 51#include "FocusController.h" 52#include "Frame.h" 53#include "FrameTree.h" 54#include "FrameView.h" 55#include "GraphicsContext.h" 56#include "HTMLFrameOwnerElement.h" 57#include "HTMLInputElement.h" 58#include "HTMLTextAreaElement.h" 59#include "HitTestResult.h" 60#include "IndentOutdentCommand.h" 61#include "InsertListCommand.h" 62#include "KeyboardEvent.h" 63#include "KillRing.h" 64#include "ModifySelectionListLevel.h" 65#include "NodeList.h" 66#include "Page.h" 67#include "Pasteboard.h" 68#include "TextCheckingHelper.h" 69#include "RemoveFormatCommand.h" 70#include "RenderBlock.h" 71#include "RenderPart.h" 72#include "RenderTextControl.h" 73#include "ReplaceSelectionCommand.h" 74#include "Settings.h" 75#include "Sound.h" 76#include "SpellChecker.h" 77#include "SpellingCorrectionCommand.h" 78#include "Text.h" 79#include "TextEvent.h" 80#include "TextIterator.h" 81#include "TypingCommand.h" 82#include "UserTypingGestureIndicator.h" 83#include "htmlediting.h" 84#include "markup.h" 85#include "visible_units.h" 86#include <wtf/UnusedParam.h> 87#include <wtf/unicode/CharacterNames.h> 88#include <wtf/unicode/Unicode.h> 89 90namespace WebCore { 91 92using namespace std; 93using namespace HTMLNames; 94using namespace WTF; 95using namespace Unicode; 96 97// When an event handler has moved the selection outside of a text control 98// we should use the target control's selection for this editing operation. 99VisibleSelection Editor::selectionForCommand(Event* event) 100{ 101 VisibleSelection selection = m_frame->selection()->selection(); 102 if (!event) 103 return selection; 104 // If the target is a text control, and the current selection is outside of its shadow tree, 105 // then use the saved selection for that text control. 106 Node* target = event->target()->toNode(); 107 Node* selectionStart = selection.start().deprecatedNode(); 108 if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) { 109 RefPtr<Range> range; 110 if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField()) 111 range = static_cast<HTMLInputElement*>(target)->selection(); 112 else if (target->hasTagName(textareaTag)) 113 range = static_cast<HTMLTextAreaElement*>(target)->selection(); 114 115 if (range) 116 return VisibleSelection(range.get()); 117 } 118 return selection; 119} 120 121// Function considers Mac editing behavior a fallback when Page or Settings is not available. 122EditingBehavior Editor::behavior() const 123{ 124 if (!m_frame || !m_frame->settings()) 125 return EditingBehavior(EditingMacBehavior); 126 127 return EditingBehavior(m_frame->settings()->editingBehaviorType()); 128} 129 130EditorClient* Editor::client() const 131{ 132 if (Page* page = m_frame->page()) 133 return page->editorClient(); 134 return 0; 135} 136 137 138TextCheckerClient* Editor::textChecker() const 139{ 140 if (EditorClient* owner = client()) 141 return owner->textChecker(); 142 return 0; 143} 144 145void Editor::handleKeyboardEvent(KeyboardEvent* event) 146{ 147 if (EditorClient* c = client()) 148 c->handleKeyboardEvent(event); 149} 150 151void Editor::handleInputMethodKeydown(KeyboardEvent* event) 152{ 153 if (EditorClient* c = client()) 154 c->handleInputMethodKeydown(event); 155} 156 157bool Editor::handleTextEvent(TextEvent* event) 158{ 159 // Default event handling for Drag and Drop will be handled by DragController 160 // so we leave the event for it. 161 if (event->isDrop()) 162 return false; 163 164 if (event->isPaste()) { 165 if (event->pastingFragment()) 166 replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle()); 167 else 168 replaceSelectionWithText(event->data(), false, event->shouldSmartReplace()); 169 return true; 170 } 171 172 String data = event->data(); 173 if (data == "\n") { 174 if (event->isLineBreak()) 175 return insertLineBreak(); 176 return insertParagraphSeparator(); 177 } 178 179 return insertTextWithoutSendingTextEvent(data, false, event); 180} 181 182bool Editor::canEdit() const 183{ 184 return m_frame->selection()->rootEditableElement(); 185} 186 187bool Editor::canEditRichly() const 188{ 189 return m_frame->selection()->isContentRichlyEditable(); 190} 191 192// WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They 193// also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items. 194// We need to use onbeforecopy as a real menu enabler because we allow elements that are not 195// normally selectable to implement copy/paste (like divs, or a document body). 196 197bool Editor::canDHTMLCut() 198{ 199 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb); 200} 201 202bool Editor::canDHTMLCopy() 203{ 204 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb); 205} 206 207bool Editor::canDHTMLPaste() 208{ 209 return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb); 210} 211 212bool Editor::canCut() const 213{ 214 return canCopy() && canDelete(); 215} 216 217static HTMLImageElement* imageElementFromImageDocument(Document* document) 218{ 219 if (!document) 220 return 0; 221 if (!document->isImageDocument()) 222 return 0; 223 224 HTMLElement* body = document->body(); 225 if (!body) 226 return 0; 227 228 Node* node = body->firstChild(); 229 if (!node) 230 return 0; 231 if (!node->hasTagName(imgTag)) 232 return 0; 233 return static_cast<HTMLImageElement*>(node); 234} 235 236bool Editor::canCopy() const 237{ 238 if (imageElementFromImageDocument(m_frame->document())) 239 return true; 240 SelectionController* selection = m_frame->selection(); 241 return selection->isRange() && !selection->isInPasswordField(); 242} 243 244bool Editor::canPaste() const 245{ 246 return canEdit(); 247} 248 249bool Editor::canDelete() const 250{ 251 SelectionController* selection = m_frame->selection(); 252 return selection->isRange() && selection->rootEditableElement(); 253} 254 255bool Editor::canDeleteRange(Range* range) const 256{ 257 ExceptionCode ec = 0; 258 Node* startContainer = range->startContainer(ec); 259 Node* endContainer = range->endContainer(ec); 260 if (!startContainer || !endContainer) 261 return false; 262 263 if (!startContainer->rendererIsEditable() || !endContainer->rendererIsEditable()) 264 return false; 265 266 if (range->collapsed(ec)) { 267 VisiblePosition start(Position(startContainer, range->startOffset(ec), Position::PositionIsOffsetInAnchor), DOWNSTREAM); 268 VisiblePosition previous = start.previous(); 269 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item. 270 if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement()) 271 return false; 272 } 273 return true; 274} 275 276bool Editor::smartInsertDeleteEnabled() 277{ 278 return client() && client()->smartInsertDeleteEnabled(); 279} 280 281bool Editor::canSmartCopyOrDelete() 282{ 283 return client() && client()->smartInsertDeleteEnabled() && m_frame->selection()->granularity() == WordGranularity; 284} 285 286bool Editor::isSelectTrailingWhitespaceEnabled() 287{ 288 return client() && client()->isSelectTrailingWhitespaceEnabled(); 289} 290 291bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction) 292{ 293 if (!canEdit()) 294 return false; 295 296 if (m_frame->selection()->isRange()) { 297 if (isTypingAction) { 298 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity); 299 revealSelectionAfterEditingOperation(); 300 } else { 301 if (killRing) 302 addToKillRing(selectedRange().get(), false); 303 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 304 // Implicitly calls revealSelectionAfterEditingOperation(). 305 } 306 } else { 307 TypingCommand::Options options = 0; 308 if (canSmartCopyOrDelete()) 309 options |= TypingCommand::SmartDelete; 310 if (killRing) 311 options |= TypingCommand::KillRing; 312 switch (direction) { 313 case DirectionForward: 314 case DirectionRight: 315 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), options, granularity); 316 break; 317 case DirectionBackward: 318 case DirectionLeft: 319 TypingCommand::deleteKeyPressed(m_frame->document(), options, granularity); 320 break; 321 } 322 revealSelectionAfterEditingOperation(); 323 } 324 325 // FIXME: We should to move this down into deleteKeyPressed. 326 // clear the "start new kill ring sequence" setting, because it was set to true 327 // when the selection was updated by deleting the range 328 if (killRing) 329 setStartNewKillRingSequence(false); 330 331 return true; 332} 333 334void Editor::deleteSelectionWithSmartDelete(bool smartDelete) 335{ 336 if (m_frame->selection()->isNone()) 337 return; 338 339 applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete)); 340} 341 342void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace) 343{ 344 Node* target = findEventTargetFromSelection(); 345 if (!target) 346 return; 347 ExceptionCode ec = 0; 348 target->dispatchEvent(TextEvent::createForPlainTextPaste(m_frame->domWindow(), pastingText, smartReplace), ec); 349} 350 351void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle) 352{ 353 Node* target = findEventTargetFromSelection(); 354 if (!target) 355 return; 356 ExceptionCode ec = 0; 357 target->dispatchEvent(TextEvent::createForFragmentPaste(m_frame->domWindow(), pastingFragment, smartReplace, matchStyle), ec); 358} 359 360void Editor::pasteAsPlainTextBypassingDHTML() 361{ 362 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 363} 364 365void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard) 366{ 367 String text = pasteboard->plainText(m_frame); 368 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted)) 369 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard)); 370} 371 372#if !PLATFORM(MAC) 373void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText) 374{ 375 RefPtr<Range> range = selectedRange(); 376 bool chosePlainText; 377 RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText); 378 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted)) 379 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), chosePlainText); 380} 381#endif 382 383bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard) 384{ 385 return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace(); 386} 387 388bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction) 389{ 390 if (!client()) 391 return false; 392 393 if (fragment) { 394 Node* child = fragment->firstChild(); 395 if (child && fragment->lastChild() == child && child->isCharacterDataNode()) 396 return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction); 397 } 398 399 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction); 400} 401 402void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle) 403{ 404 if (m_frame->selection()->isNone() || !fragment) 405 return; 406 407 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting; 408 if (selectReplacement) 409 options |= ReplaceSelectionCommand::SelectReplacement; 410 if (smartReplace) 411 options |= ReplaceSelectionCommand::SmartReplace; 412 if (matchStyle) 413 options |= ReplaceSelectionCommand::MatchStyle; 414 applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, options, EditActionPaste)); 415 revealSelectionAfterEditingOperation(); 416 417 Node* nodeToCheck = m_frame->selection()->rootEditableElement(); 418 if (m_spellChecker->canCheckAsynchronously(nodeToCheck)) 419 m_spellChecker->requestCheckingFor(textCheckingTypeMaskFor(MarkSpelling | MarkGrammar), nodeToCheck); 420} 421 422void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) 423{ 424 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true); 425} 426 427PassRefPtr<Range> Editor::selectedRange() 428{ 429 if (!m_frame) 430 return 0; 431 return m_frame->selection()->toNormalizedRange(); 432} 433 434bool Editor::shouldDeleteRange(Range* range) const 435{ 436 ExceptionCode ec; 437 if (!range || range->collapsed(ec)) 438 return false; 439 440 if (!canDeleteRange(range)) 441 return false; 442 443 return client() && client()->shouldDeleteRange(range); 444} 445 446bool Editor::tryDHTMLCopy() 447{ 448 if (m_frame->selection()->isInPasswordField()) 449 return false; 450 451 if (canCopy()) 452 // Must be done before oncopy adds types and data to the pboard, 453 // also done for security, as it erases data from the last copy/paste. 454 Pasteboard::generalPasteboard()->clear(); 455 456 return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable); 457} 458 459bool Editor::tryDHTMLCut() 460{ 461 if (m_frame->selection()->isInPasswordField()) 462 return false; 463 464 if (canCut()) 465 // Must be done before oncut adds types and data to the pboard, 466 // also done for security, as it erases data from the last copy/paste. 467 Pasteboard::generalPasteboard()->clear(); 468 469 return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable); 470} 471 472bool Editor::tryDHTMLPaste() 473{ 474 return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable); 475} 476 477void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard) 478{ 479 pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame); 480} 481 482bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const 483{ 484 return client() && client()->shouldInsertText(text, range, action); 485} 486 487bool Editor::shouldShowDeleteInterface(HTMLElement* element) const 488{ 489 return client() && client()->shouldShowDeleteInterface(element); 490} 491 492void Editor::respondToChangedSelection(const VisibleSelection& oldSelection) 493{ 494 if (client()) 495 client()->respondToChangedSelection(); 496 m_deleteButtonController->respondToChangedSelection(oldSelection); 497 m_spellingCorrector->respondToChangedSelection(oldSelection); 498} 499 500void Editor::respondToChangedContents(const VisibleSelection& endingSelection) 501{ 502 if (AXObjectCache::accessibilityEnabled()) { 503 Node* node = endingSelection.start().deprecatedNode(); 504 if (node) 505 m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false); 506 } 507 508 updateMarkersForWordsAffectedByEditing(true); 509 510 if (client()) 511 client()->respondToChangedContents(); 512} 513 514const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const 515{ 516#if !PLATFORM(QT) 517 hasMultipleFonts = false; 518 519 if (!m_frame->selection()->isRange()) { 520 Node* nodeToRemove; 521 RenderStyle* style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove 522 523 const SimpleFontData* result = 0; 524 if (style) 525 result = style->font().primaryFont(); 526 527 if (nodeToRemove) { 528 ExceptionCode ec; 529 nodeToRemove->remove(ec); 530 ASSERT(!ec); 531 } 532 533 return result; 534 } 535 536 const SimpleFontData* font = 0; 537 538 RefPtr<Range> range = m_frame->selection()->toNormalizedRange(); 539 Node* startNode = range->editingStartPosition().deprecatedNode(); 540 if (startNode) { 541 Node* pastEnd = range->pastLastNode(); 542 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one 543 // unreproducible case where this didn't happen, so check for nil also. 544 for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) { 545 RenderObject* renderer = n->renderer(); 546 if (!renderer) 547 continue; 548 // FIXME: Are there any node types that have renderers, but that we should be skipping? 549 const SimpleFontData* f = renderer->style()->font().primaryFont(); 550 if (!font) 551 font = f; 552 else if (font != f) { 553 hasMultipleFonts = true; 554 break; 555 } 556 } 557 } 558 559 return font; 560#else 561 return 0; 562#endif 563} 564 565WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const 566{ 567 hasNestedOrMultipleEmbeddings = true; 568 569 if (m_frame->selection()->isNone()) 570 return NaturalWritingDirection; 571 572 Position position = m_frame->selection()->selection().start().downstream(); 573 574 Node* node = position.deprecatedNode(); 575 if (!node) 576 return NaturalWritingDirection; 577 578 Position end; 579 if (m_frame->selection()->isRange()) { 580 end = m_frame->selection()->selection().end().upstream(); 581 582 Node* pastLast = Range::create(m_frame->document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode(); 583 for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) { 584 if (!n->isStyledElement()) 585 continue; 586 587 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n); 588 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi); 589 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 590 continue; 591 592 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); 593 if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride) 594 return NaturalWritingDirection; 595 } 596 } 597 598 if (m_frame->selection()->isCaret()) { 599 RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle(); 600 WritingDirection direction; 601 if (typingStyle && typingStyle->textDirection(direction)) { 602 hasNestedOrMultipleEmbeddings = false; 603 return direction; 604 } 605 node = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode(); 606 } 607 608 // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position 609 // to decide. 610 Node* block = enclosingBlock(node); 611 WritingDirection foundDirection = NaturalWritingDirection; 612 613 for (; node != block; node = node->parentNode()) { 614 if (!node->isStyledElement()) 615 continue; 616 617 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node); 618 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi); 619 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 620 continue; 621 622 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); 623 if (unicodeBidiValue == CSSValueNormal) 624 continue; 625 626 if (unicodeBidiValue == CSSValueBidiOverride) 627 return NaturalWritingDirection; 628 629 ASSERT(unicodeBidiValue == CSSValueEmbed); 630 RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection); 631 if (!direction || !direction->isPrimitiveValue()) 632 continue; 633 634 int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent(); 635 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl) 636 continue; 637 638 if (foundDirection != NaturalWritingDirection) 639 return NaturalWritingDirection; 640 641 // In the range case, make sure that the embedding element persists until the end of the range. 642 if (m_frame->selection()->isRange() && !end.deprecatedNode()->isDescendantOf(node)) 643 return NaturalWritingDirection; 644 645 foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; 646 } 647 hasNestedOrMultipleEmbeddings = false; 648 return foundDirection; 649} 650 651bool Editor::hasBidiSelection() const 652{ 653 if (m_frame->selection()->isNone()) 654 return false; 655 656 Node* startNode; 657 if (m_frame->selection()->isRange()) { 658 startNode = m_frame->selection()->selection().start().downstream().deprecatedNode(); 659 Node* endNode = m_frame->selection()->selection().end().upstream().deprecatedNode(); 660 if (enclosingBlock(startNode) != enclosingBlock(endNode)) 661 return false; 662 } else 663 startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode(); 664 665 RenderObject* renderer = startNode->renderer(); 666 while (renderer && !renderer->isRenderBlock()) 667 renderer = renderer->parent(); 668 669 if (!renderer) 670 return false; 671 672 RenderStyle* style = renderer->style(); 673 if (!style->isLeftToRightDirection()) 674 return true; 675 676 return toRenderBlock(renderer)->containsNonZeroBidiLevel(); 677} 678 679TriState Editor::selectionUnorderedListState() const 680{ 681 if (m_frame->selection()->isCaret()) { 682 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag)) 683 return TrueTriState; 684 } else if (m_frame->selection()->isRange()) { 685 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag); 686 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag); 687 if (startNode && endNode && startNode == endNode) 688 return TrueTriState; 689 } 690 691 return FalseTriState; 692} 693 694TriState Editor::selectionOrderedListState() const 695{ 696 if (m_frame->selection()->isCaret()) { 697 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag)) 698 return TrueTriState; 699 } else if (m_frame->selection()->isRange()) { 700 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag); 701 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag); 702 if (startNode && endNode && startNode == endNode) 703 return TrueTriState; 704 } 705 706 return FalseTriState; 707} 708 709PassRefPtr<Node> Editor::insertOrderedList() 710{ 711 if (!canEditRichly()) 712 return 0; 713 714 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList); 715 revealSelectionAfterEditingOperation(); 716 return newList; 717} 718 719PassRefPtr<Node> Editor::insertUnorderedList() 720{ 721 if (!canEditRichly()) 722 return 0; 723 724 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList); 725 revealSelectionAfterEditingOperation(); 726 return newList; 727} 728 729bool Editor::canIncreaseSelectionListLevel() 730{ 731 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document()); 732} 733 734bool Editor::canDecreaseSelectionListLevel() 735{ 736 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document()); 737} 738 739PassRefPtr<Node> Editor::increaseSelectionListLevel() 740{ 741 if (!canEditRichly() || m_frame->selection()->isNone()) 742 return 0; 743 744 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document()); 745 revealSelectionAfterEditingOperation(); 746 return newList; 747} 748 749PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered() 750{ 751 if (!canEditRichly() || m_frame->selection()->isNone()) 752 return 0; 753 754 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document()); 755 revealSelectionAfterEditingOperation(); 756 return newList.release(); 757} 758 759PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered() 760{ 761 if (!canEditRichly() || m_frame->selection()->isNone()) 762 return 0; 763 764 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document()); 765 revealSelectionAfterEditingOperation(); 766 return newList.release(); 767} 768 769void Editor::decreaseSelectionListLevel() 770{ 771 if (!canEditRichly() || m_frame->selection()->isNone()) 772 return; 773 774 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document()); 775 revealSelectionAfterEditingOperation(); 776} 777 778void Editor::removeFormattingAndStyle() 779{ 780 applyCommand(RemoveFormatCommand::create(m_frame->document())); 781} 782 783void Editor::clearLastEditCommand() 784{ 785 m_lastEditCommand.clear(); 786} 787 788// Returns whether caller should continue with "the default processing", which is the same as 789// the event handler NOT setting the return value to false 790bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy) 791{ 792 Node* target = findEventTargetFromSelection(); 793 if (!target) 794 return true; 795 796 RefPtr<Clipboard> clipboard = newGeneralClipboard(policy, m_frame); 797 798 ExceptionCode ec = 0; 799 RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard); 800 target->dispatchEvent(evt, ec); 801 bool noDefaultProcessing = evt->defaultPrevented(); 802 803 // invalidate clipboard here for security 804 clipboard->setAccessPolicy(ClipboardNumb); 805 806 return !noDefaultProcessing; 807} 808 809Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const 810{ 811 Node* target = selection.start().element(); 812 if (!target) 813 target = m_frame->document()->body(); 814 if (!target) 815 return 0; 816 return target->shadowAncestorNode(); 817 818} 819 820Node* Editor::findEventTargetFromSelection() const 821{ 822 return findEventTargetFrom(m_frame->selection()->selection()); 823} 824 825void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction) 826{ 827 switch (m_frame->selection()->selectionType()) { 828 case VisibleSelection::NoSelection: 829 // do nothing 830 break; 831 case VisibleSelection::CaretSelection: 832 computeAndSetTypingStyle(style, editingAction); 833 break; 834 case VisibleSelection::RangeSelection: 835 if (style) 836 applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction)); 837 break; 838 } 839} 840 841bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range) 842{ 843 return client()->shouldApplyStyle(style, range); 844} 845 846void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction) 847{ 848 switch (m_frame->selection()->selectionType()) { 849 case VisibleSelection::NoSelection: 850 // do nothing 851 break; 852 case VisibleSelection::CaretSelection: 853 case VisibleSelection::RangeSelection: 854 if (style) 855 applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties)); 856 break; 857 } 858} 859 860void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction) 861{ 862 if (!style || !style->length() || !canEditRichly()) 863 return; 864 865 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get())) 866 applyStyle(style, editingAction); 867} 868 869void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction) 870{ 871 if (!style || !style->length() || !canEditRichly()) 872 return; 873 874 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get())) 875 applyParagraphStyle(style, editingAction); 876} 877 878bool Editor::selectionStartHasStyle(int propertyID, const String& value) const 879{ 880 RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value); 881 RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); 882 if (!selectionStyle || !selectionStyle->style()) 883 return false; 884 return style->triStateOfStyle(selectionStyle->style()) == TrueTriState; 885} 886 887TriState Editor::selectionHasStyle(int propertyID, const String& value) const 888{ 889 RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value); 890 if (!m_frame->selection()->isCaretOrRange()) 891 return FalseTriState; 892 893 if (m_frame->selection()->isCaret()) { 894 RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); 895 if (!selectionStyle || !selectionStyle->style()) 896 return FalseTriState; 897 return style->triStateOfStyle(selectionStyle->style()); 898 } 899 900 TriState state = FalseTriState; 901 for (Node* node = m_frame->selection()->start().deprecatedNode(); node; node = node->traverseNextNode()) { 902 RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node); 903 if (nodeStyle) { 904 TriState nodeState = style->triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties); 905 if (node == m_frame->selection()->start().deprecatedNode()) 906 state = nodeState; 907 else if (state != nodeState && node->isTextNode()) { 908 state = MixedTriState; 909 break; 910 } 911 } 912 if (node == m_frame->selection()->end().deprecatedNode()) 913 break; 914 } 915 916 return state; 917} 918 919static bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) 920{ 921 RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor); 922 if (!cssValue) 923 return true; 924 925 if (!cssValue->isPrimitiveValue()) 926 return false; 927 CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue.get()); 928 929 if (value->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR) 930 return !alphaChannel(value->getRGBA32Value()); 931 932 return value->getIdent() == CSSValueTransparent; 933} 934 935String Editor::selectionStartCSSPropertyValue(int propertyID) 936{ 937 RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); 938 if (!selectionStyle || !selectionStyle->style()) 939 return String(); 940 941 String value = selectionStyle->style()->getPropertyValue(propertyID); 942 943 // If background color is transparent, traverse parent nodes until we hit a different value or document root 944 // Also, if the selection is a range, ignore the background color at the start of selection, 945 // and find the background color of the common ancestor. 946 if (propertyID == CSSPropertyBackgroundColor && (m_frame->selection()->isRange() || hasTransparentBackgroundColor(selectionStyle->style()))) { 947 RefPtr<Range> range(m_frame->selection()->toNormalizedRange()); 948 ExceptionCode ec = 0; 949 for (Node* ancestor = range->commonAncestorContainer(ec); ancestor; ancestor = ancestor->parentNode()) { 950 RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor); 951 if (!hasTransparentBackgroundColor(ancestorStyle.get())) 952 return ancestorStyle->getPropertyValue(CSSPropertyBackgroundColor); 953 } 954 } 955 956 if (propertyID == CSSPropertyFontSize) { 957 RefPtr<CSSValue> cssValue = selectionStyle->style()->getPropertyCSSValue(CSSPropertyFontSize); 958 if (cssValue->isPrimitiveValue()) { 959 value = String::number(legacyFontSizeFromCSSValue(m_frame->document(), static_cast<CSSPrimitiveValue*>(cssValue.get()), 960 selectionStyle->shouldUseFixedDefaultFontSize(), AlwaysUseLegacyFontSize)); 961 } 962 } 963 964 return value; 965} 966 967void Editor::indent() 968{ 969 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent)); 970} 971 972void Editor::outdent() 973{ 974 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent)); 975} 976 977static void dispatchEditableContentChangedEvents(const EditCommand& command) 978{ 979 Element* startRoot = command.startingRootEditableElement(); 980 Element* endRoot = command.endingRootEditableElement(); 981 ExceptionCode ec; 982 if (startRoot) 983 startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec); 984 if (endRoot && endRoot != startRoot) 985 endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec); 986} 987 988void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) 989{ 990 m_frame->document()->updateLayout(); 991 992 dispatchEditableContentChangedEvents(*cmd); 993 VisibleSelection newSelection(cmd->endingSelection()); 994 995 m_spellingCorrector->respondToAppliedEditing(cmd.get()); 996 997 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. 998 changeSelectionAfterCommand(newSelection, false, false); 999 1000 if (!cmd->preservesTypingStyle()) 1001 m_frame->selection()->clearTypingStyle(); 1002 1003 // Command will be equal to last edit command only in the case of typing 1004 if (m_lastEditCommand.get() == cmd) 1005 ASSERT(cmd->isTypingCommand()); 1006 else { 1007 // Only register a new undo command if the command passed in is 1008 // different from the last command 1009 m_lastEditCommand = cmd; 1010 if (client()) 1011 client()->registerCommandForUndo(m_lastEditCommand); 1012 } 1013 respondToChangedContents(newSelection); 1014} 1015 1016void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) 1017{ 1018 m_frame->document()->updateLayout(); 1019 1020 dispatchEditableContentChangedEvents(*cmd); 1021 1022 VisibleSelection newSelection(cmd->startingSelection()); 1023 changeSelectionAfterCommand(newSelection, true, true); 1024 1025 m_lastEditCommand = 0; 1026 if (client()) 1027 client()->registerCommandForRedo(cmd); 1028 respondToChangedContents(newSelection); 1029} 1030 1031void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) 1032{ 1033 m_frame->document()->updateLayout(); 1034 1035 dispatchEditableContentChangedEvents(*cmd); 1036 1037 VisibleSelection newSelection(cmd->endingSelection()); 1038 changeSelectionAfterCommand(newSelection, true, true); 1039 1040 m_lastEditCommand = 0; 1041 if (client()) 1042 client()->registerCommandForUndo(cmd); 1043 respondToChangedContents(newSelection); 1044} 1045 1046Editor::Editor(Frame* frame) 1047 : m_frame(frame) 1048 , m_deleteButtonController(adoptPtr(new DeleteButtonController(frame))) 1049 , m_ignoreCompositionSelectionChange(false) 1050 , m_shouldStartNewKillRingSequence(false) 1051 // This is off by default, since most editors want this behavior (this matches IE but not FF). 1052 , m_shouldStyleWithCSS(false) 1053 , m_killRing(adoptPtr(new KillRing)) 1054 , m_spellChecker(adoptPtr(new SpellChecker(frame))) 1055 , m_spellingCorrector(adoptPtr(new SpellingCorrectionController(frame))) 1056 , m_areMarkedTextMatchesHighlighted(false) 1057{ 1058} 1059 1060Editor::~Editor() 1061{ 1062} 1063 1064void Editor::clear() 1065{ 1066 m_compositionNode = 0; 1067 m_customCompositionUnderlines.clear(); 1068 m_shouldStyleWithCSS = false; 1069} 1070 1071bool Editor::insertText(const String& text, Event* triggeringEvent) 1072{ 1073 return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent); 1074} 1075 1076bool Editor::insertTextForConfirmedComposition(const String& text) 1077{ 1078 return m_frame->eventHandler()->handleTextInputEvent(text, 0, TextEventInputComposition); 1079} 1080 1081bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent) 1082{ 1083 if (text.isEmpty()) 1084 return false; 1085 1086 VisibleSelection selection = selectionForCommand(triggeringEvent); 1087 if (!selection.isContentEditable()) 1088 return false; 1089 RefPtr<Range> range = selection.toNormalizedRange(); 1090 1091 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped)) 1092 return true; 1093 1094 if (!text.isEmpty()) 1095 updateMarkersForWordsAffectedByEditing(text[0]); 1096 1097 bool shouldConsiderApplyingAutocorrection = false; 1098 if (text == " " || text == "\t") 1099 shouldConsiderApplyingAutocorrection = true; 1100 1101 if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0])) 1102 shouldConsiderApplyingAutocorrection = true; 1103 1104 bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate(); 1105 1106 // Get the selection to use for the event that triggered this insertText. 1107 // If the event handler changed the selection, we may want to use a different selection 1108 // that is contained in the event target. 1109 selection = selectionForCommand(triggeringEvent); 1110 if (selection.isContentEditable()) { 1111 if (Node* selectionStart = selection.start().deprecatedNode()) { 1112 RefPtr<Document> document = selectionStart->document(); 1113 1114 // Insert the text 1115 TypingCommand::Options options = 0; 1116 if (selectInsertedText) 1117 options |= TypingCommand::SelectInsertedText; 1118 if (autocorrectionWasApplied) 1119 options |= TypingCommand::RetainAutocorrectionIndicator; 1120 TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); 1121 1122 // Reveal the current selection 1123 if (Frame* editedFrame = document->frame()) 1124 if (Page* page = editedFrame->page()) 1125 page->focusController()->focusedOrMainFrame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); 1126 } 1127 } 1128 1129 return true; 1130} 1131 1132bool Editor::insertLineBreak() 1133{ 1134 if (!canEdit()) 1135 return false; 1136 1137 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped)) 1138 return true; 1139 1140 bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate(); 1141 TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); 1142 revealSelectionAfterEditingOperation(); 1143 1144 return true; 1145} 1146 1147bool Editor::insertParagraphSeparator() 1148{ 1149 if (!canEdit()) 1150 return false; 1151 1152 if (!canEditRichly()) 1153 return insertLineBreak(); 1154 1155 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped)) 1156 return true; 1157 1158 bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate(); 1159 TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); 1160 revealSelectionAfterEditingOperation(); 1161 1162 return true; 1163} 1164 1165void Editor::cut() 1166{ 1167 if (tryDHTMLCut()) 1168 return; // DHTML did the whole operation 1169 if (!canCut()) { 1170 systemBeep(); 1171 return; 1172 } 1173 RefPtr<Range> selection = selectedRange(); 1174 if (shouldDeleteRange(selection.get())) { 1175 updateMarkersForWordsAffectedByEditing(true); 1176 if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode())) 1177 Pasteboard::generalPasteboard()->writePlainText(selectedText()); 1178 else 1179 Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame); 1180 didWriteSelectionToPasteboard(); 1181 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 1182 } 1183} 1184 1185void Editor::copy() 1186{ 1187 if (tryDHTMLCopy()) 1188 return; // DHTML did the whole operation 1189 if (!canCopy()) { 1190 systemBeep(); 1191 return; 1192 } 1193 1194 if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode())) 1195 Pasteboard::generalPasteboard()->writePlainText(selectedText()); 1196 else { 1197 Document* document = m_frame->document(); 1198 if (HTMLImageElement* imageElement = imageElementFromImageDocument(document)) 1199 Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title()); 1200 else 1201 Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame); 1202 } 1203 1204 didWriteSelectionToPasteboard(); 1205} 1206 1207void Editor::paste() 1208{ 1209 ASSERT(m_frame->document()); 1210 if (tryDHTMLPaste()) 1211 return; // DHTML did the whole operation 1212 if (!canPaste()) 1213 return; 1214 updateMarkersForWordsAffectedByEditing(false); 1215 CachedResourceLoader* loader = m_frame->document()->cachedResourceLoader(); 1216 loader->setAllowStaleResources(true); 1217 if (m_frame->selection()->isContentRichlyEditable()) 1218 pasteWithPasteboard(Pasteboard::generalPasteboard(), true); 1219 else 1220 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 1221 loader->setAllowStaleResources(false); 1222} 1223 1224void Editor::pasteAsPlainText() 1225{ 1226 if (tryDHTMLPaste()) 1227 return; 1228 if (!canPaste()) 1229 return; 1230 updateMarkersForWordsAffectedByEditing(false); 1231 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 1232} 1233 1234void Editor::performDelete() 1235{ 1236 if (!canDelete()) { 1237 systemBeep(); 1238 return; 1239 } 1240 1241 addToKillRing(selectedRange().get(), false); 1242 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 1243 1244 // clear the "start new kill ring sequence" setting, because it was set to true 1245 // when the selection was updated by deleting the range 1246 setStartNewKillRingSequence(false); 1247} 1248 1249void Editor::copyURL(const KURL& url, const String& title) 1250{ 1251 Pasteboard::generalPasteboard()->writeURL(url, title, m_frame); 1252} 1253 1254void Editor::copyImage(const HitTestResult& result) 1255{ 1256 KURL url = result.absoluteLinkURL(); 1257 if (url.isEmpty()) 1258 url = result.absoluteImageURL(); 1259 1260 Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString()); 1261} 1262 1263bool Editor::isContinuousSpellCheckingEnabled() 1264{ 1265 return client() && client()->isContinuousSpellCheckingEnabled(); 1266} 1267 1268void Editor::toggleContinuousSpellChecking() 1269{ 1270 if (client()) 1271 client()->toggleContinuousSpellChecking(); 1272} 1273 1274bool Editor::isGrammarCheckingEnabled() 1275{ 1276 return client() && client()->isGrammarCheckingEnabled(); 1277} 1278 1279void Editor::toggleGrammarChecking() 1280{ 1281 if (client()) 1282 client()->toggleGrammarChecking(); 1283} 1284 1285int Editor::spellCheckerDocumentTag() 1286{ 1287 return client() ? client()->spellCheckerDocumentTag() : 0; 1288} 1289 1290#if USE(AUTOMATIC_TEXT_REPLACEMENT) 1291 1292void Editor::uppercaseWord() 1293{ 1294 if (client()) 1295 client()->uppercaseWord(); 1296} 1297 1298void Editor::lowercaseWord() 1299{ 1300 if (client()) 1301 client()->lowercaseWord(); 1302} 1303 1304void Editor::capitalizeWord() 1305{ 1306 if (client()) 1307 client()->capitalizeWord(); 1308} 1309 1310void Editor::showSubstitutionsPanel() 1311{ 1312 if (!client()) { 1313 LOG_ERROR("No NSSpellChecker"); 1314 return; 1315 } 1316 1317 if (client()->substitutionsPanelIsShowing()) { 1318 client()->showSubstitutionsPanel(false); 1319 return; 1320 } 1321 client()->showSubstitutionsPanel(true); 1322} 1323 1324bool Editor::substitutionsPanelIsShowing() 1325{ 1326 if (!client()) 1327 return false; 1328 return client()->substitutionsPanelIsShowing(); 1329} 1330 1331void Editor::toggleSmartInsertDelete() 1332{ 1333 if (client()) 1334 client()->toggleSmartInsertDelete(); 1335} 1336 1337bool Editor::isAutomaticQuoteSubstitutionEnabled() 1338{ 1339 return client() && client()->isAutomaticQuoteSubstitutionEnabled(); 1340} 1341 1342void Editor::toggleAutomaticQuoteSubstitution() 1343{ 1344 if (client()) 1345 client()->toggleAutomaticQuoteSubstitution(); 1346} 1347 1348bool Editor::isAutomaticLinkDetectionEnabled() 1349{ 1350 return client() && client()->isAutomaticLinkDetectionEnabled(); 1351} 1352 1353void Editor::toggleAutomaticLinkDetection() 1354{ 1355 if (client()) 1356 client()->toggleAutomaticLinkDetection(); 1357} 1358 1359bool Editor::isAutomaticDashSubstitutionEnabled() 1360{ 1361 return client() && client()->isAutomaticDashSubstitutionEnabled(); 1362} 1363 1364void Editor::toggleAutomaticDashSubstitution() 1365{ 1366 if (client()) 1367 client()->toggleAutomaticDashSubstitution(); 1368} 1369 1370bool Editor::isAutomaticTextReplacementEnabled() 1371{ 1372 return client() && client()->isAutomaticTextReplacementEnabled(); 1373} 1374 1375void Editor::toggleAutomaticTextReplacement() 1376{ 1377 if (client()) 1378 client()->toggleAutomaticTextReplacement(); 1379} 1380 1381bool Editor::isAutomaticSpellingCorrectionEnabled() 1382{ 1383 return m_spellingCorrector->isAutomaticSpellingCorrectionEnabled(); 1384} 1385 1386void Editor::toggleAutomaticSpellingCorrection() 1387{ 1388 if (client()) 1389 client()->toggleAutomaticSpellingCorrection(); 1390} 1391 1392#endif 1393 1394bool Editor::shouldEndEditing(Range* range) 1395{ 1396 return client() && client()->shouldEndEditing(range); 1397} 1398 1399bool Editor::shouldBeginEditing(Range* range) 1400{ 1401 return client() && client()->shouldBeginEditing(range); 1402} 1403 1404void Editor::clearUndoRedoOperations() 1405{ 1406 if (client()) 1407 client()->clearUndoRedoOperations(); 1408} 1409 1410bool Editor::canUndo() 1411{ 1412 return client() && client()->canUndo(); 1413} 1414 1415void Editor::undo() 1416{ 1417 if (client()) 1418 client()->undo(); 1419} 1420 1421bool Editor::canRedo() 1422{ 1423 return client() && client()->canRedo(); 1424} 1425 1426void Editor::redo() 1427{ 1428 if (client()) 1429 client()->redo(); 1430} 1431 1432void Editor::didBeginEditing() 1433{ 1434 if (client()) 1435 client()->didBeginEditing(); 1436} 1437 1438void Editor::didEndEditing() 1439{ 1440 if (client()) 1441 client()->didEndEditing(); 1442} 1443 1444void Editor::didWriteSelectionToPasteboard() 1445{ 1446 if (client()) 1447 client()->didWriteSelectionToPasteboard(); 1448} 1449 1450void Editor::toggleBold() 1451{ 1452 command("ToggleBold").execute(); 1453} 1454 1455void Editor::toggleUnderline() 1456{ 1457 command("ToggleUnderline").execute(); 1458} 1459 1460void Editor::setBaseWritingDirection(WritingDirection direction) 1461{ 1462 Node* focusedNode = frame()->document()->focusedNode(); 1463 if (focusedNode && (focusedNode->hasTagName(textareaTag) || (focusedNode->hasTagName(inputTag) && static_cast<HTMLInputElement*>(focusedNode)->isTextField()))) { 1464 if (direction == NaturalWritingDirection) 1465 return; 1466 toHTMLElement(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl"); 1467 frame()->document()->updateStyleIfNeeded(); 1468 return; 1469 } 1470 1471 RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); 1472 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false); 1473 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection); 1474} 1475 1476void Editor::selectComposition() 1477{ 1478 RefPtr<Range> range = compositionRange(); 1479 if (!range) 1480 return; 1481 1482 // The composition can start inside a composed character sequence, so we have to override checks. 1483 // See <http://bugs.webkit.org/show_bug.cgi?id=15781> 1484 VisibleSelection selection; 1485 selection.setWithoutValidation(range->startPosition(), range->endPosition()); 1486 m_frame->selection()->setSelection(selection, 0); 1487} 1488 1489void Editor::confirmComposition() 1490{ 1491 if (!m_compositionNode) 1492 return; 1493 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false); 1494} 1495 1496void Editor::confirmCompositionWithoutDisturbingSelection() 1497{ 1498 if (!m_compositionNode) 1499 return; 1500 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true); 1501} 1502 1503void Editor::confirmComposition(const String& text) 1504{ 1505 confirmComposition(text, false); 1506} 1507 1508void Editor::confirmComposition(const String& text, bool preserveSelection) 1509{ 1510 UserTypingGestureIndicator typingGestureIndicator(m_frame); 1511 1512 setIgnoreCompositionSelectionChange(true); 1513 1514 VisibleSelection oldSelection = m_frame->selection()->selection(); 1515 1516 selectComposition(); 1517 1518 if (m_frame->selection()->isNone()) { 1519 setIgnoreCompositionSelectionChange(false); 1520 return; 1521 } 1522 1523 // Dispatch a compositionend event to the focused node. 1524 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of 1525 // the DOM Event specification. 1526 Node* target = m_frame->document()->focusedNode(); 1527 if (target) { 1528 RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text); 1529 ExceptionCode ec = 0; 1530 target->dispatchEvent(event, ec); 1531 } 1532 1533 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input 1534 // will delete the old composition with an optimized replace operation. 1535 if (text.isEmpty()) 1536 TypingCommand::deleteSelection(m_frame->document(), 0); 1537 1538 m_compositionNode = 0; 1539 m_customCompositionUnderlines.clear(); 1540 1541 insertTextForConfirmedComposition(text); 1542 1543 if (preserveSelection) { 1544 m_frame->selection()->setSelection(oldSelection, 0); 1545 // An open typing command that disagrees about current selection would cause issues with typing later on. 1546 TypingCommand::closeTyping(m_lastEditCommand.get()); 1547 } 1548 1549 setIgnoreCompositionSelectionChange(false); 1550} 1551 1552void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) 1553{ 1554 UserTypingGestureIndicator typingGestureIndicator(m_frame); 1555 1556 setIgnoreCompositionSelectionChange(true); 1557 1558 // Updates styles before setting selection for composition to prevent 1559 // inserting the previous composition text into text nodes oddly. 1560 // See https://bugs.webkit.org/show_bug.cgi?id=46868 1561 m_frame->document()->updateStyleIfNeeded(); 1562 1563 selectComposition(); 1564 1565 if (m_frame->selection()->isNone()) { 1566 setIgnoreCompositionSelectionChange(false); 1567 return; 1568 } 1569 1570 Node* target = m_frame->document()->focusedNode(); 1571 if (target) { 1572 // Dispatch an appropriate composition event to the focused node. 1573 // We check the composition status and choose an appropriate composition event since this 1574 // function is used for three purposes: 1575 // 1. Starting a new composition. 1576 // Send a compositionstart event when this function creates a new composition node, i.e. 1577 // m_compositionNode == 0 && !text.isEmpty(). 1578 // 2. Updating the existing composition node. 1579 // Send a compositionupdate event when this function updates the existing composition 1580 // node, i.e. m_compositionNode != 0 && !text.isEmpty(). 1581 // 3. Canceling the ongoing composition. 1582 // Send a compositionend event when function deletes the existing composition node, i.e. 1583 // m_compositionNode != 0 && test.isEmpty(). 1584 RefPtr<CompositionEvent> event; 1585 if (!m_compositionNode) { 1586 // We should send a compositionstart event only when the given text is not empty because this 1587 // function doesn't create a composition node when the text is empty. 1588 if (!text.isEmpty()) 1589 event = CompositionEvent::create(eventNames().compositionstartEvent, m_frame->domWindow(), text); 1590 } else { 1591 if (!text.isEmpty()) 1592 event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text); 1593 else 1594 event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text); 1595 } 1596 ExceptionCode ec = 0; 1597 if (event.get()) 1598 target->dispatchEvent(event, ec); 1599 } 1600 1601 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input 1602 // will delete the old composition with an optimized replace operation. 1603 if (text.isEmpty()) 1604 TypingCommand::deleteSelection(m_frame->document(), TypingCommand::PreventSpellChecking); 1605 1606 m_compositionNode = 0; 1607 m_customCompositionUnderlines.clear(); 1608 1609 if (!text.isEmpty()) { 1610 TypingCommand::insertText(m_frame->document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); 1611 1612 // Find out what node has the composition now. 1613 Position base = m_frame->selection()->base().downstream(); 1614 Position extent = m_frame->selection()->extent(); 1615 Node* baseNode = base.deprecatedNode(); 1616 unsigned baseOffset = base.deprecatedEditingOffset(); 1617 Node* extentNode = extent.deprecatedNode(); 1618 unsigned extentOffset = extent.deprecatedEditingOffset(); 1619 1620 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) { 1621 m_compositionNode = static_cast<Text*>(baseNode); 1622 m_compositionStart = baseOffset; 1623 m_compositionEnd = extentOffset; 1624 m_customCompositionUnderlines = underlines; 1625 size_t numUnderlines = m_customCompositionUnderlines.size(); 1626 for (size_t i = 0; i < numUnderlines; ++i) { 1627 m_customCompositionUnderlines[i].startOffset += baseOffset; 1628 m_customCompositionUnderlines[i].endOffset += baseOffset; 1629 } 1630 if (baseNode->renderer()) 1631 baseNode->renderer()->repaint(); 1632 1633 unsigned start = min(baseOffset + selectionStart, extentOffset); 1634 unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset); 1635 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); 1636 m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false); 1637 } 1638 } 1639 1640 setIgnoreCompositionSelectionChange(false); 1641} 1642 1643void Editor::ignoreSpelling() 1644{ 1645 if (!client()) 1646 return; 1647 1648 RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange(); 1649 if (selectedRange) 1650 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 1651 1652 String text = selectedText(); 1653 ASSERT(text.length()); 1654 textChecker()->ignoreWordInSpellDocument(text); 1655} 1656 1657void Editor::learnSpelling() 1658{ 1659 if (!client()) 1660 return; 1661 1662 // FIXME: On Mac OS X, when use "learn" button on "Spelling and Grammar" panel, we don't call this function. It should remove misspelling markers around the learned word, see <rdar://problem/5396072>. 1663 1664 RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange(); 1665 if (selectedRange) 1666 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 1667 1668 String text = selectedText(); 1669 ASSERT(text.length()); 1670 textChecker()->learnWord(text); 1671} 1672 1673void Editor::advanceToNextMisspelling(bool startBeforeSelection) 1674{ 1675 ExceptionCode ec = 0; 1676 1677 // The basic approach is to search in two phases - from the selection end to the end of the doc, and 1678 // then we wrap and search from the doc start to (approximately) where we started. 1679 1680 // Start at the end of the selection, search to edge of document. Starting at the selection end makes 1681 // repeated "check spelling" commands work. 1682 VisibleSelection selection(frame()->selection()->selection()); 1683 RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document())); 1684 1685 bool startedWithSelection = false; 1686 if (selection.start().deprecatedNode()) { 1687 startedWithSelection = true; 1688 if (startBeforeSelection) { 1689 VisiblePosition start(selection.visibleStart()); 1690 // We match AppKit's rule: Start 1 character before the selection. 1691 VisiblePosition oneBeforeStart = start.previous(); 1692 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start); 1693 } else 1694 setStart(spellingSearchRange.get(), selection.visibleEnd()); 1695 } 1696 1697 Position position = spellingSearchRange->startPosition(); 1698 if (!isEditablePosition(position)) { 1699 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the 1700 // selection is editable. 1701 // This can happen in Mail for a mix of non-editable and editable content (like Stationary), 1702 // when spell checking the whole document before sending the message. 1703 // In that case the document might not be editable, but there are editable pockets that need to be spell checked. 1704 1705 position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent(); 1706 if (position.isNull()) 1707 return; 1708 1709 Position rangeCompliantPosition = position.parentAnchoredEquivalent(); 1710 spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), ec); 1711 startedWithSelection = false; // won't need to wrap 1712 } 1713 1714 // topNode defines the whole range we want to operate on 1715 Node* topNode = highestEditableRoot(position); 1716 // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>) 1717 spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec); 1718 1719 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking 1720 // at a word boundary. Going back by one char and then forward by a word does the trick. 1721 if (startedWithSelection) { 1722 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous(); 1723 if (oneBeforeStart.isNotNull()) 1724 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart)); 1725 // else we were already at the start of the editable node 1726 } 1727 1728 if (spellingSearchRange->collapsed(ec)) 1729 return; // nothing to search in 1730 1731 // Get the spell checker if it is available 1732 if (!client()) 1733 return; 1734 1735 // We go to the end of our first range instead of the start of it, just to be sure 1736 // we don't get foiled by any word boundary problems at the start. It means we might 1737 // do a tiny bit more searching. 1738 Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec); 1739 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec); 1740 1741 int misspellingOffset = 0; 1742 GrammarDetail grammarDetail; 1743 int grammarPhraseOffset = 0; 1744 RefPtr<Range> grammarSearchRange; 1745 String badGrammarPhrase; 1746 String misspelledWord; 1747 1748#if USE(UNIFIED_TEXT_CHECKING) 1749 grammarSearchRange = spellingSearchRange->cloneRange(ec); 1750 bool isSpelling = true; 1751 int foundOffset = 0; 1752 String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); 1753 if (isSpelling) { 1754 misspelledWord = foundItem; 1755 misspellingOffset = foundOffset; 1756 } else { 1757 badGrammarPhrase = foundItem; 1758 grammarPhraseOffset = foundOffset; 1759 } 1760#else 1761 RefPtr<Range> firstMisspellingRange; 1762 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); 1763 1764#if USE(GRAMMAR_CHECKING) 1765 // Search for bad grammar that occurs prior to the next misspelled word (if any) 1766 grammarSearchRange = spellingSearchRange->cloneRange(ec); 1767 if (!misspelledWord.isEmpty()) { 1768 // Stop looking at start of next misspelled word 1769 CharacterIterator chars(grammarSearchRange.get()); 1770 chars.advance(misspellingOffset); 1771 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec); 1772 } 1773 1774 if (isGrammarCheckingEnabled()) 1775 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); 1776#endif 1777#endif 1778 1779 // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the 1780 // block rather than at a selection). 1781 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { 1782 spellingSearchRange->setStart(topNode, 0, ec); 1783 // going until the end of the very first chunk we tested is far enough 1784 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec); 1785 1786#if USE(UNIFIED_TEXT_CHECKING) 1787 grammarSearchRange = spellingSearchRange->cloneRange(ec); 1788 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); 1789 if (isSpelling) { 1790 misspelledWord = foundItem; 1791 misspellingOffset = foundOffset; 1792 } else { 1793 badGrammarPhrase = foundItem; 1794 grammarPhraseOffset = foundOffset; 1795 } 1796#else 1797 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); 1798 1799#if USE(GRAMMAR_CHECKING) 1800 grammarSearchRange = spellingSearchRange->cloneRange(ec); 1801 if (!misspelledWord.isEmpty()) { 1802 // Stop looking at start of next misspelled word 1803 CharacterIterator chars(grammarSearchRange.get()); 1804 chars.advance(misspellingOffset); 1805 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec); 1806 } 1807 1808 if (isGrammarCheckingEnabled()) 1809 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); 1810#endif 1811#endif 1812 } 1813 1814 if (!badGrammarPhrase.isEmpty()) { 1815 ASSERT(WTF_USE_GRAMMAR_CHECKING); 1816 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar 1817 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling 1818 // panel, and store a marker so we draw the green squiggle later. 1819 1820 ASSERT(badGrammarPhrase.length() > 0); 1821 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0); 1822 1823 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph 1824 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length); 1825 frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY)); 1826 frame()->selection()->revealSelection(); 1827 1828 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail); 1829 frame()->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription); 1830 } else if (!misspelledWord.isEmpty()) { 1831 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store 1832 // a marker so we draw the red squiggle later. 1833 1834 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length()); 1835 frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM)); 1836 frame()->selection()->revealSelection(); 1837 1838 client()->updateSpellingUIWithMisspelledWord(misspelledWord); 1839 frame()->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling); 1840 } 1841} 1842 1843bool Editor::isSelectionMisspelled() 1844{ 1845 String selectedString = selectedText(); 1846 int length = selectedString.length(); 1847 if (!length) 1848 return false; 1849 1850 if (!client()) 1851 return false; 1852 1853 int misspellingLocation = -1; 1854 int misspellingLength = 0; 1855 textChecker()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength); 1856 1857 // The selection only counts as misspelled if the selected text is exactly one misspelled word 1858 if (misspellingLength != length) 1859 return false; 1860 1861 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen). 1862 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work 1863 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling 1864 // or a grammar error. 1865 client()->updateSpellingUIWithMisspelledWord(selectedString); 1866 1867 return true; 1868} 1869 1870bool Editor::isSelectionUngrammatical() 1871{ 1872#if USE(GRAMMAR_CHECKING) 1873 Vector<String> ignoredGuesses; 1874 return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(ignoredGuesses); 1875#else 1876 return false; 1877#endif 1878} 1879 1880Vector<String> Editor::guessesForUngrammaticalSelection() 1881{ 1882#if USE(GRAMMAR_CHECKING) 1883 Vector<String> guesses; 1884 // Ignore the result of isUngrammatical; we just want the guesses, whether or not there are any 1885 TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(guesses); 1886 return guesses; 1887#else 1888 return Vector<String>(); 1889#endif 1890} 1891 1892Vector<String> Editor::guessesForMisspelledSelection() 1893{ 1894 String selectedString = selectedText(); 1895 ASSERT(selectedString.length()); 1896 1897 Vector<String> guesses; 1898 if (client()) 1899 textChecker()->getGuessesForWord(selectedString, String(), guesses); 1900 return guesses; 1901} 1902 1903Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical) 1904{ 1905#if USE(UNIFIED_TEXT_CHECKING) 1906 return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical); 1907#else 1908 misspelled = isSelectionMisspelled(); 1909 if (misspelled) { 1910 ungrammatical = false; 1911 return guessesForMisspelledSelection(); 1912 } 1913 if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) { 1914 ungrammatical = true; 1915 return guessesForUngrammaticalSelection(); 1916 } 1917 ungrammatical = false; 1918 return Vector<String>(); 1919#endif 1920} 1921 1922void Editor::showSpellingGuessPanel() 1923{ 1924 if (!client()) { 1925 LOG_ERROR("No NSSpellChecker"); 1926 return; 1927 } 1928 1929#ifndef BUILDING_ON_TIGER 1930 // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone 1931 // to match rest of OS X. 1932 if (client()->spellingUIIsShowing()) { 1933 client()->showSpellingUI(false); 1934 return; 1935 } 1936#endif 1937 1938 advanceToNextMisspelling(true); 1939 client()->showSpellingUI(true); 1940} 1941 1942bool Editor::spellingPanelIsShowing() 1943{ 1944 if (!client()) 1945 return false; 1946 return client()->spellingUIIsShowing(); 1947} 1948 1949void Editor::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection) 1950{ 1951 RefPtr<Range> selectedRange = movingSelection.toNormalizedRange(); 1952 if (selectedRange) { 1953 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 1954 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Grammar); 1955 } 1956} 1957 1958void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection) 1959{ 1960 bool markSpelling = isContinuousSpellCheckingEnabled(); 1961 bool markGrammar = markSpelling && isGrammarCheckingEnabled(); 1962 1963 if (markSpelling) { 1964 RefPtr<Range> unusedFirstMisspellingRange; 1965 markMisspellings(movingSelection, unusedFirstMisspellingRange); 1966 } 1967 1968 if (markGrammar) 1969 markBadGrammar(movingSelection); 1970} 1971 1972void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement) 1973{ 1974#if USE(UNIFIED_TEXT_CHECKING) 1975 m_spellingCorrector->applyPendingCorrection(selectionAfterTyping); 1976 1977 TextCheckingOptions textCheckingOptions = 0; 1978 if (isContinuousSpellCheckingEnabled()) 1979 textCheckingOptions |= MarkSpelling; 1980 1981#if USE(AUTOMATIC_TEXT_REPLACEMENT) 1982 if (doReplacement 1983 && (isAutomaticQuoteSubstitutionEnabled() 1984 || isAutomaticLinkDetectionEnabled() 1985 || isAutomaticDashSubstitutionEnabled() 1986 || isAutomaticTextReplacementEnabled() 1987 || ((textCheckingOptions & MarkSpelling) && isAutomaticSpellingCorrectionEnabled()))) 1988 textCheckingOptions |= PerformReplacement; 1989#endif 1990 if (!textCheckingOptions & (MarkSpelling | PerformReplacement)) 1991 return; 1992 1993 if (isGrammarCheckingEnabled()) 1994 textCheckingOptions |= MarkGrammar; 1995 1996 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)); 1997 if (textCheckingOptions & MarkGrammar) { 1998 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)); 1999 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get()); 2000 } else { 2001 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get()); 2002 } 2003 2004#else 2005 UNUSED_PARAM(selectionAfterTyping); 2006 UNUSED_PARAM(doReplacement); 2007 2008 if (!isContinuousSpellCheckingEnabled()) 2009 return; 2010 2011 // Check spelling of one word 2012 RefPtr<Range> misspellingRange; 2013 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange); 2014 2015 // Autocorrect the misspelled word. 2016 if (!misspellingRange) 2017 return; 2018 2019 // Get the misspelled word. 2020 const String misspelledWord = plainText(misspellingRange.get()); 2021 String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord); 2022 2023 // If autocorrected word is non empty, replace the misspelled word by this word. 2024 if (!autocorrectedString.isEmpty()) { 2025 VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM); 2026 if (newSelection != frame()->selection()->selection()) { 2027 if (!frame()->selection()->shouldChangeSelection(newSelection)) 2028 return; 2029 frame()->selection()->setSelection(newSelection); 2030 } 2031 2032 if (!frame()->editor()->shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped)) 2033 return; 2034 frame()->editor()->replaceSelectionWithText(autocorrectedString, false, false); 2035 2036 // Reset the charet one character further. 2037 frame()->selection()->moveTo(frame()->selection()->end()); 2038 frame()->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity); 2039 } 2040 2041 if (!isGrammarCheckingEnabled()) 2042 return; 2043 2044 // Check grammar of entire sentence 2045 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart))); 2046#endif 2047} 2048 2049void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange) 2050{ 2051 // This function is called with a selection already expanded to word boundaries. 2052 // Might be nice to assert that here. 2053 2054 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that 2055 // grammar checking can only be on if spell checking is also on. 2056 if (!isContinuousSpellCheckingEnabled()) 2057 return; 2058 2059 RefPtr<Range> searchRange(selection.toNormalizedRange()); 2060 if (!searchRange) 2061 return; 2062 2063 // If we're not in an editable node, bail. 2064 Node* editableNode = searchRange->startContainer(); 2065 if (!editableNode || !editableNode->rendererIsEditable()) 2066 return; 2067 2068 if (!isSpellCheckingEnabledFor(editableNode)) 2069 return; 2070 2071 // Get the spell checker if it is available 2072 if (!client()) 2073 return; 2074 2075 TextCheckingHelper checker(client(), searchRange); 2076 if (checkSpelling) 2077 checker.markAllMisspellings(firstMisspellingRange); 2078 else { 2079 ASSERT(WTF_USE_GRAMMAR_CHECKING); 2080 if (isGrammarCheckingEnabled()) 2081 checker.markAllBadGrammar(); 2082 } 2083} 2084 2085bool Editor::isSpellCheckingEnabledFor(Node* node) const 2086{ 2087 if (!node) 2088 return false; 2089 const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement(); 2090 if (!focusedElement) 2091 return false; 2092 return focusedElement->isSpellCheckingEnabled(); 2093} 2094 2095bool Editor::isSpellCheckingEnabledInFocusedNode() const 2096{ 2097 return isSpellCheckingEnabledFor(m_frame->selection()->start().deprecatedNode()); 2098} 2099 2100void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange) 2101{ 2102 markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange); 2103} 2104 2105void Editor::markBadGrammar(const VisibleSelection& selection) 2106{ 2107 ASSERT(WTF_USE_GRAMMAR_CHECKING); 2108 RefPtr<Range> firstMisspellingRange; 2109 markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange); 2110} 2111 2112void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCheckingOptions, Range* spellingRange, Range* grammarRange) 2113{ 2114#if USE(UNIFIED_TEXT_CHECKING) 2115 // There shouldn't be pending autocorrection at this moment. 2116 ASSERT(!m_spellingCorrector->hasPendingCorrection()); 2117 2118 bool shouldMarkSpelling = textCheckingOptions & MarkSpelling; 2119 bool shouldMarkGrammar = textCheckingOptions & MarkGrammar; 2120 bool shouldPerformReplacement = textCheckingOptions & PerformReplacement; 2121 bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel; 2122 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection); 2123 2124 // This function is called with selections already expanded to word boundaries. 2125 ExceptionCode ec = 0; 2126 if (!client() || !spellingRange || (shouldMarkGrammar && !grammarRange)) 2127 return; 2128 2129 // If we're not in an editable node, bail. 2130 Node* editableNode = spellingRange->startContainer(); 2131 if (!editableNode || !editableNode->rendererIsEditable()) 2132 return; 2133 2134 if (!isSpellCheckingEnabledFor(editableNode)) 2135 return; 2136 2137 // Expand the range to encompass entire paragraphs, since text checking needs that much context. 2138 int selectionOffset = 0; 2139 int ambiguousBoundaryOffset = -1; 2140 bool selectionChanged = false; 2141 bool restoreSelectionAfterChange = false; 2142 bool adjustSelectionForParagraphBoundaries = false; 2143 2144 TextCheckingParagraph spellingParagraph(spellingRange); 2145 TextCheckingParagraph grammarParagraph(shouldMarkGrammar ? grammarRange : 0); 2146 2147 if (shouldMarkGrammar ? (spellingParagraph.isRangeEmpty() && grammarParagraph.isEmpty()) : spellingParagraph.isEmpty()) 2148 return; 2149 2150 if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) { 2151 if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) { 2152 // Attempt to save the caret position so we can restore it later if needed 2153 Position caretPosition = m_frame->selection()->end(); 2154 int offset = spellingParagraph.offsetTo(caretPosition, ec); 2155 if (!ec) { 2156 selectionOffset = offset; 2157 restoreSelectionAfterChange = true; 2158 if (selectionOffset > 0 && (selectionOffset > spellingParagraph.textLength() || spellingParagraph.textCharAt(selectionOffset - 1) == newlineCharacter)) 2159 adjustSelectionForParagraphBoundaries = true; 2160 if (selectionOffset > 0 && selectionOffset <= spellingParagraph.textLength() && isAmbiguousBoundaryCharacter(spellingParagraph.textCharAt(selectionOffset - 1))) 2161 ambiguousBoundaryOffset = selectionOffset - 1; 2162 } 2163 } 2164 } 2165 2166 Vector<TextCheckingResult> results; 2167 if (shouldMarkGrammar) 2168 textChecker()->checkTextOfParagraph(grammarParagraph.textCharacters(), grammarParagraph.textLength(), 2169 textCheckingTypeMaskFor(textCheckingOptions), results); 2170 else 2171 textChecker()->checkTextOfParagraph(spellingParagraph.textCharacters(), spellingParagraph.textLength(), 2172 textCheckingTypeMaskFor(textCheckingOptions), results); 2173 2174 2175 // If this checking is only for showing correction panel, we shouldn't bother to mark misspellings. 2176 if (shouldShowCorrectionPanel) 2177 shouldMarkSpelling = false; 2178 2179 int offsetDueToReplacement = 0; 2180 2181 for (unsigned i = 0; i < results.size(); i++) { 2182 int spellingRangeEndOffset = spellingParagraph.checkingEnd() + offsetDueToReplacement; 2183 const TextCheckingResult* result = &results[i]; 2184 int resultLocation = result->location + offsetDueToReplacement; 2185 int resultLength = result->length; 2186 bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset; 2187 2188 // Only mark misspelling if: 2189 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false. 2190 // 2. Result falls within spellingRange. 2191 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark 2192 // "wouldn'" as misspelled right after apostrophe is typed. 2193 if (shouldMarkSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingParagraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) { 2194 ASSERT(resultLength > 0 && resultLocation >= 0); 2195 RefPtr<Range> misspellingRange = spellingParagraph.subrange(resultLocation, resultLength); 2196 if (!m_spellingCorrector->isSpellingMarkerAllowed(misspellingRange)) 2197 continue; 2198 misspellingRange->startContainer(ec)->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling); 2199 } else if (shouldMarkGrammar && result->type == TextCheckingTypeGrammar && grammarParagraph.checkingRangeCovers(resultLocation, resultLength)) { 2200 ASSERT(resultLength > 0 && resultLocation >= 0); 2201 for (unsigned j = 0; j < result->details.size(); j++) { 2202 const GrammarDetail* detail = &result->details[j]; 2203 ASSERT(detail->length > 0 && detail->location >= 0); 2204 if (grammarParagraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) { 2205 RefPtr<Range> badGrammarRange = grammarParagraph.subrange(resultLocation + detail->location, detail->length); 2206 grammarRange->startContainer(ec)->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription); 2207 } 2208 } 2209 } else if (resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingParagraph.checkingStart() 2210 && (result->type == TextCheckingTypeLink 2211 || result->type == TextCheckingTypeQuote 2212 || result->type == TextCheckingTypeDash 2213 || result->type == TextCheckingTypeReplacement 2214 || result->type == TextCheckingTypeCorrection)) { 2215 // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation. 2216 ASSERT(resultLength > 0 && resultLocation >= 0); 2217 2218 if (shouldShowCorrectionPanel && (resultLocation + resultLength < spellingRangeEndOffset || result->type != TextCheckingTypeCorrection)) 2219 continue; 2220 2221 int replacementLength = result->replacement.length(); 2222 2223 // Apply replacement if: 2224 // 1. The replacement length is non-zero. 2225 // 2. The result doesn't end at an ambiguous boundary. 2226 // (FIXME: this is required until 6853027 is fixed and text checking can do this for us 2227 bool doReplacement = replacementLength > 0 && !resultEndsAtAmbiguousBoundary; 2228 RefPtr<Range> rangeToReplace = spellingParagraph.subrange(resultLocation, resultLength); 2229 VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM); 2230 2231 // adding links should be done only immediately after they are typed 2232 if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1) 2233 continue; 2234 2235 String replacedString; 2236 2237 // Don't correct spelling in an already-corrected word. 2238 if (result->type == TextCheckingTypeCorrection) { 2239 replacedString = plainText(rangeToReplace.get()); 2240 DocumentMarkerController* markers = m_frame->document()->markers(); 2241 if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::Replacement)) { 2242 doReplacement = false; 2243 m_spellingCorrector->recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement); 2244 } else if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::RejectedCorrection)) 2245 doReplacement = false; 2246 } 2247 2248 if (!(shouldPerformReplacement || shouldShowCorrectionPanel) || !doReplacement) 2249 continue; 2250 2251 if (shouldShowCorrectionPanel) { 2252 ASSERT(SUPPORT_AUTOCORRECTION_PANEL); 2253 // shouldShowCorrectionPanel can be true only when the panel is available. 2254 if (resultLocation + resultLength == spellingRangeEndOffset) { 2255 // We only show the correction panel on the last word. 2256 m_spellingCorrector->show(rangeToReplace, result->replacement); 2257 break; 2258 } 2259 // If this function is called for showing correction panel, we ignore other correction or replacement. 2260 continue; 2261 } 2262 2263 if (selectionToReplace != m_frame->selection()->selection()) { 2264 if (!m_frame->selection()->shouldChangeSelection(selectionToReplace)) 2265 continue; 2266 } 2267 2268 if (result->type == TextCheckingTypeLink) { 2269 m_frame->selection()->setSelection(selectionToReplace); 2270 selectionChanged = true; 2271 restoreSelectionAfterChange = false; 2272 if (canEditRichly()) 2273 applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement)); 2274 } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) { 2275 if (result->type == TextCheckingTypeCorrection) 2276 applyCommand(SpellingCorrectionCommand::create(rangeToReplace, result->replacement)); 2277 else { 2278 m_frame->selection()->setSelection(selectionToReplace); 2279 replaceSelectionWithText(result->replacement, false, false); 2280 } 2281 2282 if (AXObjectCache::accessibilityEnabled()) { 2283 if (Element* root = m_frame->selection()->selection().rootEditableElement()) 2284 m_frame->document()->axObjectCache()->postNotification(root->renderer(), AXObjectCache::AXAutocorrectionOccured, true); 2285 } 2286 2287 selectionChanged = true; 2288 offsetDueToReplacement += replacementLength - resultLength; 2289 if (resultLocation < selectionOffset) { 2290 selectionOffset += replacementLength - resultLength; 2291 if (ambiguousBoundaryOffset >= 0) 2292 ambiguousBoundaryOffset = selectionOffset - 1; 2293 } 2294 2295 // Add a marker so that corrections can easily be undone and won't be re-corrected. 2296 if (result->type == TextCheckingTypeCorrection) 2297 m_spellingCorrector->markCorrection(spellingParagraph.subrange(resultLocation, replacementLength), replacedString); 2298 } 2299 } 2300 } 2301 2302 if (selectionChanged) { 2303 // Restore the caret position if we have made any replacements 2304 spellingParagraph.expandRangeToNextEnd(); 2305 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= spellingParagraph.rangeLength()) { 2306 RefPtr<Range> selectionRange = spellingParagraph.subrange(0, selectionOffset); 2307 m_frame->selection()->moveTo(selectionRange->endPosition(), DOWNSTREAM); 2308 if (adjustSelectionForParagraphBoundaries) 2309 m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity); 2310 } else { 2311 // If this fails for any reason, the fallback is to go one position beyond the last replacement 2312 m_frame->selection()->moveTo(m_frame->selection()->end()); 2313 m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity); 2314 } 2315 } 2316#else 2317 ASSERT_NOT_REACHED(); 2318 UNUSED_PARAM(textCheckingOptions); 2319 UNUSED_PARAM(spellingRange); 2320 UNUSED_PARAM(grammarRange); 2321#endif // USE(UNIFIED_TEXT_CHECKING) 2322} 2323 2324void Editor::changeBackToReplacedString(const String& replacedString) 2325{ 2326#if USE(UNIFIED_TEXT_CHECKING) 2327 if (replacedString.isEmpty()) 2328 return; 2329 2330 RefPtr<Range> selection = selectedRange(); 2331 if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted)) 2332 return; 2333 2334 m_spellingCorrector->recordAutocorrectionResponseReversed(replacedString, selection); 2335 TextCheckingParagraph paragraph(selection); 2336 replaceSelectionWithText(replacedString, false, false); 2337 RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length()); 2338 changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String()); 2339 m_spellingCorrector->markReversed(changedRange.get()); 2340#else 2341 ASSERT_NOT_REACHED(); 2342 UNUSED_PARAM(replacedString); 2343#endif // USE(UNIFIED_TEXT_CHECKING) 2344} 2345 2346 2347void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection) 2348{ 2349#if USE(UNIFIED_TEXT_CHECKING) 2350 if (!isContinuousSpellCheckingEnabled()) 2351 return; 2352 TextCheckingOptions textCheckingOptions = MarkSpelling | CheckForCorrection; 2353 if (markGrammar && isGrammarCheckingEnabled()) 2354 textCheckingOptions |= MarkGrammar; 2355 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get()); 2356#else 2357 RefPtr<Range> firstMisspellingRange; 2358 markMisspellings(spellingSelection, firstMisspellingRange); 2359 if (markGrammar) 2360 markBadGrammar(grammarSelection); 2361#endif 2362} 2363 2364void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction) 2365{ 2366 m_spellingCorrector->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction); 2367} 2368 2369void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary) 2370{ 2371 if (!m_spellingCorrector->shouldRemoveMarkersUponEditing()) 2372 return; 2373 2374 // We want to remove the markers from a word if an editing command will change the word. This can happen in one of 2375 // several scenarios: 2376 // 1. Insert in the middle of a word. 2377 // 2. Appending non whitespace at the beginning of word. 2378 // 3. Appending non whitespace at the end of word. 2379 // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to 2380 // remove the markers on that word. 2381 // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of 2382 // selection, and remove words between the selection boundaries. 2383 // 2384 VisiblePosition startOfSelection = frame()->selection()->selection().start(); 2385 VisiblePosition endOfSelection = frame()->selection()->selection().end(); 2386 if (startOfSelection.isNull()) 2387 return; 2388 // First word is the word that ends after or on the start of selection. 2389 VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary); 2390 VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary); 2391 // Last word is the word that begins before or on the end of selection 2392 VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary); 2393 VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary); 2394 2395 if (startOfFirstWord.isNull()) { 2396 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); 2397 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); 2398 } 2399 2400 if (endOfLastWord.isNull()) { 2401 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); 2402 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); 2403 } 2404 2405 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection, 2406 // we choose next word as the first word. 2407 if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) { 2408 startOfFirstWord = nextWordPosition(startOfFirstWord); 2409 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); 2410 if (startOfFirstWord == endOfSelection) 2411 return; 2412 } 2413 2414 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection, 2415 // we choose previous word as the last word. 2416 if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) { 2417 startOfLastWord = previousWordPosition(startOfLastWord); 2418 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); 2419 if (endOfLastWord == startOfSelection) 2420 return; 2421 } 2422 2423 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull()) 2424 return; 2425 2426 // Now we remove markers on everything between startOfFirstWord and endOfLastWord. 2427 // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the 2428 // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant 2429 // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde, 2430 // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of 2431 // of marker that contains the word in question, and remove marker on that whole range. 2432 Document* document = m_frame->document(); 2433 RefPtr<Range> wordRange = Range::create(document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent()); 2434 2435 document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption, DocumentMarkerController::RemovePartiallyOverlappingMarker); 2436 document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement); 2437} 2438 2439PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) 2440{ 2441 Document* document = m_frame->documentAtPoint(windowPoint); 2442 if (!document) 2443 return 0; 2444 2445 Frame* frame = document->frame(); 2446 ASSERT(frame); 2447 FrameView* frameView = frame->view(); 2448 if (!frameView) 2449 return 0; 2450 IntPoint framePoint = frameView->windowToContents(windowPoint); 2451 VisibleSelection selection(frame->visiblePositionForPoint(framePoint)); 2452 return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement()); 2453} 2454 2455void Editor::revealSelectionAfterEditingOperation() 2456{ 2457 if (m_ignoreCompositionSelectionChange) 2458 return; 2459 2460 m_frame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); 2461} 2462 2463void Editor::setIgnoreCompositionSelectionChange(bool ignore) 2464{ 2465 if (m_ignoreCompositionSelectionChange == ignore) 2466 return; 2467 2468 m_ignoreCompositionSelectionChange = ignore; 2469 if (!ignore) 2470 revealSelectionAfterEditingOperation(); 2471} 2472 2473PassRefPtr<Range> Editor::compositionRange() const 2474{ 2475 if (!m_compositionNode) 2476 return 0; 2477 unsigned length = m_compositionNode->length(); 2478 unsigned start = min(m_compositionStart, length); 2479 unsigned end = min(max(start, m_compositionEnd), length); 2480 if (start >= end) 2481 return 0; 2482 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end); 2483} 2484 2485bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const 2486{ 2487 if (!m_compositionNode) 2488 return false; 2489 Position start = m_frame->selection()->start(); 2490 if (start.deprecatedNode() != m_compositionNode) 2491 return false; 2492 Position end = m_frame->selection()->end(); 2493 if (end.deprecatedNode() != m_compositionNode) 2494 return false; 2495 2496 if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart) 2497 return false; 2498 if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd) 2499 return false; 2500 2501 selectionStart = start.deprecatedEditingOffset() - m_compositionStart; 2502 selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd; 2503 return true; 2504} 2505 2506void Editor::transpose() 2507{ 2508 if (!canEdit()) 2509 return; 2510 2511 VisibleSelection selection = m_frame->selection()->selection(); 2512 if (!selection.isCaret()) 2513 return; 2514 2515 // Make a selection that goes back one character and forward two characters. 2516 VisiblePosition caret = selection.visibleStart(); 2517 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); 2518 VisiblePosition previous = next.previous(); 2519 if (next == previous) 2520 return; 2521 previous = previous.previous(); 2522 if (!inSameParagraph(next, previous)) 2523 return; 2524 RefPtr<Range> range = makeRange(previous, next); 2525 if (!range) 2526 return; 2527 VisibleSelection newSelection(range.get(), DOWNSTREAM); 2528 2529 // Transpose the two characters. 2530 String text = plainText(range.get()); 2531 if (text.length() != 2) 2532 return; 2533 String transposed = text.right(1) + text.left(1); 2534 2535 // Select the two characters. 2536 if (newSelection != m_frame->selection()->selection()) { 2537 if (!m_frame->selection()->shouldChangeSelection(newSelection)) 2538 return; 2539 m_frame->selection()->setSelection(newSelection); 2540 } 2541 2542 // Insert the transposed characters. 2543 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped)) 2544 return; 2545 replaceSelectionWithText(transposed, false, false); 2546} 2547 2548void Editor::addToKillRing(Range* range, bool prepend) 2549{ 2550 if (m_shouldStartNewKillRingSequence) 2551 killRing()->startNewSequence(); 2552 2553 String text = plainText(range); 2554 if (prepend) 2555 killRing()->prepend(text); 2556 else 2557 killRing()->append(text); 2558 m_shouldStartNewKillRingSequence = false; 2559} 2560 2561void Editor::startCorrectionPanelTimer() 2562{ 2563 m_spellingCorrector->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection); 2564} 2565 2566void Editor::handleCorrectionPanelResult(const String& correction) 2567{ 2568 m_spellingCorrector->handleCorrectionPanelResult(correction); 2569} 2570 2571 2572void Editor::dismissCorrectionPanelAsIgnored() 2573{ 2574 m_spellingCorrector->dismiss(ReasonForDismissingCorrectionPanelIgnored); 2575} 2576 2577bool Editor::insideVisibleArea(const IntPoint& point) const 2578{ 2579 if (m_frame->excludeFromTextSearch()) 2580 return false; 2581 2582 // Right now, we only check the visibility of a point for disconnected frames. For all other 2583 // frames, we assume visibility. 2584 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true); 2585 if (!frame->isDisconnected()) 2586 return true; 2587 2588 RenderPart* renderer = frame->ownerRenderer(); 2589 if (!renderer) 2590 return false; 2591 2592 RenderBlock* container = renderer->containingBlock(); 2593 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN)) 2594 return true; 2595 2596 IntRect rectInPageCoords = container->overflowClipRect(0, 0); 2597 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1, 2598 rectInPageCoords.width(), rectInPageCoords.height()); 2599 2600 return rectInFrameCoords.contains(point); 2601} 2602 2603bool Editor::insideVisibleArea(Range* range) const 2604{ 2605 if (!range) 2606 return true; 2607 2608 if (m_frame->excludeFromTextSearch()) 2609 return false; 2610 2611 // Right now, we only check the visibility of a range for disconnected frames. For all other 2612 // frames, we assume visibility. 2613 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true); 2614 if (!frame->isDisconnected()) 2615 return true; 2616 2617 RenderPart* renderer = frame->ownerRenderer(); 2618 if (!renderer) 2619 return false; 2620 2621 RenderBlock* container = renderer->containingBlock(); 2622 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN)) 2623 return true; 2624 2625 IntRect rectInPageCoords = container->overflowClipRect(0, 0); 2626 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1, 2627 rectInPageCoords.width(), rectInPageCoords.height()); 2628 IntRect resultRect = range->boundingBox(); 2629 2630 return rectInFrameCoords.contains(resultRect); 2631} 2632 2633PassRefPtr<Range> Editor::firstVisibleRange(const String& target, FindOptions options) 2634{ 2635 RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); 2636 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options & ~Backwards); 2637 ExceptionCode ec = 0; 2638 2639 while (!insideVisibleArea(resultRange.get())) { 2640 searchRange->setStartAfter(resultRange->endContainer(), ec); 2641 if (searchRange->startContainer() == searchRange->endContainer()) 2642 return Range::create(m_frame->document()); 2643 resultRange = findPlainText(searchRange.get(), target, options & ~Backwards); 2644 } 2645 2646 return resultRange; 2647} 2648 2649PassRefPtr<Range> Editor::lastVisibleRange(const String& target, FindOptions options) 2650{ 2651 RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); 2652 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options | Backwards); 2653 ExceptionCode ec = 0; 2654 2655 while (!insideVisibleArea(resultRange.get())) { 2656 searchRange->setEndBefore(resultRange->startContainer(), ec); 2657 if (searchRange->startContainer() == searchRange->endContainer()) 2658 return Range::create(m_frame->document()); 2659 resultRange = findPlainText(searchRange.get(), target, options | Backwards); 2660 } 2661 2662 return resultRange; 2663} 2664 2665PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, FindOptions options) 2666{ 2667 if (m_frame->excludeFromTextSearch()) 2668 return Range::create(m_frame->document()); 2669 2670 RefPtr<Range> resultRange = currentRange; 2671 RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); 2672 ExceptionCode ec = 0; 2673 bool forward = !(options & Backwards); 2674 for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, options)) { 2675 if (resultRange->collapsed(ec)) { 2676 if (!resultRange->startContainer()->isInShadowTree()) 2677 break; 2678 searchRange = rangeOfContents(m_frame->document()); 2679 if (forward) 2680 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec); 2681 else 2682 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec); 2683 continue; 2684 } 2685 2686 if (forward) 2687 searchRange->setStartAfter(resultRange->endContainer(), ec); 2688 else 2689 searchRange->setEndBefore(resultRange->startContainer(), ec); 2690 2691 Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); 2692 if (searchRange->collapsed(ec) && shadowTreeRoot) { 2693 if (forward) 2694 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); 2695 else 2696 searchRange->setStartBefore(shadowTreeRoot, ec); 2697 } 2698 2699 if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode()) 2700 break; 2701 } 2702 2703 if (insideVisibleArea(resultRange.get())) 2704 return resultRange; 2705 2706 if (!(options & WrapAround)) 2707 return Range::create(m_frame->document()); 2708 2709 if (options & Backwards) 2710 return lastVisibleRange(target, options); 2711 2712 return firstVisibleRange(target, options); 2713} 2714 2715void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle) 2716{ 2717 // If the new selection is orphaned, then don't update the selection. 2718 if (newSelection.start().isOrphan() || newSelection.end().isOrphan()) 2719 return; 2720 2721 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection, 2722 // because there is work that it must do in this situation. 2723 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls. 2724 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid 2725 bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection(); 2726 if (selectionDidNotChangeDOMPosition || m_frame->selection()->shouldChangeSelection(newSelection)) { 2727 SelectionController::SetSelectionOptions options = 0; 2728 if (closeTyping) 2729 options |= SelectionController::CloseTyping; 2730 if (clearTypingStyle) 2731 options |= SelectionController::ClearTypingStyle; 2732 m_frame->selection()->setSelection(newSelection, options); 2733 } 2734 2735 // Some editing operations change the selection visually without affecting its position within the DOM. 2736 // For example when you press return in the following (the caret is marked by ^): 2737 // <div contentEditable="true"><div>^Hello</div></div> 2738 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't 2739 // change the caret's DOM position (["hello", 0]). In these situations the above SelectionController::setSelection call 2740 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and 2741 // starts a new kill ring sequence, but we want to do these things (matches AppKit). 2742 if (selectionDidNotChangeDOMPosition) 2743 client()->respondToChangedSelection(); 2744} 2745 2746String Editor::selectedText() const 2747{ 2748 // We remove '\0' characters because they are not visibly rendered to the user. 2749 return plainText(m_frame->selection()->toNormalizedRange().get()).replace(0, ""); 2750} 2751 2752IntRect Editor::firstRectForRange(Range* range) const 2753{ 2754 int extraWidthToEndOfLine = 0; 2755 ASSERT(range->startContainer()); 2756 ASSERT(range->endContainer()); 2757 2758 InlineBox* startInlineBox; 2759 int startCaretOffset; 2760 Position startPosition = VisiblePosition(range->startPosition()).deepEquivalent(); 2761 if (startPosition.isNull()) 2762 return IntRect(); 2763 startPosition.getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset); 2764 2765 RenderObject* startRenderer = startPosition.deprecatedNode()->renderer(); 2766 ASSERT(startRenderer); 2767 IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine); 2768 if (startCaretRect != IntRect()) 2769 startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox(); 2770 2771 InlineBox* endInlineBox; 2772 int endCaretOffset; 2773 Position endPosition = VisiblePosition(range->endPosition()).deepEquivalent(); 2774 if (endPosition.isNull()) 2775 return IntRect(); 2776 endPosition.getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset); 2777 2778 RenderObject* endRenderer = endPosition.deprecatedNode()->renderer(); 2779 ASSERT(endRenderer); 2780 IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset); 2781 if (endCaretRect != IntRect()) 2782 endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox(); 2783 2784 if (startCaretRect.y() == endCaretRect.y()) { 2785 // start and end are on the same line 2786 return IntRect(min(startCaretRect.x(), endCaretRect.x()), 2787 startCaretRect.y(), 2788 abs(endCaretRect.x() - startCaretRect.x()), 2789 max(startCaretRect.height(), endCaretRect.height())); 2790 } 2791 2792 // start and end aren't on the same line, so go from start to the end of its line 2793 return IntRect(startCaretRect.x(), 2794 startCaretRect.y(), 2795 startCaretRect.width() + extraWidthToEndOfLine, 2796 startCaretRect.height()); 2797} 2798 2799bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const 2800{ 2801 return client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting); 2802} 2803 2804void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction editingAction) 2805{ 2806 if (!style || !style->length()) { 2807 m_frame->selection()->clearTypingStyle(); 2808 return; 2809 } 2810 2811 // Calculate the current typing style. 2812 RefPtr<EditingStyle> typingStyle; 2813 if (m_frame->selection()->typingStyle()) { 2814 typingStyle = m_frame->selection()->typingStyle()->copy(); 2815 typingStyle->overrideWithStyle(style->makeMutable().get()); 2816 } else 2817 typingStyle = EditingStyle::create(style); 2818 2819 typingStyle->prepareToApplyAt(m_frame->selection()->selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection); 2820 2821 // Handle block styles, substracting these from the typing style. 2822 RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties(); 2823 if (!blockStyle->isEmpty()) 2824 applyCommand(ApplyStyleCommand::create(m_frame->document(), blockStyle.get(), editingAction)); 2825 2826 // Set the remaining style as the typing style. 2827 m_frame->selection()->setTypingStyle(typingStyle); 2828} 2829 2830PassRefPtr<EditingStyle> Editor::selectionStartStyle() const 2831{ 2832 if (m_frame->selection()->isNone()) 2833 return 0; 2834 2835 RefPtr<Range> range(m_frame->selection()->toNormalizedRange()); 2836 Position position = range->editingStartPosition(); 2837 2838 // If the pos is at the end of a text node, then this node is not fully selected. 2839 // Move it to the next deep equivalent position to avoid removing the style from this node. 2840 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead. 2841 // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold. 2842 Node* positionNode = position.containerNode(); 2843 if (m_frame->selection()->isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset()) 2844 position = nextVisuallyDistinctCandidate(position); 2845 2846 Element* element = position.element(); 2847 if (!element) 2848 return 0; 2849 2850 RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties); 2851 style->mergeTypingStyle(m_frame->document()); 2852 return style; 2853} 2854 2855void Editor::textFieldDidBeginEditing(Element* e) 2856{ 2857 if (client()) 2858 client()->textFieldDidBeginEditing(e); 2859} 2860 2861void Editor::textFieldDidEndEditing(Element* e) 2862{ 2863 if (client()) 2864 client()->textFieldDidEndEditing(e); 2865} 2866 2867void Editor::textDidChangeInTextField(Element* e) 2868{ 2869 if (client()) 2870 client()->textDidChangeInTextField(e); 2871} 2872 2873bool Editor::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke) 2874{ 2875 if (client()) 2876 return client()->doTextFieldCommandFromEvent(e, ke); 2877 2878 return false; 2879} 2880 2881void Editor::textWillBeDeletedInTextField(Element* input) 2882{ 2883 if (client()) 2884 client()->textWillBeDeletedInTextField(input); 2885} 2886 2887void Editor::textDidChangeInTextArea(Element* e) 2888{ 2889 if (client()) 2890 client()->textDidChangeInTextArea(e); 2891} 2892 2893void Editor::applyEditingStyleToBodyElement() const 2894{ 2895 RefPtr<NodeList> list = m_frame->document()->getElementsByTagName("body"); 2896 unsigned len = list->length(); 2897 for (unsigned i = 0; i < len; i++) 2898 applyEditingStyleToElement(static_cast<Element*>(list->item(i))); 2899} 2900 2901void Editor::applyEditingStyleToElement(Element* element) const 2902{ 2903 if (!element) 2904 return; 2905 2906 CSSStyleDeclaration* style = element->style(); 2907 ASSERT(style); 2908 2909 ExceptionCode ec = 0; 2910 style->setProperty(CSSPropertyWordWrap, "break-word", false, ec); 2911 ASSERT(!ec); 2912 style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec); 2913 ASSERT(!ec); 2914 style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec); 2915 ASSERT(!ec); 2916} 2917 2918RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const 2919{ 2920 nodeToRemove = 0; 2921 2922 if (m_frame->selection()->isNone()) 2923 return 0; 2924 2925 Position position = m_frame->selection()->selection().visibleStart().deepEquivalent(); 2926 if (!position.isCandidate()) 2927 return 0; 2928 if (!position.deprecatedNode()) 2929 return 0; 2930 2931 RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle(); 2932 if (!typingStyle || !typingStyle->style()) 2933 return position.deprecatedNode()->renderer()->style(); 2934 2935 RefPtr<Element> styleElement = m_frame->document()->createElement(spanTag, false); 2936 2937 ExceptionCode ec = 0; 2938 String styleText = typingStyle->style()->cssText() + " display: inline"; 2939 styleElement->setAttribute(styleAttr, styleText.impl(), ec); 2940 ASSERT(!ec); 2941 2942 styleElement->appendChild(m_frame->document()->createEditingTextNode(""), ec); 2943 ASSERT(!ec); 2944 2945 position.deprecatedNode()->parentNode()->appendChild(styleElement, ec); 2946 ASSERT(!ec); 2947 2948 nodeToRemove = styleElement.get(); 2949 return styleElement->renderer() ? styleElement->renderer()->style() : 0; 2950} 2951 2952// Searches from the beginning of the document if nothing is selected. 2953bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection) 2954{ 2955 FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0); 2956 return findString(target, options); 2957} 2958 2959bool Editor::findString(const String& target, FindOptions options) 2960{ 2961 if (target.isEmpty()) 2962 return false; 2963 2964 if (m_frame->excludeFromTextSearch()) 2965 return false; 2966 2967 // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge 2968 // is used depends on whether we're searching forward or backward, and whether startInSelection is set. 2969 RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); 2970 VisibleSelection selection = m_frame->selection()->selection(); 2971 2972 bool forward = !(options & Backwards); 2973 bool startInSelection = options & StartInSelection; 2974 if (forward) 2975 setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd()); 2976 else 2977 setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart()); 2978 2979 RefPtr<Node> shadowTreeRoot = selection.shadowTreeRootNode(); 2980 if (shadowTreeRoot) { 2981 ExceptionCode ec = 0; 2982 if (forward) 2983 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec); 2984 else 2985 searchRange->setStart(shadowTreeRoot.get(), 0, ec); 2986 } 2987 2988 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options)); 2989 // If we started in the selection and the found range exactly matches the existing selection, find again. 2990 // Build a selection with the found range to remove collapsed whitespace. 2991 // Compare ranges instead of selection objects to ignore the way that the current selection was made. 2992 if (startInSelection && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), selection.toNormalizedRange().get())) { 2993 searchRange = rangeOfContents(m_frame->document()); 2994 if (forward) 2995 setStart(searchRange.get(), selection.visibleEnd()); 2996 else 2997 setEnd(searchRange.get(), selection.visibleStart()); 2998 2999 if (shadowTreeRoot) { 3000 ExceptionCode ec = 0; 3001 if (forward) 3002 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec); 3003 else 3004 searchRange->setStart(shadowTreeRoot.get(), 0, ec); 3005 } 3006 3007 resultRange = findPlainText(searchRange.get(), target, options); 3008 } 3009 3010 ExceptionCode exception = 0; 3011 3012 // If nothing was found in the shadow tree, search in main content following the shadow tree. 3013 if (resultRange->collapsed(exception) && shadowTreeRoot) { 3014 searchRange = rangeOfContents(m_frame->document()); 3015 if (forward) 3016 searchRange->setStartAfter(shadowTreeRoot->shadowHost(), exception); 3017 else 3018 searchRange->setEndBefore(shadowTreeRoot->shadowHost(), exception); 3019 3020 resultRange = findPlainText(searchRange.get(), target, options); 3021 } 3022 3023 if (!insideVisibleArea(resultRange.get())) { 3024 resultRange = nextVisibleRange(resultRange.get(), target, options); 3025 if (!resultRange) 3026 return false; 3027 } 3028 3029 // If we didn't find anything and we're wrapping, search again in the entire document (this will 3030 // redundantly re-search the area already searched in some cases). 3031 if (resultRange->collapsed(exception) && options & WrapAround) { 3032 searchRange = rangeOfContents(m_frame->document()); 3033 resultRange = findPlainText(searchRange.get(), target, options); 3034 // We used to return false here if we ended up with the same range that we started with 3035 // (e.g., the selection was already the only instance of this text). But we decided that 3036 // this should be a success case instead, so we'll just fall through in that case. 3037 } 3038 3039 if (resultRange->collapsed(exception)) 3040 return false; 3041 3042 m_frame->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM)); 3043 m_frame->selection()->revealSelection(); 3044 return true; 3045} 3046 3047static bool isFrameInRange(Frame* frame, Range* range) 3048{ 3049 bool inRange = false; 3050 for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document()->ownerElement()) { 3051 if (ownerElement->document() == range->ownerDocument()) { 3052 ExceptionCode ec = 0; 3053 inRange = range->intersectsNode(ownerElement, ec); 3054 break; 3055 } 3056 } 3057 return inRange; 3058} 3059 3060unsigned Editor::countMatchesForText(const String& target, FindOptions options, unsigned limit, bool markMatches) 3061{ 3062 return countMatchesForText(target, 0, options, limit, markMatches); 3063} 3064 3065unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches) 3066{ 3067 if (target.isEmpty()) 3068 return 0; 3069 3070 RefPtr<Range> searchRange; 3071 if (range) { 3072 if (range->ownerDocument() == m_frame->document()) 3073 searchRange = range; 3074 else if (!isFrameInRange(m_frame, range)) 3075 return 0; 3076 } 3077 if (!searchRange) 3078 searchRange = rangeOfContents(m_frame->document()); 3079 3080 Node* originalEndContainer = searchRange->endContainer(); 3081 int originalEndOffset = searchRange->endOffset(); 3082 3083 ExceptionCode exception = 0; 3084 unsigned matchCount = 0; 3085 do { 3086 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options & ~Backwards)); 3087 if (resultRange->collapsed(exception)) { 3088 if (!resultRange->startContainer()->isInShadowTree()) 3089 break; 3090 3091 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception); 3092 searchRange->setEnd(originalEndContainer, originalEndOffset, exception); 3093 continue; 3094 } 3095 3096 // Only treat the result as a match if it is visible 3097 if (insideVisibleArea(resultRange.get())) { 3098 ++matchCount; 3099 if (markMatches) 3100 m_frame->document()->markers()->addMarker(resultRange.get(), DocumentMarker::TextMatch); 3101 } 3102 3103 // Stop looking if we hit the specified limit. A limit of 0 means no limit. 3104 if (limit > 0 && matchCount >= limit) 3105 break; 3106 3107 // Set the new start for the search range to be the end of the previous 3108 // result range. There is no need to use a VisiblePosition here, 3109 // since findPlainText will use a TextIterator to go over the visible 3110 // text nodes. 3111 searchRange->setStart(resultRange->endContainer(exception), resultRange->endOffset(exception), exception); 3112 3113 Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); 3114 if (searchRange->collapsed(exception) && shadowTreeRoot) 3115 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception); 3116 } while (true); 3117 3118 if (markMatches) { 3119 // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match. 3120 if (m_frame->view() && m_frame->contentRenderer()) { 3121 m_frame->document()->updateLayout(); // Ensure layout is up to date. 3122 IntRect visibleRect = m_frame->view()->visibleContentRect(); 3123 if (!visibleRect.isEmpty()) { 3124 GraphicsContext context((PlatformGraphicsContext*)0); 3125 context.setPaintingDisabled(true); 3126 3127 PaintBehavior oldBehavior = m_frame->view()->paintBehavior(); 3128 m_frame->view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); 3129 m_frame->view()->paintContents(&context, visibleRect); 3130 m_frame->view()->setPaintBehavior(oldBehavior); 3131 } 3132 } 3133 } 3134 3135 return matchCount; 3136} 3137 3138void Editor::setMarkedTextMatchesAreHighlighted(bool flag) 3139{ 3140 if (flag == m_areMarkedTextMatchesHighlighted) 3141 return; 3142 3143 m_areMarkedTextMatchesHighlighted = flag; 3144 m_frame->document()->markers()->repaintMarkers(DocumentMarker::TextMatch); 3145} 3146 3147void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions options) 3148{ 3149 m_spellingCorrector->stopPendingCorrection(oldSelection); 3150 3151 bool closeTyping = options & SelectionController::CloseTyping; 3152 bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled(); 3153 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled(); 3154 if (isContinuousSpellCheckingEnabled) { 3155 VisibleSelection newAdjacentWords; 3156 VisibleSelection newSelectedSentence; 3157 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); 3158 if (m_frame->selection()->selection().isContentEditable() || caretBrowsing) { 3159 VisiblePosition newStart(m_frame->selection()->selection().visibleStart()); 3160 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary)); 3161 if (isContinuousGrammarCheckingEnabled) 3162 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart)); 3163 } 3164 3165 // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself. 3166 bool shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered); 3167 3168 // When typing we check spelling elsewhere, so don't redo it here. 3169 // If this is a change in selection resulting from a delete operation, 3170 // oldSelection may no longer be in the document. 3171 if (shouldCheckSpellingAndGrammar && closeTyping && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) { 3172 VisiblePosition oldStart(oldSelection.visibleStart()); 3173 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary)); 3174 if (oldAdjacentWords != newAdjacentWords) { 3175 if (isContinuousGrammarCheckingEnabled) { 3176 VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart)); 3177 markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence); 3178 } else 3179 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords); 3180 } 3181 } 3182 3183#if !PLATFORM(MAC) || (PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD))) 3184 // This only erases markers that are in the first unit (word or sentence) of the selection. 3185 // Perhaps peculiar, but it matches AppKit on these Mac OSX versions. 3186 if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange()) 3187 m_frame->document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling); 3188#endif 3189 if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange()) 3190 m_frame->document()->markers()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar); 3191 } 3192 3193 // When continuous spell checking is off, existing markers disappear after the selection changes. 3194 if (!isContinuousSpellCheckingEnabled) 3195 m_frame->document()->markers()->removeMarkers(DocumentMarker::Spelling); 3196 if (!isContinuousGrammarCheckingEnabled) 3197 m_frame->document()->markers()->removeMarkers(DocumentMarker::Grammar); 3198 3199 respondToChangedSelection(oldSelection); 3200} 3201 3202static Node* findFirstMarkable(Node* node) 3203{ 3204 while (node) { 3205 if (!node->renderer()) 3206 return 0; 3207 if (node->renderer()->isText()) 3208 return node; 3209 if (node->renderer()->isTextControl()) 3210 node = toRenderTextControl(node->renderer())->visiblePositionForIndex(1).deepEquivalent().deprecatedNode(); 3211 else if (node->firstChild()) 3212 node = node->firstChild(); 3213 else 3214 node = node->nextSibling(); 3215 } 3216 3217 return 0; 3218} 3219 3220bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const 3221{ 3222 Node* node = findFirstMarkable(m_frame->selection()->start().deprecatedNode()); 3223 if (!node) 3224 return false; 3225 3226 unsigned int startOffset = static_cast<unsigned int>(from); 3227 unsigned int endOffset = static_cast<unsigned int>(from + length); 3228 Vector<DocumentMarker> markers = m_frame->document()->markers()->markersForNode(node); 3229 for (size_t i = 0; i < markers.size(); ++i) { 3230 DocumentMarker marker = markers[i]; 3231 if (marker.startOffset <= startOffset && endOffset <= marker.endOffset && marker.type == markerType) 3232 return true; 3233 } 3234 3235 return false; 3236} 3237 3238FloatRect Editor::windowRectForRange(const Range* range) const 3239{ 3240 FrameView* view = frame()->view(); 3241 if (!view) 3242 return FloatRect(); 3243 Vector<FloatQuad> textQuads; 3244 range->textQuads(textQuads); 3245 FloatRect boundingRect; 3246 size_t size = textQuads.size(); 3247 for (size_t i = 0; i < size; ++i) 3248 boundingRect.unite(textQuads[i].boundingBox()); 3249 return view->contentsToWindow(IntRect(boundingRect)); 3250} 3251 3252TextCheckingTypeMask Editor::textCheckingTypeMaskFor(TextCheckingOptions textCheckingOptions) 3253{ 3254 bool shouldMarkSpelling = textCheckingOptions & MarkSpelling; 3255 bool shouldMarkGrammar = textCheckingOptions & MarkGrammar; 3256 bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel; 3257 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection); 3258 3259 TextCheckingTypeMask checkingTypes = 0; 3260 if (shouldMarkSpelling) 3261 checkingTypes |= TextCheckingTypeSpelling; 3262 if (shouldMarkGrammar) 3263 checkingTypes |= TextCheckingTypeGrammar; 3264 if (shouldCheckForCorrection) 3265 checkingTypes |= TextCheckingTypeCorrection; 3266 3267#if USE(AUTOMATIC_TEXT_REPLACEMENT) 3268 bool shouldPerformReplacement = textCheckingOptions & PerformReplacement; 3269 if (shouldPerformReplacement) { 3270 if (isAutomaticLinkDetectionEnabled()) 3271 checkingTypes |= TextCheckingTypeLink; 3272 if (isAutomaticQuoteSubstitutionEnabled()) 3273 checkingTypes |= TextCheckingTypeQuote; 3274 if (isAutomaticDashSubstitutionEnabled()) 3275 checkingTypes |= TextCheckingTypeDash; 3276 if (isAutomaticTextReplacementEnabled()) 3277 checkingTypes |= TextCheckingTypeReplacement; 3278 if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled()) 3279 checkingTypes |= TextCheckingTypeCorrection; 3280 } 3281#endif 3282 3283 return checkingTypes; 3284} 3285 3286} // namespace WebCore 3287