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