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 88 namespace blink { 89 90 using namespace HTMLNames; 91 using namespace WTF; 92 using namespace Unicode; 93 94 Editor::RevealSelectionScope::RevealSelectionScope(Editor* editor) 95 : m_editor(editor) 96 { 97 ++m_editor->m_preventRevealSelection; 98 } 99 100 Editor::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. 110 VisibleSelection 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. 127 EditingBehavior Editor::behavior() const 128 { 129 if (!frame().settings()) 130 return EditingBehavior(EditingMacBehavior); 131 132 return EditingBehavior(frame().settings()->editingBehaviorType()); 133 } 134 135 static EditorClient& emptyEditorClient() 136 { 137 DEFINE_STATIC_LOCAL(EmptyEditorClient, client, ()); 138 return client; 139 } 140 141 EditorClient& Editor::client() const 142 { 143 if (Page* page = frame().page()) 144 return page->editorClient(); 145 return emptyEditorClient(); 146 } 147 148 UndoStack* Editor::undoStack() const 149 { 150 if (Page* page = frame().page()) 151 return &page->undoStack(); 152 return 0; 153 } 154 155 bool 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 180 bool Editor::canEdit() const 181 { 182 return frame().selection().rootEditableElement(); 183 } 184 185 bool 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 195 bool Editor::canDHTMLCut() 196 { 197 return !frame().selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecut, DataTransferNumb); 198 } 199 200 bool Editor::canDHTMLCopy() 201 { 202 return !frame().selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecopy, DataTransferNumb); 203 } 204 205 bool Editor::canDHTMLPaste() 206 { 207 return !dispatchCPPEvent(EventTypeNames::beforepaste, DataTransferNumb); 208 } 209 210 bool Editor::canCut() const 211 { 212 return canCopy() && canDelete(); 213 } 214 215 static 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 232 bool 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 240 bool Editor::canPaste() const 241 { 242 return canEdit(); 243 } 244 245 bool Editor::canDelete() const 246 { 247 FrameSelection& selection = frame().selection(); 248 return selection.isRange() && selection.rootEditableElement(); 249 } 250 251 bool 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 271 bool Editor::smartInsertDeleteEnabled() const 272 { 273 if (Settings* settings = frame().settings()) 274 return settings->smartInsertDeleteEnabled(); 275 return false; 276 } 277 278 bool Editor::canSmartCopyOrDelete() const 279 { 280 return smartInsertDeleteEnabled() && frame().selection().granularity() == WordGranularity; 281 } 282 283 bool Editor::isSelectTrailingWhitespaceEnabled() const 284 { 285 if (Settings* settings = frame().settings()) 286 return settings->selectTrailingWhitespaceEnabled(); 287 return false; 288 } 289 290 bool 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 336 void 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 345 void 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 353 void 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 361 bool Editor::tryDHTMLCopy() 362 { 363 if (frame().selection().isInPasswordField()) 364 return false; 365 366 return !dispatchCPPEvent(EventTypeNames::copy, DataTransferWritable); 367 } 368 369 bool Editor::tryDHTMLCut() 370 { 371 if (frame().selection().isInPasswordField()) 372 return false; 373 374 return !dispatchCPPEvent(EventTypeNames::cut, DataTransferWritable); 375 } 376 377 bool Editor::tryDHTMLPaste(PasteMode pasteMode) 378 { 379 return !dispatchCPPEvent(EventTypeNames::paste, DataTransferReadable, pasteMode); 380 } 381 382 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard) 383 { 384 String text = pasteboard->plainText(); 385 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard)); 386 } 387 388 void 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 417 void 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 424 static 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 448 static 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 472 bool 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 499 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard) 500 { 501 return smartInsertDeleteEnabled() && pasteboard->canSmartReplace(); 502 } 503 504 void 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 525 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) 526 { 527 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true); 528 } 529 530 PassRefPtrWillBeRawPtr<Range> Editor::selectedRange() 531 { 532 return frame().selection().toNormalizedRange(); 533 } 534 535 bool Editor::shouldDeleteRange(Range* range) const 536 { 537 if (!range || range->collapsed()) 538 return false; 539 540 return canDeleteRange(range); 541 } 542 543 void Editor::notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) 544 { 545 client().respondToChangedSelection(m_frame, frame().selection().selectionType()); 546 setStartNewKillRingSequence(true); 547 } 548 549 void 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 561 void Editor::removeFormattingAndStyle() 562 { 563 ASSERT(frame().document()); 564 RemoveFormatCommand::create(*frame().document())->apply(); 565 } 566 567 void Editor::clearLastEditCommand() 568 { 569 m_lastEditCommand.clear(); 570 } 571 572 Element* 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 581 Element* Editor::findEventTargetFromSelection() const 582 { 583 return findEventTargetFrom(frame().selection().selection()); 584 } 585 586 void 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 604 void 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 612 void Editor::applyStyleToSelection(StylePropertySet* style, EditAction editingAction) 613 { 614 if (!style || style->isEmpty() || !canEditRichly()) 615 return; 616 617 applyStyle(style, editingAction); 618 } 619 620 void Editor::applyParagraphStyleToSelection(StylePropertySet* style, EditAction editingAction) 621 { 622 if (!style || style->isEmpty() || !canEditRichly()) 623 return; 624 625 applyParagraphStyle(style, editingAction); 626 } 627 628 bool 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 634 TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const 635 { 636 return EditingStyle::create(propertyID, value)->triStateOfStyle(frame().selection().selection()); 637 } 638 639 String 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 651 static 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 659 void 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 689 void 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 707 void 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 723 PassOwnPtrWillBeRawPtr<Editor> Editor::create(LocalFrame& frame) 724 { 725 return adoptPtrWillBeNoop(new Editor(frame)); 726 } 727 728 Editor::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 741 Editor::~Editor() 742 { 743 } 744 745 void Editor::clear() 746 { 747 frame().inputMethodController().clear(); 748 m_shouldStyleWithCSS = false; 749 m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv; 750 } 751 752 bool Editor::insertText(const String& text, KeyboardEvent* triggeringEvent) 753 { 754 return frame().eventHandler().handleTextInputEvent(text, triggeringEvent); 755 } 756 757 bool 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 793 bool 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 807 bool 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 824 void 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 844 void 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 862 void 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 878 void Editor::pasteAsPlainText() 879 { 880 if (tryDHTMLPaste(PlainTextOnly)) 881 return; 882 if (!canPaste()) 883 return; 884 spellChecker().updateMarkersForWordsAffectedByEditing(false); 885 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); 886 } 887 888 void 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 900 static 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 933 void 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 965 void Editor::copyImage(const HitTestResult& result) 966 { 967 writeImageNodeToPasteboard(Pasteboard::generalPasteboard(), result.innerNonSharedNode(), result.altDisplayString()); 968 } 969 970 bool Editor::canUndo() 971 { 972 if (UndoStack* undoStack = this->undoStack()) 973 return undoStack->canUndo(); 974 return false; 975 } 976 977 void Editor::undo() 978 { 979 if (UndoStack* undoStack = this->undoStack()) 980 undoStack->undo(); 981 } 982 983 bool Editor::canRedo() 984 { 985 if (UndoStack* undoStack = this->undoStack()) 986 return undoStack->canRedo(); 987 return false; 988 } 989 990 void Editor::redo() 991 { 992 if (UndoStack* undoStack = this->undoStack()) 993 undoStack->redo(); 994 } 995 996 void 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 1013 void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) 1014 { 1015 if (m_preventRevealSelection) 1016 return; 1017 1018 frame().selection().revealSelection(alignment, revealExtentOption); 1019 } 1020 1021 void 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 1058 void 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 1071 void 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 1092 IntRect 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 1121 void 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 1150 bool 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 1156 bool 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 1170 PassRefPtrWillBeRawPtr<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 1182 static 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 1213 PassRefPtrWillBeRawPtr<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 1254 void 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 1263 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) 1264 { 1265 spellChecker().respondToChangedSelection(oldSelection, options); 1266 frame().inputMethodController().cancelCompositionIfSelectionIsInvalid(); 1267 notifyComponentsOnChangedSelection(oldSelection, options); 1268 } 1269 1270 SpellChecker& Editor::spellChecker() const 1271 { 1272 return frame().spellChecker(); 1273 } 1274 1275 void Editor::toggleOverwriteModeEnabled() 1276 { 1277 m_overwriteModeEnabled = !m_overwriteModeEnabled; 1278 frame().selection().setShouldShowBlockCursor(m_overwriteModeEnabled); 1279 } 1280 1281 void 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