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