1 /* 2 * Copyright (C) 2006, 2007, 2008, 2011 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 "core/editing/Editor.h" 29 30 #include "CSSPropertyNames.h" 31 #include "HTMLNames.h" 32 #include "SVGNames.h" 33 #include "XLinkNames.h" 34 #include "bindings/v8/ExceptionStatePlaceholder.h" 35 #include "core/accessibility/AXObjectCache.h" 36 #include "core/css/CSSComputedStyleDeclaration.h" 37 #include "core/css/StylePropertySet.h" 38 #include "core/dom/Clipboard.h" 39 #include "core/dom/DocumentFragment.h" 40 #include "core/dom/DocumentMarkerController.h" 41 #include "core/dom/NodeList.h" 42 #include "core/dom/NodeTraversal.h" 43 #include "core/dom/ParserContentPolicy.h" 44 #include "core/dom/Text.h" 45 #include "core/editing/ApplyStyleCommand.h" 46 #include "core/editing/DeleteSelectionCommand.h" 47 #include "core/editing/IndentOutdentCommand.h" 48 #include "core/editing/InputMethodController.h" 49 #include "core/editing/InsertListCommand.h" 50 #include "core/editing/ModifySelectionListLevel.h" 51 #include "core/editing/RemoveFormatCommand.h" 52 #include "core/editing/RenderedPosition.h" 53 #include "core/editing/ReplaceSelectionCommand.h" 54 #include "core/editing/SimplifyMarkupCommand.h" 55 #include "core/editing/SpellChecker.h" 56 #include "core/editing/TypingCommand.h" 57 #include "core/editing/UndoStack.h" 58 #include "core/editing/VisibleUnits.h" 59 #include "core/editing/htmlediting.h" 60 #include "core/editing/markup.h" 61 #include "core/events/ClipboardEvent.h" 62 #include "core/events/KeyboardEvent.h" 63 #include "core/events/ScopedEventQueue.h" 64 #include "core/events/TextEvent.h" 65 #include "core/events/ThreadLocalEventNames.h" 66 #include "core/fetch/ImageResource.h" 67 #include "core/fetch/ResourceFetcher.h" 68 #include "core/html/HTMLImageElement.h" 69 #include "core/html/HTMLInputElement.h" 70 #include "core/html/HTMLTextAreaElement.h" 71 #include "core/html/parser/HTMLParserIdioms.h" 72 #include "core/loader/EmptyClients.h" 73 #include "core/page/EditorClient.h" 74 #include "core/page/EventHandler.h" 75 #include "core/page/FocusController.h" 76 #include "core/frame/Frame.h" 77 #include "core/frame/FrameView.h" 78 #include "core/page/Page.h" 79 #include "core/frame/Settings.h" 80 #include "core/platform/Pasteboard.h" 81 #include "core/platform/chromium/ChromiumDataObject.h" 82 #include "core/rendering/HitTestResult.h" 83 #include "core/rendering/RenderImage.h" 84 #include "platform/KillRing.h" 85 #include "platform/weborigin/KURL.h" 86 #include "wtf/unicode/CharacterNames.h" 87 88 namespace WebCore { 89 90 using namespace std; 91 using namespace HTMLNames; 92 using namespace WTF; 93 using namespace Unicode; 94 95 Editor::RevealSelectionScope::RevealSelectionScope(Editor* editor) 96 : m_editor(editor) 97 { 98 ++m_editor->m_preventRevealSelection; 99 } 100 101 Editor::RevealSelectionScope::~RevealSelectionScope() 102 { 103 ASSERT(m_editor->m_preventRevealSelection); 104 --m_editor->m_preventRevealSelection; 105 if (!m_editor->m_preventRevealSelection) 106 m_editor->m_frame.selection().revealSelection(ScrollAlignment::alignToEdgeIfNeeded, RevealExtent); 107 } 108 109 // When an event handler has moved the selection outside of a text control 110 // we should use the target control's selection for this editing operation. 111 VisibleSelection Editor::selectionForCommand(Event* event) 112 { 113 VisibleSelection selection = m_frame.selection().selection(); 114 if (!event) 115 return selection; 116 // If the target is a text control, and the current selection is outside of its shadow tree, 117 // then use the saved selection for that text control. 118 HTMLTextFormControlElement* textFormControlOfSelectionStart = enclosingTextFormControl(selection.start()); 119 HTMLTextFormControlElement* textFromControlOfTarget = isHTMLTextFormControlElement(event->target()->toNode()) ? toHTMLTextFormControlElement(event->target()->toNode()) : 0; 120 if (textFromControlOfTarget && (selection.start().isNull() || textFromControlOfTarget != textFormControlOfSelectionStart)) { 121 if (RefPtr<Range> range = textFromControlOfTarget->selection()) 122 return VisibleSelection(range.get(), DOWNSTREAM, selection.isDirectional()); 123 } 124 return selection; 125 } 126 127 // Function considers Mac editing behavior a fallback when Page or Settings is not available. 128 EditingBehavior Editor::behavior() const 129 { 130 if (!m_frame.settings()) 131 return EditingBehavior(EditingMacBehavior); 132 133 return EditingBehavior(m_frame.settings()->editingBehaviorType()); 134 } 135 136 static EditorClient& emptyEditorClient() 137 { 138 DEFINE_STATIC_LOCAL(EmptyEditorClient, client, ()); 139 return client; 140 } 141 142 EditorClient& Editor::client() const 143 { 144 if (Page* page = m_frame.page()) 145 return page->editorClient(); 146 return emptyEditorClient(); 147 } 148 149 UndoStack* Editor::undoStack() const 150 { 151 if (Page* page = m_frame.page()) 152 return &page->undoStack(); 153 return 0; 154 } 155 156 bool Editor::handleTextEvent(TextEvent* event) 157 { 158 // Default event handling for Drag and Drop will be handled by DragController 159 // so we leave the event for it. 160 if (event->isDrop()) 161 return false; 162 163 if (event->isPaste()) { 164 if (event->pastingFragment()) 165 replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle()); 166 else 167 replaceSelectionWithText(event->data(), false, event->shouldSmartReplace()); 168 return true; 169 } 170 171 String data = event->data(); 172 if (data == "\n") { 173 if (event->isLineBreak()) 174 return insertLineBreak(); 175 return insertParagraphSeparator(); 176 } 177 178 return insertTextWithoutSendingTextEvent(data, false, event); 179 } 180 181 bool Editor::canEdit() const 182 { 183 return m_frame.selection().rootEditableElement(); 184 } 185 186 bool Editor::canEditRichly() const 187 { 188 return m_frame.selection().isContentRichlyEditable(); 189 } 190 191 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They 192 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items. 193 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not 194 // normally selectable to implement copy/paste (like divs, or a document body). 195 196 bool Editor::canDHTMLCut() 197 { 198 return !m_frame.selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecut, ClipboardNumb); 199 } 200 201 bool Editor::canDHTMLCopy() 202 { 203 return !m_frame.selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecopy, ClipboardNumb); 204 } 205 206 bool Editor::canDHTMLPaste() 207 { 208 return !dispatchCPPEvent(EventTypeNames::beforepaste, ClipboardNumb); 209 } 210 211 bool Editor::canCut() const 212 { 213 return canCopy() && canDelete(); 214 } 215 216 static HTMLImageElement* imageElementFromImageDocument(Document* document) 217 { 218 if (!document) 219 return 0; 220 if (!document->isImageDocument()) 221 return 0; 222 223 HTMLElement* body = document->body(); 224 if (!body) 225 return 0; 226 227 Node* node = body->firstChild(); 228 if (!node) 229 return 0; 230 if (!node->hasTagName(imgTag)) 231 return 0; 232 return toHTMLImageElement(node); 233 } 234 235 bool Editor::canCopy() const 236 { 237 if (imageElementFromImageDocument(m_frame.document())) 238 return true; 239 FrameSelection& selection = m_frame.selection(); 240 return selection.isRange() && !selection.isInPasswordField(); 241 } 242 243 bool Editor::canPaste() const 244 { 245 return canEdit(); 246 } 247 248 bool Editor::canDelete() const 249 { 250 FrameSelection& selection = m_frame.selection(); 251 return selection.isRange() && selection.rootEditableElement(); 252 } 253 254 bool Editor::canDeleteRange(Range* range) const 255 { 256 Node* startContainer = range->startContainer(); 257 Node* endContainer = range->endContainer(); 258 if (!startContainer || !endContainer) 259 return false; 260 261 if (!startContainer->rendererIsEditable() || !endContainer->rendererIsEditable()) 262 return false; 263 264 if (range->collapsed(IGNORE_EXCEPTION)) { 265 VisiblePosition start(range->startPosition(), DOWNSTREAM); 266 VisiblePosition previous = start.previous(); 267 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item. 268 if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement()) 269 return false; 270 } 271 return true; 272 } 273 274 bool Editor::smartInsertDeleteEnabled() const 275 { 276 if (Settings* settings = m_frame.settings()) 277 return settings->smartInsertDeleteEnabled(); 278 return false; 279 } 280 281 bool Editor::canSmartCopyOrDelete() const 282 { 283 return smartInsertDeleteEnabled() && m_frame.selection().granularity() == WordGranularity; 284 } 285 286 bool Editor::isSelectTrailingWhitespaceEnabled() const 287 { 288 if (Settings* settings = m_frame.settings()) 289 return settings->selectTrailingWhitespaceEnabled(); 290 return false; 291 } 292 293 bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction) 294 { 295 if (!canEdit()) 296 return false; 297 298 if (m_frame.selection().isRange()) { 299 if (isTypingAction) { 300 ASSERT(m_frame.document()); 301 TypingCommand::deleteKeyPressed(*m_frame.document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity); 302 revealSelectionAfterEditingOperation(); 303 } else { 304 if (killRing) 305 addToKillRing(selectedRange().get(), false); 306 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 307 // Implicitly calls revealSelectionAfterEditingOperation(). 308 } 309 } else { 310 TypingCommand::Options options = 0; 311 if (canSmartCopyOrDelete()) 312 options |= TypingCommand::SmartDelete; 313 if (killRing) 314 options |= TypingCommand::KillRing; 315 switch (direction) { 316 case DirectionForward: 317 case DirectionRight: 318 ASSERT(m_frame.document()); 319 TypingCommand::forwardDeleteKeyPressed(*m_frame.document(), options, granularity); 320 break; 321 case DirectionBackward: 322 case DirectionLeft: 323 ASSERT(m_frame.document()); 324 TypingCommand::deleteKeyPressed(*m_frame.document(), options, granularity); 325 break; 326 } 327 revealSelectionAfterEditingOperation(); 328 } 329 330 // FIXME: We should to move this down into deleteKeyPressed. 331 // clear the "start new kill ring sequence" setting, because it was set to true 332 // when the selection was updated by deleting the range 333 if (killRing) 334 setStartNewKillRingSequence(false); 335 336 return true; 337 } 338 339 void Editor::deleteSelectionWithSmartDelete(bool smartDelete) 340 { 341 if (m_frame.selection().isNone()) 342 return; 343 344 ASSERT(m_frame.document()); 345 DeleteSelectionCommand::create(*m_frame.document(), smartDelete)->apply(); 346 } 347 348 void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace) 349 { 350 Node* target = findEventTargetFromSelection(); 351 if (!target) 352 return; 353 target->dispatchEvent(TextEvent::createForPlainTextPaste(m_frame.domWindow(), pastingText, smartReplace), IGNORE_EXCEPTION); 354 } 355 356 void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle) 357 { 358 Node* target = findEventTargetFromSelection(); 359 if (!target) 360 return; 361 target->dispatchEvent(TextEvent::createForFragmentPaste(m_frame.domWindow(), pastingFragment, smartReplace, matchStyle), IGNORE_EXCEPTION); 362 } 363 364 bool Editor::tryDHTMLCopy() 365 { 366 if (m_frame.selection().isInPasswordField()) 367 return false; 368 369 return !dispatchCPPEvent(EventTypeNames::copy, ClipboardWritable); 370 } 371 372 bool Editor::tryDHTMLCut() 373 { 374 if (m_frame.selection().isInPasswordField()) 375 return false; 376 377 return !dispatchCPPEvent(EventTypeNames::cut, ClipboardWritable); 378 } 379 380 bool Editor::tryDHTMLPaste(PasteMode pasteMode) 381 { 382 return !dispatchCPPEvent(EventTypeNames::paste, ClipboardReadable, pasteMode); 383 } 384 385 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard) 386 { 387 String text = pasteboard->plainText(); 388 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard)); 389 } 390 391 void Editor::pasteWithPasteboard(Pasteboard* pasteboard) 392 { 393 RefPtr<Range> range = selectedRange(); 394 RefPtr<DocumentFragment> fragment; 395 bool chosePlainText = false; 396 397 if (pasteboard->isHTMLAvailable()) { 398 unsigned fragmentStart = 0; 399 unsigned fragmentEnd = 0; 400 KURL url; 401 String markup = pasteboard->readHTML(url, fragmentStart, fragmentEnd); 402 if (!markup.isEmpty()) { 403 ASSERT(m_frame.document()); 404 fragment = createFragmentFromMarkupWithContext(*m_frame.document(), markup, fragmentStart, fragmentEnd, url, DisallowScriptingAndPluginContent); 405 } 406 } 407 408 if (!fragment) { 409 String text = pasteboard->plainText(); 410 if (!text.isEmpty()) { 411 chosePlainText = true; 412 fragment = createFragmentFromText(range.get(), text); 413 } 414 } 415 416 if (fragment) 417 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), chosePlainText); 418 } 419 420 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard, Range* selectedRange, const String& plainText) 421 { 422 String html = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs); 423 KURL url = selectedRange->startContainer()->document().url(); 424 pasteboard->writeHTML(html, url, plainText, canSmartCopyOrDelete()); 425 } 426 427 static void writeImageNodeToPasteboard(Pasteboard* pasteboard, Node* node, const String& title) 428 { 429 ASSERT(pasteboard); 430 ASSERT(node); 431 432 if (!(node->renderer() && node->renderer()->isImage())) 433 return; 434 435 RenderImage* renderer = toRenderImage(node->renderer()); 436 ImageResource* cachedImage = renderer->cachedImage(); 437 if (!cachedImage || cachedImage->errorOccurred()) 438 return; 439 Image* image = cachedImage->imageForRenderer(renderer); 440 ASSERT(image); 441 442 // FIXME: This should probably be reconciled with HitTestResult::absoluteImageURL. 443 AtomicString urlString; 444 if (node->hasTagName(imgTag) || node->hasTagName(inputTag)) 445 urlString = toElement(node)->getAttribute(srcAttr); 446 else if (node->hasTagName(SVGNames::imageTag)) 447 urlString = toElement(node)->getAttribute(XLinkNames::hrefAttr); 448 else if (node->hasTagName(embedTag) || node->hasTagName(objectTag)) 449 urlString = toElement(node)->imageSourceURL(); 450 KURL url = urlString.isEmpty() ? KURL() : node->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); 451 452 pasteboard->writeImage(image, url, title); 453 } 454 455 // Returns whether caller should continue with "the default processing", which is the same as 456 // the event handler NOT setting the return value to false 457 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy, PasteMode pasteMode) 458 { 459 Node* target = findEventTargetFromSelection(); 460 if (!target) 461 return true; 462 463 RefPtr<Clipboard> clipboard = Clipboard::create( 464 Clipboard::CopyAndPaste, 465 policy, 466 policy == ClipboardWritable 467 ? ChromiumDataObject::create() 468 : ChromiumDataObject::createFromPasteboard(pasteMode)); 469 470 RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard); 471 target->dispatchEvent(evt, IGNORE_EXCEPTION); 472 bool noDefaultProcessing = evt->defaultPrevented(); 473 if (noDefaultProcessing && policy == ClipboardWritable) { 474 RefPtr<ChromiumDataObject> dataObject = clipboard->dataObject(); 475 Pasteboard::generalPasteboard()->writeDataObject(dataObject.release()); 476 } 477 478 // invalidate clipboard here for security 479 clipboard->setAccessPolicy(ClipboardNumb); 480 481 return !noDefaultProcessing; 482 } 483 484 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard) 485 { 486 return smartInsertDeleteEnabled() && pasteboard->canSmartReplace(); 487 } 488 489 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle) 490 { 491 if (m_frame.selection().isNone() || !m_frame.selection().isContentEditable() || !fragment) 492 return; 493 494 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting | ReplaceSelectionCommand::SanitizeFragment; 495 if (selectReplacement) 496 options |= ReplaceSelectionCommand::SelectReplacement; 497 if (smartReplace) 498 options |= ReplaceSelectionCommand::SmartReplace; 499 if (matchStyle) 500 options |= ReplaceSelectionCommand::MatchStyle; 501 ASSERT(m_frame.document()); 502 ReplaceSelectionCommand::create(*m_frame.document(), fragment, options, EditActionPaste)->apply(); 503 revealSelectionAfterEditingOperation(); 504 505 if (m_frame.selection().isInPasswordField() || !spellChecker().isContinuousSpellCheckingEnabled()) 506 return; 507 spellChecker().chunkAndMarkAllMisspellingsAndBadGrammar(m_frame.selection().rootEditableElement()); 508 } 509 510 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) 511 { 512 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true); 513 } 514 515 PassRefPtr<Range> Editor::selectedRange() 516 { 517 return m_frame.selection().toNormalizedRange(); 518 } 519 520 bool Editor::shouldDeleteRange(Range* range) const 521 { 522 if (!range || range->collapsed(IGNORE_EXCEPTION)) 523 return false; 524 525 return canDeleteRange(range); 526 } 527 528 void Editor::notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) 529 { 530 client().respondToChangedSelection(m_frame.selection().selectionType()); 531 setStartNewKillRingSequence(true); 532 } 533 534 void Editor::respondToChangedContents(const VisibleSelection& endingSelection) 535 { 536 if (AXObjectCache::accessibilityEnabled()) { 537 Node* node = endingSelection.start().deprecatedNode(); 538 if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) 539 cache->postNotification(node, AXObjectCache::AXValueChanged, false); 540 } 541 542 spellChecker().updateMarkersForWordsAffectedByEditing(true); 543 client().respondToChangedContents(); 544 } 545 546 TriState Editor::selectionUnorderedListState() const 547 { 548 if (m_frame.selection().isCaret()) { 549 if (enclosingNodeWithTag(m_frame.selection().selection().start(), ulTag)) 550 return TrueTriState; 551 } else if (m_frame.selection().isRange()) { 552 Node* startNode = enclosingNodeWithTag(m_frame.selection().selection().start(), ulTag); 553 Node* endNode = enclosingNodeWithTag(m_frame.selection().selection().end(), ulTag); 554 if (startNode && endNode && startNode == endNode) 555 return TrueTriState; 556 } 557 558 return FalseTriState; 559 } 560 561 TriState Editor::selectionOrderedListState() const 562 { 563 if (m_frame.selection().isCaret()) { 564 if (enclosingNodeWithTag(m_frame.selection().selection().start(), olTag)) 565 return TrueTriState; 566 } else if (m_frame.selection().isRange()) { 567 Node* startNode = enclosingNodeWithTag(m_frame.selection().selection().start(), olTag); 568 Node* endNode = enclosingNodeWithTag(m_frame.selection().selection().end(), olTag); 569 if (startNode && endNode && startNode == endNode) 570 return TrueTriState; 571 } 572 573 return FalseTriState; 574 } 575 576 PassRefPtr<Node> Editor::insertOrderedList() 577 { 578 if (!canEditRichly()) 579 return 0; 580 581 ASSERT(m_frame.document()); 582 RefPtr<Node> newList = InsertListCommand::insertList(*m_frame.document(), InsertListCommand::OrderedList); 583 revealSelectionAfterEditingOperation(); 584 return newList; 585 } 586 587 PassRefPtr<Node> Editor::insertUnorderedList() 588 { 589 if (!canEditRichly()) 590 return 0; 591 592 ASSERT(m_frame.document()); 593 RefPtr<Node> newList = InsertListCommand::insertList(*m_frame.document(), InsertListCommand::UnorderedList); 594 revealSelectionAfterEditingOperation(); 595 return newList; 596 } 597 598 bool Editor::canIncreaseSelectionListLevel() 599 { 600 ASSERT(m_frame.document()); 601 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(*m_frame.document()); 602 } 603 604 bool Editor::canDecreaseSelectionListLevel() 605 { 606 ASSERT(m_frame.document()); 607 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(*m_frame.document()); 608 } 609 610 PassRefPtr<Node> Editor::increaseSelectionListLevel() 611 { 612 if (!canEditRichly() || m_frame.selection().isNone()) 613 return 0; 614 615 ASSERT(m_frame.document()); 616 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(*m_frame.document()); 617 revealSelectionAfterEditingOperation(); 618 return newList; 619 } 620 621 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered() 622 { 623 if (!canEditRichly() || m_frame.selection().isNone()) 624 return 0; 625 626 ASSERT(m_frame.document()); 627 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(*m_frame.document()); 628 revealSelectionAfterEditingOperation(); 629 return newList.release(); 630 } 631 632 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered() 633 { 634 if (!canEditRichly() || m_frame.selection().isNone()) 635 return 0; 636 637 ASSERT(m_frame.document()); 638 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(*m_frame.document()); 639 revealSelectionAfterEditingOperation(); 640 return newList.release(); 641 } 642 643 void Editor::decreaseSelectionListLevel() 644 { 645 if (!canEditRichly() || m_frame.selection().isNone()) 646 return; 647 648 ASSERT(m_frame.document()); 649 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(*m_frame.document()); 650 revealSelectionAfterEditingOperation(); 651 } 652 653 void Editor::removeFormattingAndStyle() 654 { 655 ASSERT(m_frame.document()); 656 RemoveFormatCommand::create(*m_frame.document())->apply(); 657 } 658 659 void Editor::clearLastEditCommand() 660 { 661 m_lastEditCommand.clear(); 662 } 663 664 Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const 665 { 666 Node* target = selection.start().element(); 667 if (!target) 668 target = m_frame.document()->body(); 669 670 return target; 671 } 672 673 Node* Editor::findEventTargetFromSelection() const 674 { 675 return findEventTargetFrom(m_frame.selection().selection()); 676 } 677 678 void Editor::applyStyle(StylePropertySet* style, EditAction editingAction) 679 { 680 switch (m_frame.selection().selectionType()) { 681 case NoSelection: 682 // do nothing 683 break; 684 case CaretSelection: 685 computeAndSetTypingStyle(style, editingAction); 686 break; 687 case RangeSelection: 688 if (style) { 689 ASSERT(m_frame.document()); 690 ApplyStyleCommand::create(*m_frame.document(), EditingStyle::create(style).get(), editingAction)->apply(); 691 } 692 break; 693 } 694 } 695 696 void Editor::applyParagraphStyle(StylePropertySet* style, EditAction editingAction) 697 { 698 if (m_frame.selection().isNone() || !style) 699 return; 700 ASSERT(m_frame.document()); 701 ApplyStyleCommand::create(*m_frame.document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties)->apply(); 702 } 703 704 void Editor::applyStyleToSelection(StylePropertySet* style, EditAction editingAction) 705 { 706 if (!style || style->isEmpty() || !canEditRichly()) 707 return; 708 709 applyStyle(style, editingAction); 710 } 711 712 void Editor::applyParagraphStyleToSelection(StylePropertySet* style, EditAction editingAction) 713 { 714 if (!style || style->isEmpty() || !canEditRichly()) 715 return; 716 717 applyParagraphStyle(style, editingAction); 718 } 719 720 bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const 721 { 722 return EditingStyle::create(propertyID, value)->triStateOfStyle( 723 EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor).get()); 724 } 725 726 TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const 727 { 728 return EditingStyle::create(propertyID, value)->triStateOfStyle(m_frame.selection().selection()); 729 } 730 731 String Editor::selectionStartCSSPropertyValue(CSSPropertyID propertyID) 732 { 733 RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), 734 propertyID == CSSPropertyBackgroundColor); 735 if (!selectionStyle || !selectionStyle->style()) 736 return String(); 737 738 if (propertyID == CSSPropertyFontSize) 739 return String::number(selectionStyle->legacyFontSize(m_frame.document())); 740 return selectionStyle->style()->getPropertyValue(propertyID); 741 } 742 743 void Editor::indent() 744 { 745 ASSERT(m_frame.document()); 746 IndentOutdentCommand::create(*m_frame.document(), IndentOutdentCommand::Indent)->apply(); 747 } 748 749 void Editor::outdent() 750 { 751 ASSERT(m_frame.document()); 752 IndentOutdentCommand::create(*m_frame.document(), IndentOutdentCommand::Outdent)->apply(); 753 } 754 755 static void dispatchEditableContentChangedEvents(PassRefPtr<Element> startRoot, PassRefPtr<Element> endRoot) 756 { 757 if (startRoot) 758 startRoot->dispatchEvent(Event::create(EventTypeNames::webkitEditableContentChanged), IGNORE_EXCEPTION); 759 if (endRoot && endRoot != startRoot) 760 endRoot->dispatchEvent(Event::create(EventTypeNames::webkitEditableContentChanged), IGNORE_EXCEPTION); 761 } 762 763 void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd) 764 { 765 EventQueueScope scope; 766 m_frame.document()->updateLayout(); 767 768 EditCommandComposition* composition = cmd->composition(); 769 ASSERT(composition); 770 dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement()); 771 VisibleSelection newSelection(cmd->endingSelection()); 772 773 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. 774 changeSelectionAfterCommand(newSelection, 0); 775 776 if (!cmd->preservesTypingStyle()) 777 m_frame.selection().clearTypingStyle(); 778 779 // Command will be equal to last edit command only in the case of typing 780 if (m_lastEditCommand.get() == cmd) { 781 ASSERT(cmd->isTypingCommand()); 782 } else { 783 // Only register a new undo command if the command passed in is 784 // different from the last command 785 m_lastEditCommand = cmd; 786 if (UndoStack* undoStack = this->undoStack()) 787 undoStack->registerUndoStep(m_lastEditCommand->ensureComposition()); 788 } 789 790 respondToChangedContents(newSelection); 791 } 792 793 void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd) 794 { 795 EventQueueScope scope; 796 m_frame.document()->updateLayout(); 797 798 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 799 800 VisibleSelection newSelection(cmd->startingSelection()); 801 changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle); 802 803 m_lastEditCommand = 0; 804 if (UndoStack* undoStack = this->undoStack()) 805 undoStack->registerRedoStep(cmd); 806 respondToChangedContents(newSelection); 807 } 808 809 void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd) 810 { 811 EventQueueScope scope; 812 m_frame.document()->updateLayout(); 813 814 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 815 816 VisibleSelection newSelection(cmd->endingSelection()); 817 changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle); 818 819 m_lastEditCommand = 0; 820 if (UndoStack* undoStack = this->undoStack()) 821 undoStack->registerUndoStep(cmd); 822 respondToChangedContents(newSelection); 823 } 824 825 PassOwnPtr<Editor> Editor::create(Frame& frame) 826 { 827 return adoptPtr(new Editor(frame)); 828 } 829 830 Editor::Editor(Frame& frame) 831 : m_frame(frame) 832 , m_preventRevealSelection(0) 833 , m_shouldStartNewKillRingSequence(false) 834 // This is off by default, since most editors want this behavior (this matches IE but not FF). 835 , m_shouldStyleWithCSS(false) 836 , m_killRing(adoptPtr(new KillRing)) 837 , m_areMarkedTextMatchesHighlighted(false) 838 , m_defaultParagraphSeparator(EditorParagraphSeparatorIsDiv) 839 , m_overwriteModeEnabled(false) 840 { 841 } 842 843 Editor::~Editor() 844 { 845 } 846 847 void Editor::clear() 848 { 849 m_frame.inputMethodController().clear(); 850 m_shouldStyleWithCSS = false; 851 m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv; 852 } 853 854 bool Editor::insertText(const String& text, Event* triggeringEvent) 855 { 856 return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent); 857 } 858 859 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent) 860 { 861 if (text.isEmpty()) 862 return false; 863 864 VisibleSelection selection = selectionForCommand(triggeringEvent); 865 if (!selection.isContentEditable()) 866 return false; 867 RefPtr<Range> range = selection.toNormalizedRange(); 868 869 spellChecker().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0])); 870 871 // Get the selection to use for the event that triggered this insertText. 872 // If the event handler changed the selection, we may want to use a different selection 873 // that is contained in the event target. 874 selection = selectionForCommand(triggeringEvent); 875 if (selection.isContentEditable()) { 876 if (Node* selectionStart = selection.start().deprecatedNode()) { 877 RefPtr<Document> document(selectionStart->document()); 878 879 // Insert the text 880 TypingCommand::Options options = 0; 881 if (selectInsertedText) 882 options |= TypingCommand::SelectInsertedText; 883 TypingCommand::insertText(*document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); 884 885 // Reveal the current selection 886 if (Frame* editedFrame = document->frame()) { 887 if (Page* page = editedFrame->page()) 888 page->focusController().focusedOrMainFrame()->selection().revealSelection(ScrollAlignment::alignCenterIfNeeded); 889 } 890 } 891 } 892 893 return true; 894 } 895 896 bool Editor::insertLineBreak() 897 { 898 if (!canEdit()) 899 return false; 900 901 VisiblePosition caret = m_frame.selection().selection().visibleStart(); 902 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); 903 ASSERT(m_frame.document()); 904 TypingCommand::insertLineBreak(*m_frame.document(), 0); 905 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); 906 907 return true; 908 } 909 910 bool Editor::insertParagraphSeparator() 911 { 912 if (!canEdit()) 913 return false; 914 915 if (!canEditRichly()) 916 return insertLineBreak(); 917 918 VisiblePosition caret = m_frame.selection().selection().visibleStart(); 919 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); 920 ASSERT(m_frame.document()); 921 TypingCommand::insertParagraphSeparator(*m_frame.document(), 0); 922 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); 923 924 return true; 925 } 926 927 void Editor::cut() 928 { 929 if (tryDHTMLCut()) 930 return; // DHTML did the whole operation 931 if (!canCut()) 932 return; 933 RefPtr<Range> selection = selectedRange(); 934 if (shouldDeleteRange(selection.get())) { 935 spellChecker().updateMarkersForWordsAffectedByEditing(true); 936 String plainText = m_frame.selectedTextForClipboard(); 937 if (enclosingTextFormControl(m_frame.selection().start())) { 938 Pasteboard::generalPasteboard()->writePlainText(plainText, 939 canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace); 940 } else { 941 writeSelectionToPasteboard(Pasteboard::generalPasteboard(), selection.get(), plainText); 942 } 943 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 944 } 945 } 946 947 void Editor::copy() 948 { 949 if (tryDHTMLCopy()) 950 return; // DHTML did the whole operation 951 if (!canCopy()) 952 return; 953 if (enclosingTextFormControl(m_frame.selection().start())) { 954 Pasteboard::generalPasteboard()->writePlainText(m_frame.selectedTextForClipboard(), 955 canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace); 956 } else { 957 Document* document = m_frame.document(); 958 if (HTMLImageElement* imageElement = imageElementFromImageDocument(document)) 959 writeImageNodeToPasteboard(Pasteboard::generalPasteboard(), imageElement, document->title()); 960 else 961 writeSelectionToPasteboard(Pasteboard::generalPasteboard(), selectedRange().get(), m_frame.selectedTextForClipboard()); 962 } 963 } 964 965 void Editor::paste() 966 { 967 ASSERT(m_frame.document()); 968 if (tryDHTMLPaste(AllMimeTypes)) 969 return; // DHTML did the whole operation 970 if (!canPaste()) 971 return; 972 spellChecker().updateMarkersForWordsAffectedByEditing(false); 973 ResourceFetcher* loader = m_frame.document()->fetcher(); 974 ResourceCacheValidationSuppressor validationSuppressor(loader); 975 if (m_frame.selection().isContentRichlyEditable()) 976 pasteWithPasteboard(Pasteboard::generalPasteboard()); 977 else 978 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 979 } 980 981 void Editor::pasteAsPlainText() 982 { 983 if (tryDHTMLPaste(PlainTextOnly)) 984 return; 985 if (!canPaste()) 986 return; 987 spellChecker().updateMarkersForWordsAffectedByEditing(false); 988 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 989 } 990 991 void Editor::performDelete() 992 { 993 if (!canDelete()) 994 return; 995 addToKillRing(selectedRange().get(), false); 996 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 997 998 // clear the "start new kill ring sequence" setting, because it was set to true 999 // when the selection was updated by deleting the range 1000 setStartNewKillRingSequence(false); 1001 } 1002 1003 void Editor::copyImage(const HitTestResult& result) 1004 { 1005 writeImageNodeToPasteboard(Pasteboard::generalPasteboard(), result.innerNonSharedNode(), result.altDisplayString()); 1006 } 1007 1008 bool Editor::canUndo() 1009 { 1010 if (UndoStack* undoStack = this->undoStack()) 1011 return undoStack->canUndo(); 1012 return false; 1013 } 1014 1015 void Editor::undo() 1016 { 1017 if (UndoStack* undoStack = this->undoStack()) 1018 undoStack->undo(); 1019 } 1020 1021 bool Editor::canRedo() 1022 { 1023 if (UndoStack* undoStack = this->undoStack()) 1024 return undoStack->canRedo(); 1025 return false; 1026 } 1027 1028 void Editor::redo() 1029 { 1030 if (UndoStack* undoStack = this->undoStack()) 1031 undoStack->redo(); 1032 } 1033 1034 void Editor::setBaseWritingDirection(WritingDirection direction) 1035 { 1036 Node* focusedElement = frame().document()->focusedElement(); 1037 if (focusedElement && isHTMLTextFormControlElement(focusedElement)) { 1038 if (direction == NaturalWritingDirection) 1039 return; 1040 toHTMLElement(focusedElement)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl"); 1041 focusedElement->dispatchInputEvent(); 1042 frame().document()->updateStyleIfNeeded(); 1043 return; 1044 } 1045 1046 RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create(); 1047 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false); 1048 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection); 1049 } 1050 1051 void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) 1052 { 1053 if (m_preventRevealSelection) 1054 return; 1055 1056 m_frame.selection().revealSelection(alignment, revealExtentOption); 1057 } 1058 1059 void Editor::transpose() 1060 { 1061 if (!canEdit()) 1062 return; 1063 1064 VisibleSelection selection = m_frame.selection().selection(); 1065 if (!selection.isCaret()) 1066 return; 1067 1068 // Make a selection that goes back one character and forward two characters. 1069 VisiblePosition caret = selection.visibleStart(); 1070 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); 1071 VisiblePosition previous = next.previous(); 1072 if (next == previous) 1073 return; 1074 previous = previous.previous(); 1075 if (!inSameParagraph(next, previous)) 1076 return; 1077 RefPtr<Range> range = makeRange(previous, next); 1078 if (!range) 1079 return; 1080 VisibleSelection newSelection(range.get(), DOWNSTREAM); 1081 1082 // Transpose the two characters. 1083 String text = plainText(range.get()); 1084 if (text.length() != 2) 1085 return; 1086 String transposed = text.right(1) + text.left(1); 1087 1088 // Select the two characters. 1089 if (newSelection != m_frame.selection().selection()) 1090 m_frame.selection().setSelection(newSelection); 1091 1092 // Insert the transposed characters. 1093 replaceSelectionWithText(transposed, false, false); 1094 } 1095 1096 void Editor::addToKillRing(Range* range, bool prepend) 1097 { 1098 if (m_shouldStartNewKillRingSequence) 1099 killRing().startNewSequence(); 1100 1101 String text = plainText(range); 1102 if (prepend) 1103 killRing().prepend(text); 1104 else 1105 killRing().append(text); 1106 m_shouldStartNewKillRingSequence = false; 1107 } 1108 1109 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options) 1110 { 1111 // If the new selection is orphaned, then don't update the selection. 1112 if (newSelection.start().isOrphan() || newSelection.end().isOrphan()) 1113 return; 1114 1115 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid 1116 bool selectionDidNotChangeDOMPosition = newSelection == m_frame.selection().selection(); 1117 m_frame.selection().setSelection(newSelection, options); 1118 1119 // Some editing operations change the selection visually without affecting its position within the DOM. 1120 // For example when you press return in the following (the caret is marked by ^): 1121 // <div contentEditable="true"><div>^Hello</div></div> 1122 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't 1123 // change the caret's DOM position (["hello", 0]). In these situations the above FrameSelection::setSelection call 1124 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and 1125 // starts a new kill ring sequence, but we want to do these things (matches AppKit). 1126 if (selectionDidNotChangeDOMPosition) 1127 client().respondToChangedSelection(m_frame.selection().selectionType()); 1128 } 1129 1130 IntRect Editor::firstRectForRange(Range* range) const 1131 { 1132 LayoutUnit extraWidthToEndOfLine = 0; 1133 ASSERT(range->startContainer()); 1134 ASSERT(range->endContainer()); 1135 1136 IntRect startCaretRect = RenderedPosition(VisiblePosition(range->startPosition()).deepEquivalent(), DOWNSTREAM).absoluteRect(&extraWidthToEndOfLine); 1137 if (startCaretRect == LayoutRect()) 1138 return IntRect(); 1139 1140 IntRect endCaretRect = RenderedPosition(VisiblePosition(range->endPosition()).deepEquivalent(), UPSTREAM).absoluteRect(); 1141 if (endCaretRect == LayoutRect()) 1142 return IntRect(); 1143 1144 if (startCaretRect.y() == endCaretRect.y()) { 1145 // start and end are on the same line 1146 return IntRect(min(startCaretRect.x(), endCaretRect.x()), 1147 startCaretRect.y(), 1148 abs(endCaretRect.x() - startCaretRect.x()), 1149 max(startCaretRect.height(), endCaretRect.height())); 1150 } 1151 1152 // start and end aren't on the same line, so go from start to the end of its line 1153 return IntRect(startCaretRect.x(), 1154 startCaretRect.y(), 1155 startCaretRect.width() + extraWidthToEndOfLine, 1156 startCaretRect.height()); 1157 } 1158 1159 void Editor::computeAndSetTypingStyle(StylePropertySet* style, EditAction editingAction) 1160 { 1161 if (!style || style->isEmpty()) { 1162 m_frame.selection().clearTypingStyle(); 1163 return; 1164 } 1165 1166 // Calculate the current typing style. 1167 RefPtr<EditingStyle> typingStyle; 1168 if (m_frame.selection().typingStyle()) { 1169 typingStyle = m_frame.selection().typingStyle()->copy(); 1170 typingStyle->overrideWithStyle(style); 1171 } else { 1172 typingStyle = EditingStyle::create(style); 1173 } 1174 1175 typingStyle->prepareToApplyAt(m_frame.selection().selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection); 1176 1177 // Handle block styles, substracting these from the typing style. 1178 RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties(); 1179 if (!blockStyle->isEmpty()) { 1180 ASSERT(m_frame.document()); 1181 ApplyStyleCommand::create(*m_frame.document(), blockStyle.get(), editingAction)->apply(); 1182 } 1183 1184 // Set the remaining style as the typing style. 1185 m_frame.selection().setTypingStyle(typingStyle); 1186 } 1187 1188 bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection) 1189 { 1190 FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0); 1191 return findString(target, options); 1192 } 1193 1194 bool Editor::findString(const String& target, FindOptions options) 1195 { 1196 VisibleSelection selection = m_frame.selection().selection(); 1197 1198 RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options); 1199 1200 if (!resultRange) 1201 return false; 1202 1203 m_frame.selection().setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM)); 1204 m_frame.selection().revealSelection(); 1205 return true; 1206 } 1207 1208 PassRefPtr<Range> Editor::findStringAndScrollToVisible(const String& target, Range* previousMatch, FindOptions options) 1209 { 1210 RefPtr<Range> nextMatch = rangeOfString(target, previousMatch, options); 1211 if (!nextMatch) 1212 return 0; 1213 1214 nextMatch->firstNode()->renderer()->scrollRectToVisible(nextMatch->boundingBox(), 1215 ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); 1216 1217 return nextMatch.release(); 1218 } 1219 1220 PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options) 1221 { 1222 if (target.isEmpty()) 1223 return 0; 1224 1225 // Start from an edge of the reference range, if there's a reference range that's not in shadow content. Which edge 1226 // is used depends on whether we're searching forward or backward, and whether startInSelection is set. 1227 RefPtr<Range> searchRange(rangeOfContents(m_frame.document())); 1228 1229 bool forward = !(options & Backwards); 1230 bool startInReferenceRange = referenceRange && (options & StartInSelection); 1231 if (referenceRange) { 1232 if (forward) 1233 searchRange->setStart(startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition()); 1234 else 1235 searchRange->setEnd(startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition()); 1236 } 1237 1238 RefPtr<Node> shadowTreeRoot = referenceRange && referenceRange->startContainer() ? referenceRange->startContainer()->nonBoundaryShadowTreeRootNode() : 0; 1239 if (shadowTreeRoot) { 1240 if (forward) 1241 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount()); 1242 else 1243 searchRange->setStart(shadowTreeRoot.get(), 0); 1244 } 1245 1246 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options)); 1247 // If we started in the reference range and the found range exactly matches the reference range, find again. 1248 // Build a selection with the found range to remove collapsed whitespace. 1249 // Compare ranges instead of selection objects to ignore the way that the current selection was made. 1250 if (startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) { 1251 searchRange = rangeOfContents(m_frame.document()); 1252 if (forward) 1253 searchRange->setStart(referenceRange->endPosition()); 1254 else 1255 searchRange->setEnd(referenceRange->startPosition()); 1256 1257 if (shadowTreeRoot) { 1258 if (forward) 1259 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount()); 1260 else 1261 searchRange->setStart(shadowTreeRoot.get(), 0); 1262 } 1263 1264 resultRange = findPlainText(searchRange.get(), target, options); 1265 } 1266 1267 // If nothing was found in the shadow tree, search in main content following the shadow tree. 1268 if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && shadowTreeRoot) { 1269 searchRange = rangeOfContents(m_frame.document()); 1270 if (forward) 1271 searchRange->setStartAfter(shadowTreeRoot->shadowHost()); 1272 else 1273 searchRange->setEndBefore(shadowTreeRoot->shadowHost()); 1274 1275 resultRange = findPlainText(searchRange.get(), target, options); 1276 } 1277 1278 // If we didn't find anything and we're wrapping, search again in the entire document (this will 1279 // redundantly re-search the area already searched in some cases). 1280 if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && options & WrapAround) { 1281 searchRange = rangeOfContents(m_frame.document()); 1282 resultRange = findPlainText(searchRange.get(), target, options); 1283 // We used to return false here if we ended up with the same range that we started with 1284 // (e.g., the reference range was already the only instance of this text). But we decided that 1285 // this should be a success case instead, so we'll just fall through in that case. 1286 } 1287 1288 return resultRange->collapsed(ASSERT_NO_EXCEPTION) ? 0 : resultRange.release(); 1289 } 1290 1291 void Editor::setMarkedTextMatchesAreHighlighted(bool flag) 1292 { 1293 if (flag == m_areMarkedTextMatchesHighlighted) 1294 return; 1295 1296 m_areMarkedTextMatchesHighlighted = flag; 1297 m_frame.document()->markers()->repaintMarkers(DocumentMarker::TextMatch); 1298 } 1299 1300 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) 1301 { 1302 spellChecker().respondToChangedSelection(oldSelection, options); 1303 m_frame.inputMethodController().cancelCompositionIfSelectionIsInvalid(); 1304 notifyComponentsOnChangedSelection(oldSelection, options); 1305 } 1306 1307 SpellChecker& Editor::spellChecker() const 1308 { 1309 return m_frame.spellChecker(); 1310 } 1311 1312 void Editor::toggleOverwriteModeEnabled() 1313 { 1314 m_overwriteModeEnabled = !m_overwriteModeEnabled; 1315 frame().selection().setShouldShowBlockCursor(m_overwriteModeEnabled); 1316 } 1317 1318 } // namespace WebCore 1319