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