• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RevealSelectionScope(Editor * editor)94 Editor::RevealSelectionScope::RevealSelectionScope(Editor* editor)
95     : m_editor(editor)
96 {
97     ++m_editor->m_preventRevealSelection;
98 }
99 
~RevealSelectionScope()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.
selectionForCommand(Event * event)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.
behavior() const127 EditingBehavior Editor::behavior() const
128 {
129     if (!frame().settings())
130         return EditingBehavior(EditingMacBehavior);
131 
132     return EditingBehavior(frame().settings()->editingBehaviorType());
133 }
134 
emptyEditorClient()135 static EditorClient& emptyEditorClient()
136 {
137     DEFINE_STATIC_LOCAL(EmptyEditorClient, client, ());
138     return client;
139 }
140 
client() const141 EditorClient& Editor::client() const
142 {
143     if (Page* page = frame().page())
144         return page->editorClient();
145     return emptyEditorClient();
146 }
147 
undoStack() const148 UndoStack* Editor::undoStack() const
149 {
150     if (Page* page = frame().page())
151         return &page->undoStack();
152     return 0;
153 }
154 
handleTextEvent(TextEvent * event)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 
canEdit() const180 bool Editor::canEdit() const
181 {
182     return frame().selection().rootEditableElement();
183 }
184 
canEditRichly() const185 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 
canDHTMLCut()195 bool Editor::canDHTMLCut()
196 {
197     return !frame().selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecut, DataTransferNumb);
198 }
199 
canDHTMLCopy()200 bool Editor::canDHTMLCopy()
201 {
202     return !frame().selection().isInPasswordField() && !dispatchCPPEvent(EventTypeNames::beforecopy, DataTransferNumb);
203 }
204 
canDHTMLPaste()205 bool Editor::canDHTMLPaste()
206 {
207     return !dispatchCPPEvent(EventTypeNames::beforepaste, DataTransferNumb);
208 }
209 
canCut() const210 bool Editor::canCut() const
211 {
212     return canCopy() && canDelete();
213 }
214 
imageElementFromImageDocument(Document * document)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 
canCopy() const232 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 
canPaste() const240 bool Editor::canPaste() const
241 {
242     return canEdit();
243 }
244 
canDelete() const245 bool Editor::canDelete() const
246 {
247     FrameSelection& selection = frame().selection();
248     return selection.isRange() && selection.rootEditableElement();
249 }
250 
canDeleteRange(Range * range) const251 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 
smartInsertDeleteEnabled() const271 bool Editor::smartInsertDeleteEnabled() const
272 {
273     if (Settings* settings = frame().settings())
274         return settings->smartInsertDeleteEnabled();
275     return false;
276 }
277 
canSmartCopyOrDelete() const278 bool Editor::canSmartCopyOrDelete() const
279 {
280     return smartInsertDeleteEnabled() && frame().selection().granularity() == WordGranularity;
281 }
282 
isSelectTrailingWhitespaceEnabled() const283 bool Editor::isSelectTrailingWhitespaceEnabled() const
284 {
285     if (Settings* settings = frame().settings())
286         return settings->selectTrailingWhitespaceEnabled();
287     return false;
288 }
289 
deleteWithDirection(SelectionDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)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 
deleteSelectionWithSmartDelete(bool smartDelete)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 
pasteAsPlainText(const String & pastingText,bool smartReplace)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 
pasteAsFragment(PassRefPtrWillBeRawPtr<DocumentFragment> pastingFragment,bool smartReplace,bool matchStyle)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 
tryDHTMLCopy()361 bool Editor::tryDHTMLCopy()
362 {
363     if (frame().selection().isInPasswordField())
364         return false;
365 
366     return !dispatchCPPEvent(EventTypeNames::copy, DataTransferWritable);
367 }
368 
tryDHTMLCut()369 bool Editor::tryDHTMLCut()
370 {
371     if (frame().selection().isInPasswordField())
372         return false;
373 
374     return !dispatchCPPEvent(EventTypeNames::cut, DataTransferWritable);
375 }
376 
tryDHTMLPaste(PasteMode pasteMode)377 bool Editor::tryDHTMLPaste(PasteMode pasteMode)
378 {
379     return !dispatchCPPEvent(EventTypeNames::paste, DataTransferReadable, pasteMode);
380 }
381 
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)382 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
383 {
384     String text = pasteboard->plainText();
385     pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
386 }
387 
pasteWithPasteboard(Pasteboard * pasteboard)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 
writeSelectionToPasteboard(Pasteboard * pasteboard,Range * selectedRange,const String & plainText)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 
imageFromNode(const Node & node)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 
writeImageNodeToPasteboard(Pasteboard * pasteboard,Node * node,const String & title)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
dispatchCPPEvent(const AtomicString & eventType,DataTransferAccessPolicy policy,PasteMode pasteMode)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 
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)499 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
500 {
501     return smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
502 }
503 
replaceSelectionWithFragment(PassRefPtrWillBeRawPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)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 
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)525 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
526 {
527     replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
528 }
529 
selectedRange()530 PassRefPtrWillBeRawPtr<Range> Editor::selectedRange()
531 {
532     return frame().selection().toNormalizedRange();
533 }
534 
shouldDeleteRange(Range * range) const535 bool Editor::shouldDeleteRange(Range* range) const
536 {
537     if (!range || range->collapsed())
538         return false;
539 
540     return canDeleteRange(range);
541 }
542 
notifyComponentsOnChangedSelection(const VisibleSelection & oldSelection,FrameSelection::SetSelectionOptions options)543 void Editor::notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
544 {
545     client().respondToChangedSelection(m_frame, frame().selection().selectionType());
546     setStartNewKillRingSequence(true);
547 }
548 
respondToChangedContents(const VisibleSelection & endingSelection)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 
removeFormattingAndStyle()561 void Editor::removeFormattingAndStyle()
562 {
563     ASSERT(frame().document());
564     RemoveFormatCommand::create(*frame().document())->apply();
565 }
566 
clearLastEditCommand()567 void Editor::clearLastEditCommand()
568 {
569     m_lastEditCommand.clear();
570 }
571 
findEventTargetFrom(const VisibleSelection & selection) const572 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 
findEventTargetFromSelection() const581 Element* Editor::findEventTargetFromSelection() const
582 {
583     return findEventTargetFrom(frame().selection().selection());
584 }
585 
applyStyle(StylePropertySet * style,EditAction editingAction)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 
applyParagraphStyle(StylePropertySet * style,EditAction editingAction)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 
applyStyleToSelection(StylePropertySet * style,EditAction editingAction)612 void Editor::applyStyleToSelection(StylePropertySet* style, EditAction editingAction)
613 {
614     if (!style || style->isEmpty() || !canEditRichly())
615         return;
616 
617     applyStyle(style, editingAction);
618 }
619 
applyParagraphStyleToSelection(StylePropertySet * style,EditAction editingAction)620 void Editor::applyParagraphStyleToSelection(StylePropertySet* style, EditAction editingAction)
621 {
622     if (!style || style->isEmpty() || !canEditRichly())
623         return;
624 
625     applyParagraphStyle(style, editingAction);
626 }
627 
selectionStartHasStyle(CSSPropertyID propertyID,const String & value) const628 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 
selectionHasStyle(CSSPropertyID propertyID,const String & value) const634 TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const
635 {
636     return EditingStyle::create(propertyID, value)->triStateOfStyle(frame().selection().selection());
637 }
638 
selectionStartCSSPropertyValue(CSSPropertyID propertyID)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 
dispatchEditableContentChangedEvents(PassRefPtrWillBeRawPtr<Element> startRoot,PassRefPtrWillBeRawPtr<Element> endRoot)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 
appliedEditing(PassRefPtrWillBeRawPtr<CompositeEditCommand> cmd)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 
unappliedEditing(PassRefPtrWillBeRawPtr<EditCommandComposition> cmd)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 
reappliedEditing(PassRefPtrWillBeRawPtr<EditCommandComposition> cmd)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 
create(LocalFrame & frame)723 PassOwnPtrWillBeRawPtr<Editor> Editor::create(LocalFrame& frame)
724 {
725     return adoptPtrWillBeNoop(new Editor(frame));
726 }
727 
Editor(LocalFrame & frame)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 
~Editor()741 Editor::~Editor()
742 {
743 }
744 
clear()745 void Editor::clear()
746 {
747     frame().inputMethodController().clear();
748     m_shouldStyleWithCSS = false;
749     m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv;
750 }
751 
insertText(const String & text,KeyboardEvent * triggeringEvent)752 bool Editor::insertText(const String& text, KeyboardEvent* triggeringEvent)
753 {
754     return frame().eventHandler().handleTextInputEvent(text, triggeringEvent);
755 }
756 
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,TextEvent * triggeringEvent)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 
insertLineBreak()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 
insertParagraphSeparator()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 
cut()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 
copy()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 
paste()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 
pasteAsPlainText()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 
performDelete()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 
countEditingEvent(ExecutionContext * executionContext,const Event * event,UseCounter::Feature featureOnInput,UseCounter::Feature featureOnTextArea,UseCounter::Feature featureOnContentEditable,UseCounter::Feature featureOnNonNode)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 
countEvent(ExecutionContext * executionContext,const Event * event)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 
copyImage(const HitTestResult & result)965 void Editor::copyImage(const HitTestResult& result)
966 {
967     writeImageNodeToPasteboard(Pasteboard::generalPasteboard(), result.innerNonSharedNode(), result.altDisplayString());
968 }
969 
canUndo()970 bool Editor::canUndo()
971 {
972     if (UndoStack* undoStack = this->undoStack())
973         return undoStack->canUndo();
974     return false;
975 }
976 
undo()977 void Editor::undo()
978 {
979     if (UndoStack* undoStack = this->undoStack())
980         undoStack->undo();
981 }
982 
canRedo()983 bool Editor::canRedo()
984 {
985     if (UndoStack* undoStack = this->undoStack())
986         return undoStack->canRedo();
987     return false;
988 }
989 
redo()990 void Editor::redo()
991 {
992     if (UndoStack* undoStack = this->undoStack())
993         undoStack->redo();
994 }
995 
setBaseWritingDirection(WritingDirection direction)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 
revealSelectionAfterEditingOperation(const ScrollAlignment & alignment,RevealExtentOption revealExtentOption)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 
transpose()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 
addToKillRing(Range * range,bool prepend)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 
changeSelectionAfterCommand(const VisibleSelection & newSelection,FrameSelection::SetSelectionOptions options)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 
firstRectForRange(Range * range) const1092 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 
computeAndSetTypingStyle(StylePropertySet * style,EditAction editingAction)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 
findString(const String & target,bool forward,bool caseFlag,bool wrapFlag,bool startInSelection)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 
findString(const String & target,FindOptions options)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 
findStringAndScrollToVisible(const String & target,Range * previousMatch,FindOptions options)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 
findStringBetweenPositions(const String & target,const Position & start,const Position & end,FindOptions options)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 
rangeOfString(const String & target,Range * referenceRange,FindOptions options)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 
setMarkedTextMatchesAreHighlighted(bool flag)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 
respondToChangedSelection(const VisibleSelection & oldSelection,FrameSelection::SetSelectionOptions options)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 
spellChecker() const1270 SpellChecker& Editor::spellChecker() const
1271 {
1272     return frame().spellChecker();
1273 }
1274 
toggleOverwriteModeEnabled()1275 void Editor::toggleOverwriteModeEnabled()
1276 {
1277     m_overwriteModeEnabled = !m_overwriteModeEnabled;
1278     frame().selection().setShouldShowBlockCursor(m_overwriteModeEnabled);
1279 }
1280 
trace(Visitor * visitor)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