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