1 /*
2 * Copyright (C) 2006, 2007, 2008 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 "Editor.h"
29
30 #include "AXObjectCache.h"
31 #include "ApplyStyleCommand.h"
32 #include "CSSComputedStyleDeclaration.h"
33 #include "CSSMutableStyleDeclaration.h"
34 #include "CSSProperty.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSStyleSelector.h"
37 #include "CSSValueKeywords.h"
38 #include "CachedResourceLoader.h"
39 #include "ClipboardEvent.h"
40 #include "CompositionEvent.h"
41 #include "SpellingCorrectionController.h"
42 #include "CreateLinkCommand.h"
43 #include "DeleteButtonController.h"
44 #include "DeleteSelectionCommand.h"
45 #include "DocumentFragment.h"
46 #include "DocumentMarkerController.h"
47 #include "EditingText.h"
48 #include "EditorClient.h"
49 #include "EventHandler.h"
50 #include "EventNames.h"
51 #include "FocusController.h"
52 #include "Frame.h"
53 #include "FrameTree.h"
54 #include "FrameView.h"
55 #include "GraphicsContext.h"
56 #include "HTMLFrameOwnerElement.h"
57 #include "HTMLInputElement.h"
58 #include "HTMLTextAreaElement.h"
59 #include "HitTestResult.h"
60 #include "IndentOutdentCommand.h"
61 #include "InsertListCommand.h"
62 #include "KeyboardEvent.h"
63 #include "KillRing.h"
64 #include "ModifySelectionListLevel.h"
65 #include "NodeList.h"
66 #include "Page.h"
67 #include "Pasteboard.h"
68 #include "TextCheckingHelper.h"
69 #include "RemoveFormatCommand.h"
70 #include "RenderBlock.h"
71 #include "RenderPart.h"
72 #include "RenderTextControl.h"
73 #include "ReplaceSelectionCommand.h"
74 #include "Settings.h"
75 #include "Sound.h"
76 #include "SpellChecker.h"
77 #include "SpellingCorrectionCommand.h"
78 #include "Text.h"
79 #include "TextEvent.h"
80 #include "TextIterator.h"
81 #include "TypingCommand.h"
82 #include "UserTypingGestureIndicator.h"
83 #include "htmlediting.h"
84 #include "markup.h"
85 #include "visible_units.h"
86 #include <wtf/UnusedParam.h>
87 #include <wtf/unicode/CharacterNames.h>
88 #include <wtf/unicode/Unicode.h>
89
90 namespace WebCore {
91
92 using namespace std;
93 using namespace HTMLNames;
94 using namespace WTF;
95 using namespace Unicode;
96
97 // When an event handler has moved the selection outside of a text control
98 // we should use the target control's selection for this editing operation.
selectionForCommand(Event * event)99 VisibleSelection Editor::selectionForCommand(Event* event)
100 {
101 VisibleSelection selection = m_frame->selection()->selection();
102 if (!event)
103 return selection;
104 // If the target is a text control, and the current selection is outside of its shadow tree,
105 // then use the saved selection for that text control.
106 Node* target = event->target()->toNode();
107 Node* selectionStart = selection.start().deprecatedNode();
108 if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
109 RefPtr<Range> range;
110 if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
111 range = static_cast<HTMLInputElement*>(target)->selection();
112 else if (target->hasTagName(textareaTag))
113 range = static_cast<HTMLTextAreaElement*>(target)->selection();
114
115 if (range)
116 return VisibleSelection(range.get());
117 }
118 return selection;
119 }
120
121 // Function considers Mac editing behavior a fallback when Page or Settings is not available.
behavior() const122 EditingBehavior Editor::behavior() const
123 {
124 if (!m_frame || !m_frame->settings())
125 return EditingBehavior(EditingMacBehavior);
126
127 return EditingBehavior(m_frame->settings()->editingBehaviorType());
128 }
129
client() const130 EditorClient* Editor::client() const
131 {
132 if (Page* page = m_frame->page())
133 return page->editorClient();
134 return 0;
135 }
136
137
textChecker() const138 TextCheckerClient* Editor::textChecker() const
139 {
140 if (EditorClient* owner = client())
141 return owner->textChecker();
142 return 0;
143 }
144
handleKeyboardEvent(KeyboardEvent * event)145 void Editor::handleKeyboardEvent(KeyboardEvent* event)
146 {
147 if (EditorClient* c = client())
148 c->handleKeyboardEvent(event);
149 }
150
handleInputMethodKeydown(KeyboardEvent * event)151 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
152 {
153 if (EditorClient* c = client())
154 c->handleInputMethodKeydown(event);
155 }
156
handleTextEvent(TextEvent * event)157 bool Editor::handleTextEvent(TextEvent* event)
158 {
159 // Default event handling for Drag and Drop will be handled by DragController
160 // so we leave the event for it.
161 if (event->isDrop())
162 return false;
163
164 if (event->isPaste()) {
165 if (event->pastingFragment())
166 replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle());
167 else
168 replaceSelectionWithText(event->data(), false, event->shouldSmartReplace());
169 return true;
170 }
171
172 String data = event->data();
173 if (data == "\n") {
174 if (event->isLineBreak())
175 return insertLineBreak();
176 return insertParagraphSeparator();
177 }
178
179 return insertTextWithoutSendingTextEvent(data, false, event);
180 }
181
canEdit() const182 bool Editor::canEdit() const
183 {
184 return m_frame->selection()->rootEditableElement();
185 }
186
canEditRichly() const187 bool Editor::canEditRichly() const
188 {
189 return m_frame->selection()->isContentRichlyEditable();
190 }
191
192 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
193 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
194 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
195 // normally selectable to implement copy/paste (like divs, or a document body).
196
canDHTMLCut()197 bool Editor::canDHTMLCut()
198 {
199 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
200 }
201
canDHTMLCopy()202 bool Editor::canDHTMLCopy()
203 {
204 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
205 }
206
canDHTMLPaste()207 bool Editor::canDHTMLPaste()
208 {
209 return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
210 }
211
canCut() const212 bool Editor::canCut() const
213 {
214 return canCopy() && canDelete();
215 }
216
imageElementFromImageDocument(Document * document)217 static HTMLImageElement* imageElementFromImageDocument(Document* document)
218 {
219 if (!document)
220 return 0;
221 if (!document->isImageDocument())
222 return 0;
223
224 HTMLElement* body = document->body();
225 if (!body)
226 return 0;
227
228 Node* node = body->firstChild();
229 if (!node)
230 return 0;
231 if (!node->hasTagName(imgTag))
232 return 0;
233 return static_cast<HTMLImageElement*>(node);
234 }
235
canCopy() const236 bool Editor::canCopy() const
237 {
238 if (imageElementFromImageDocument(m_frame->document()))
239 return true;
240 SelectionController* selection = m_frame->selection();
241 return selection->isRange() && !selection->isInPasswordField();
242 }
243
canPaste() const244 bool Editor::canPaste() const
245 {
246 return canEdit();
247 }
248
canDelete() const249 bool Editor::canDelete() const
250 {
251 SelectionController* selection = m_frame->selection();
252 return selection->isRange() && selection->rootEditableElement();
253 }
254
canDeleteRange(Range * range) const255 bool Editor::canDeleteRange(Range* range) const
256 {
257 ExceptionCode ec = 0;
258 Node* startContainer = range->startContainer(ec);
259 Node* endContainer = range->endContainer(ec);
260 if (!startContainer || !endContainer)
261 return false;
262
263 if (!startContainer->rendererIsEditable() || !endContainer->rendererIsEditable())
264 return false;
265
266 if (range->collapsed(ec)) {
267 VisiblePosition start(Position(startContainer, range->startOffset(ec), Position::PositionIsOffsetInAnchor), DOWNSTREAM);
268 VisiblePosition previous = start.previous();
269 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
270 if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement())
271 return false;
272 }
273 return true;
274 }
275
smartInsertDeleteEnabled()276 bool Editor::smartInsertDeleteEnabled()
277 {
278 return client() && client()->smartInsertDeleteEnabled();
279 }
280
canSmartCopyOrDelete()281 bool Editor::canSmartCopyOrDelete()
282 {
283 return client() && client()->smartInsertDeleteEnabled() && m_frame->selection()->granularity() == WordGranularity;
284 }
285
isSelectTrailingWhitespaceEnabled()286 bool Editor::isSelectTrailingWhitespaceEnabled()
287 {
288 return client() && client()->isSelectTrailingWhitespaceEnabled();
289 }
290
deleteWithDirection(SelectionDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)291 bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
292 {
293 if (!canEdit())
294 return false;
295
296 if (m_frame->selection()->isRange()) {
297 if (isTypingAction) {
298 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity);
299 revealSelectionAfterEditingOperation();
300 } else {
301 if (killRing)
302 addToKillRing(selectedRange().get(), false);
303 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
304 // Implicitly calls revealSelectionAfterEditingOperation().
305 }
306 } else {
307 TypingCommand::Options options = 0;
308 if (canSmartCopyOrDelete())
309 options |= TypingCommand::SmartDelete;
310 if (killRing)
311 options |= TypingCommand::KillRing;
312 switch (direction) {
313 case DirectionForward:
314 case DirectionRight:
315 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), options, granularity);
316 break;
317 case DirectionBackward:
318 case DirectionLeft:
319 TypingCommand::deleteKeyPressed(m_frame->document(), options, granularity);
320 break;
321 }
322 revealSelectionAfterEditingOperation();
323 }
324
325 // FIXME: We should to move this down into deleteKeyPressed.
326 // clear the "start new kill ring sequence" setting, because it was set to true
327 // when the selection was updated by deleting the range
328 if (killRing)
329 setStartNewKillRingSequence(false);
330
331 return true;
332 }
333
deleteSelectionWithSmartDelete(bool smartDelete)334 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
335 {
336 if (m_frame->selection()->isNone())
337 return;
338
339 applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
340 }
341
pasteAsPlainText(const String & pastingText,bool smartReplace)342 void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace)
343 {
344 Node* target = findEventTargetFromSelection();
345 if (!target)
346 return;
347 ExceptionCode ec = 0;
348 target->dispatchEvent(TextEvent::createForPlainTextPaste(m_frame->domWindow(), pastingText, smartReplace), ec);
349 }
350
pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment,bool smartReplace,bool matchStyle)351 void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle)
352 {
353 Node* target = findEventTargetFromSelection();
354 if (!target)
355 return;
356 ExceptionCode ec = 0;
357 target->dispatchEvent(TextEvent::createForFragmentPaste(m_frame->domWindow(), pastingFragment, smartReplace, matchStyle), ec);
358 }
359
pasteAsPlainTextBypassingDHTML()360 void Editor::pasteAsPlainTextBypassingDHTML()
361 {
362 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
363 }
364
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)365 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
366 {
367 String text = pasteboard->plainText(m_frame);
368 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
369 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
370 }
371
372 #if !PLATFORM(MAC)
pasteWithPasteboard(Pasteboard * pasteboard,bool allowPlainText)373 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
374 {
375 RefPtr<Range> range = selectedRange();
376 bool chosePlainText;
377 RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
378 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
379 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
380 }
381 #endif
382
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)383 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
384 {
385 return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
386 }
387
shouldInsertFragment(PassRefPtr<DocumentFragment> fragment,PassRefPtr<Range> replacingDOMRange,EditorInsertAction givenAction)388 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
389 {
390 if (!client())
391 return false;
392
393 if (fragment) {
394 Node* child = fragment->firstChild();
395 if (child && fragment->lastChild() == child && child->isCharacterDataNode())
396 return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
397 }
398
399 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
400 }
401
replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)402 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
403 {
404 if (m_frame->selection()->isNone() || !fragment)
405 return;
406
407 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting;
408 if (selectReplacement)
409 options |= ReplaceSelectionCommand::SelectReplacement;
410 if (smartReplace)
411 options |= ReplaceSelectionCommand::SmartReplace;
412 if (matchStyle)
413 options |= ReplaceSelectionCommand::MatchStyle;
414 applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, options, EditActionPaste));
415 revealSelectionAfterEditingOperation();
416
417 Node* nodeToCheck = m_frame->selection()->rootEditableElement();
418 if (m_spellChecker->canCheckAsynchronously(nodeToCheck))
419 m_spellChecker->requestCheckingFor(textCheckingTypeMaskFor(MarkSpelling | MarkGrammar), nodeToCheck);
420 }
421
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)422 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
423 {
424 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
425 }
426
selectedRange()427 PassRefPtr<Range> Editor::selectedRange()
428 {
429 if (!m_frame)
430 return 0;
431 return m_frame->selection()->toNormalizedRange();
432 }
433
shouldDeleteRange(Range * range) const434 bool Editor::shouldDeleteRange(Range* range) const
435 {
436 ExceptionCode ec;
437 if (!range || range->collapsed(ec))
438 return false;
439
440 if (!canDeleteRange(range))
441 return false;
442
443 return client() && client()->shouldDeleteRange(range);
444 }
445
tryDHTMLCopy()446 bool Editor::tryDHTMLCopy()
447 {
448 if (m_frame->selection()->isInPasswordField())
449 return false;
450
451 if (canCopy())
452 // Must be done before oncopy adds types and data to the pboard,
453 // also done for security, as it erases data from the last copy/paste.
454 Pasteboard::generalPasteboard()->clear();
455
456 return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
457 }
458
tryDHTMLCut()459 bool Editor::tryDHTMLCut()
460 {
461 if (m_frame->selection()->isInPasswordField())
462 return false;
463
464 if (canCut())
465 // Must be done before oncut adds types and data to the pboard,
466 // also done for security, as it erases data from the last copy/paste.
467 Pasteboard::generalPasteboard()->clear();
468
469 return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
470 }
471
tryDHTMLPaste()472 bool Editor::tryDHTMLPaste()
473 {
474 return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
475 }
476
writeSelectionToPasteboard(Pasteboard * pasteboard)477 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
478 {
479 pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
480 }
481
shouldInsertText(const String & text,Range * range,EditorInsertAction action) const482 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
483 {
484 return client() && client()->shouldInsertText(text, range, action);
485 }
486
shouldShowDeleteInterface(HTMLElement * element) const487 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
488 {
489 return client() && client()->shouldShowDeleteInterface(element);
490 }
491
respondToChangedSelection(const VisibleSelection & oldSelection)492 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
493 {
494 if (client())
495 client()->respondToChangedSelection();
496 m_deleteButtonController->respondToChangedSelection(oldSelection);
497 m_spellingCorrector->respondToChangedSelection(oldSelection);
498 }
499
respondToChangedContents(const VisibleSelection & endingSelection)500 void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
501 {
502 if (AXObjectCache::accessibilityEnabled()) {
503 Node* node = endingSelection.start().deprecatedNode();
504 if (node)
505 m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false);
506 }
507
508 updateMarkersForWordsAffectedByEditing(true);
509
510 if (client())
511 client()->respondToChangedContents();
512 }
513
fontForSelection(bool & hasMultipleFonts) const514 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
515 {
516 #if !PLATFORM(QT)
517 hasMultipleFonts = false;
518
519 if (!m_frame->selection()->isRange()) {
520 Node* nodeToRemove;
521 RenderStyle* style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
522
523 const SimpleFontData* result = 0;
524 if (style)
525 result = style->font().primaryFont();
526
527 if (nodeToRemove) {
528 ExceptionCode ec;
529 nodeToRemove->remove(ec);
530 ASSERT(!ec);
531 }
532
533 return result;
534 }
535
536 const SimpleFontData* font = 0;
537
538 RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
539 Node* startNode = range->editingStartPosition().deprecatedNode();
540 if (startNode) {
541 Node* pastEnd = range->pastLastNode();
542 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
543 // unreproducible case where this didn't happen, so check for nil also.
544 for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
545 RenderObject* renderer = n->renderer();
546 if (!renderer)
547 continue;
548 // FIXME: Are there any node types that have renderers, but that we should be skipping?
549 const SimpleFontData* f = renderer->style()->font().primaryFont();
550 if (!font)
551 font = f;
552 else if (font != f) {
553 hasMultipleFonts = true;
554 break;
555 }
556 }
557 }
558
559 return font;
560 #else
561 return 0;
562 #endif
563 }
564
textDirectionForSelection(bool & hasNestedOrMultipleEmbeddings) const565 WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const
566 {
567 hasNestedOrMultipleEmbeddings = true;
568
569 if (m_frame->selection()->isNone())
570 return NaturalWritingDirection;
571
572 Position position = m_frame->selection()->selection().start().downstream();
573
574 Node* node = position.deprecatedNode();
575 if (!node)
576 return NaturalWritingDirection;
577
578 Position end;
579 if (m_frame->selection()->isRange()) {
580 end = m_frame->selection()->selection().end().upstream();
581
582 Node* pastLast = Range::create(m_frame->document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
583 for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
584 if (!n->isStyledElement())
585 continue;
586
587 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n);
588 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
589 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
590 continue;
591
592 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
593 if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
594 return NaturalWritingDirection;
595 }
596 }
597
598 if (m_frame->selection()->isCaret()) {
599 RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle();
600 WritingDirection direction;
601 if (typingStyle && typingStyle->textDirection(direction)) {
602 hasNestedOrMultipleEmbeddings = false;
603 return direction;
604 }
605 node = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode();
606 }
607
608 // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position
609 // to decide.
610 Node* block = enclosingBlock(node);
611 WritingDirection foundDirection = NaturalWritingDirection;
612
613 for (; node != block; node = node->parentNode()) {
614 if (!node->isStyledElement())
615 continue;
616
617 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
618 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
619 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
620 continue;
621
622 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
623 if (unicodeBidiValue == CSSValueNormal)
624 continue;
625
626 if (unicodeBidiValue == CSSValueBidiOverride)
627 return NaturalWritingDirection;
628
629 ASSERT(unicodeBidiValue == CSSValueEmbed);
630 RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
631 if (!direction || !direction->isPrimitiveValue())
632 continue;
633
634 int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
635 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
636 continue;
637
638 if (foundDirection != NaturalWritingDirection)
639 return NaturalWritingDirection;
640
641 // In the range case, make sure that the embedding element persists until the end of the range.
642 if (m_frame->selection()->isRange() && !end.deprecatedNode()->isDescendantOf(node))
643 return NaturalWritingDirection;
644
645 foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
646 }
647 hasNestedOrMultipleEmbeddings = false;
648 return foundDirection;
649 }
650
hasBidiSelection() const651 bool Editor::hasBidiSelection() const
652 {
653 if (m_frame->selection()->isNone())
654 return false;
655
656 Node* startNode;
657 if (m_frame->selection()->isRange()) {
658 startNode = m_frame->selection()->selection().start().downstream().deprecatedNode();
659 Node* endNode = m_frame->selection()->selection().end().upstream().deprecatedNode();
660 if (enclosingBlock(startNode) != enclosingBlock(endNode))
661 return false;
662 } else
663 startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode();
664
665 RenderObject* renderer = startNode->renderer();
666 while (renderer && !renderer->isRenderBlock())
667 renderer = renderer->parent();
668
669 if (!renderer)
670 return false;
671
672 RenderStyle* style = renderer->style();
673 if (!style->isLeftToRightDirection())
674 return true;
675
676 return toRenderBlock(renderer)->containsNonZeroBidiLevel();
677 }
678
selectionUnorderedListState() const679 TriState Editor::selectionUnorderedListState() const
680 {
681 if (m_frame->selection()->isCaret()) {
682 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag))
683 return TrueTriState;
684 } else if (m_frame->selection()->isRange()) {
685 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag);
686 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag);
687 if (startNode && endNode && startNode == endNode)
688 return TrueTriState;
689 }
690
691 return FalseTriState;
692 }
693
selectionOrderedListState() const694 TriState Editor::selectionOrderedListState() const
695 {
696 if (m_frame->selection()->isCaret()) {
697 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag))
698 return TrueTriState;
699 } else if (m_frame->selection()->isRange()) {
700 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag);
701 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag);
702 if (startNode && endNode && startNode == endNode)
703 return TrueTriState;
704 }
705
706 return FalseTriState;
707 }
708
insertOrderedList()709 PassRefPtr<Node> Editor::insertOrderedList()
710 {
711 if (!canEditRichly())
712 return 0;
713
714 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
715 revealSelectionAfterEditingOperation();
716 return newList;
717 }
718
insertUnorderedList()719 PassRefPtr<Node> Editor::insertUnorderedList()
720 {
721 if (!canEditRichly())
722 return 0;
723
724 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
725 revealSelectionAfterEditingOperation();
726 return newList;
727 }
728
canIncreaseSelectionListLevel()729 bool Editor::canIncreaseSelectionListLevel()
730 {
731 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
732 }
733
canDecreaseSelectionListLevel()734 bool Editor::canDecreaseSelectionListLevel()
735 {
736 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
737 }
738
increaseSelectionListLevel()739 PassRefPtr<Node> Editor::increaseSelectionListLevel()
740 {
741 if (!canEditRichly() || m_frame->selection()->isNone())
742 return 0;
743
744 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
745 revealSelectionAfterEditingOperation();
746 return newList;
747 }
748
increaseSelectionListLevelOrdered()749 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
750 {
751 if (!canEditRichly() || m_frame->selection()->isNone())
752 return 0;
753
754 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
755 revealSelectionAfterEditingOperation();
756 return newList.release();
757 }
758
increaseSelectionListLevelUnordered()759 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
760 {
761 if (!canEditRichly() || m_frame->selection()->isNone())
762 return 0;
763
764 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
765 revealSelectionAfterEditingOperation();
766 return newList.release();
767 }
768
decreaseSelectionListLevel()769 void Editor::decreaseSelectionListLevel()
770 {
771 if (!canEditRichly() || m_frame->selection()->isNone())
772 return;
773
774 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
775 revealSelectionAfterEditingOperation();
776 }
777
removeFormattingAndStyle()778 void Editor::removeFormattingAndStyle()
779 {
780 applyCommand(RemoveFormatCommand::create(m_frame->document()));
781 }
782
clearLastEditCommand()783 void Editor::clearLastEditCommand()
784 {
785 m_lastEditCommand.clear();
786 }
787
788 // Returns whether caller should continue with "the default processing", which is the same as
789 // the event handler NOT setting the return value to false
dispatchCPPEvent(const AtomicString & eventType,ClipboardAccessPolicy policy)790 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
791 {
792 Node* target = findEventTargetFromSelection();
793 if (!target)
794 return true;
795
796 RefPtr<Clipboard> clipboard = newGeneralClipboard(policy, m_frame);
797
798 ExceptionCode ec = 0;
799 RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
800 target->dispatchEvent(evt, ec);
801 bool noDefaultProcessing = evt->defaultPrevented();
802
803 // invalidate clipboard here for security
804 clipboard->setAccessPolicy(ClipboardNumb);
805
806 return !noDefaultProcessing;
807 }
808
findEventTargetFrom(const VisibleSelection & selection) const809 Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const
810 {
811 Node* target = selection.start().element();
812 if (!target)
813 target = m_frame->document()->body();
814 if (!target)
815 return 0;
816 return target->shadowAncestorNode();
817
818 }
819
findEventTargetFromSelection() const820 Node* Editor::findEventTargetFromSelection() const
821 {
822 return findEventTargetFrom(m_frame->selection()->selection());
823 }
824
applyStyle(CSSStyleDeclaration * style,EditAction editingAction)825 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
826 {
827 switch (m_frame->selection()->selectionType()) {
828 case VisibleSelection::NoSelection:
829 // do nothing
830 break;
831 case VisibleSelection::CaretSelection:
832 computeAndSetTypingStyle(style, editingAction);
833 break;
834 case VisibleSelection::RangeSelection:
835 if (style)
836 applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction));
837 break;
838 }
839 }
840
shouldApplyStyle(CSSStyleDeclaration * style,Range * range)841 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
842 {
843 return client()->shouldApplyStyle(style, range);
844 }
845
applyParagraphStyle(CSSStyleDeclaration * style,EditAction editingAction)846 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
847 {
848 switch (m_frame->selection()->selectionType()) {
849 case VisibleSelection::NoSelection:
850 // do nothing
851 break;
852 case VisibleSelection::CaretSelection:
853 case VisibleSelection::RangeSelection:
854 if (style)
855 applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties));
856 break;
857 }
858 }
859
applyStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)860 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
861 {
862 if (!style || !style->length() || !canEditRichly())
863 return;
864
865 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
866 applyStyle(style, editingAction);
867 }
868
applyParagraphStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)869 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
870 {
871 if (!style || !style->length() || !canEditRichly())
872 return;
873
874 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
875 applyParagraphStyle(style, editingAction);
876 }
877
selectionStartHasStyle(int propertyID,const String & value) const878 bool Editor::selectionStartHasStyle(int propertyID, const String& value) const
879 {
880 RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value);
881 RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
882 if (!selectionStyle || !selectionStyle->style())
883 return false;
884 return style->triStateOfStyle(selectionStyle->style()) == TrueTriState;
885 }
886
selectionHasStyle(int propertyID,const String & value) const887 TriState Editor::selectionHasStyle(int propertyID, const String& value) const
888 {
889 RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value);
890 if (!m_frame->selection()->isCaretOrRange())
891 return FalseTriState;
892
893 if (m_frame->selection()->isCaret()) {
894 RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
895 if (!selectionStyle || !selectionStyle->style())
896 return FalseTriState;
897 return style->triStateOfStyle(selectionStyle->style());
898 }
899
900 TriState state = FalseTriState;
901 for (Node* node = m_frame->selection()->start().deprecatedNode(); node; node = node->traverseNextNode()) {
902 RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
903 if (nodeStyle) {
904 TriState nodeState = style->triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
905 if (node == m_frame->selection()->start().deprecatedNode())
906 state = nodeState;
907 else if (state != nodeState && node->isTextNode()) {
908 state = MixedTriState;
909 break;
910 }
911 }
912 if (node == m_frame->selection()->end().deprecatedNode())
913 break;
914 }
915
916 return state;
917 }
918
selectionStartCSSPropertyValue(int propertyID)919 String Editor::selectionStartCSSPropertyValue(int propertyID)
920 {
921 RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
922 if (!selectionStyle || !selectionStyle->style())
923 return String();
924
925 String value = selectionStyle->style()->getPropertyValue(propertyID);
926
927 // If background color is transparent, traverse parent nodes until we hit a different value or document root
928 // Also, if the selection is a range, ignore the background color at the start of selection,
929 // and find the background color of the common ancestor.
930 if (propertyID == CSSPropertyBackgroundColor && (m_frame->selection()->isRange() || hasTransparentBackgroundColor(selectionStyle->style()))) {
931 RefPtr<Range> range(m_frame->selection()->toNormalizedRange());
932 ExceptionCode ec = 0;
933 if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(ec)))
934 return value->cssText();
935 }
936
937 if (propertyID == CSSPropertyFontSize) {
938 RefPtr<CSSValue> cssValue = selectionStyle->style()->getPropertyCSSValue(CSSPropertyFontSize);
939 if (cssValue->isPrimitiveValue()) {
940 value = String::number(legacyFontSizeFromCSSValue(m_frame->document(), static_cast<CSSPrimitiveValue*>(cssValue.get()),
941 selectionStyle->shouldUseFixedDefaultFontSize(), AlwaysUseLegacyFontSize));
942 }
943 }
944
945 return value;
946 }
947
indent()948 void Editor::indent()
949 {
950 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
951 }
952
outdent()953 void Editor::outdent()
954 {
955 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
956 }
957
dispatchEditableContentChangedEvents(const EditCommand & command)958 static void dispatchEditableContentChangedEvents(const EditCommand& command)
959 {
960 Element* startRoot = command.startingRootEditableElement();
961 Element* endRoot = command.endingRootEditableElement();
962 ExceptionCode ec;
963 if (startRoot)
964 startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
965 if (endRoot && endRoot != startRoot)
966 endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
967 }
968
appliedEditing(PassRefPtr<EditCommand> cmd)969 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
970 {
971 m_frame->document()->updateLayout();
972
973 dispatchEditableContentChangedEvents(*cmd);
974 VisibleSelection newSelection(cmd->endingSelection());
975
976 m_spellingCorrector->respondToAppliedEditing(cmd.get());
977
978 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
979 changeSelectionAfterCommand(newSelection, false, false);
980
981 if (!cmd->preservesTypingStyle())
982 m_frame->selection()->clearTypingStyle();
983
984 // Command will be equal to last edit command only in the case of typing
985 if (m_lastEditCommand.get() == cmd)
986 ASSERT(cmd->isTypingCommand());
987 else {
988 // Only register a new undo command if the command passed in is
989 // different from the last command
990 m_lastEditCommand = cmd;
991 if (client())
992 client()->registerCommandForUndo(m_lastEditCommand);
993 }
994 respondToChangedContents(newSelection);
995 }
996
unappliedEditing(PassRefPtr<EditCommand> cmd)997 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
998 {
999 m_frame->document()->updateLayout();
1000
1001 dispatchEditableContentChangedEvents(*cmd);
1002
1003 VisibleSelection newSelection(cmd->startingSelection());
1004 changeSelectionAfterCommand(newSelection, true, true);
1005 m_spellingCorrector->respondToUnappliedEditing(cmd.get());
1006
1007 m_lastEditCommand = 0;
1008 if (client())
1009 client()->registerCommandForRedo(cmd);
1010 respondToChangedContents(newSelection);
1011 }
1012
reappliedEditing(PassRefPtr<EditCommand> cmd)1013 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
1014 {
1015 m_frame->document()->updateLayout();
1016
1017 dispatchEditableContentChangedEvents(*cmd);
1018
1019 VisibleSelection newSelection(cmd->endingSelection());
1020 changeSelectionAfterCommand(newSelection, true, true);
1021
1022 m_lastEditCommand = 0;
1023 if (client())
1024 client()->registerCommandForUndo(cmd);
1025 respondToChangedContents(newSelection);
1026 }
1027
Editor(Frame * frame)1028 Editor::Editor(Frame* frame)
1029 : m_frame(frame)
1030 , m_deleteButtonController(adoptPtr(new DeleteButtonController(frame)))
1031 , m_ignoreCompositionSelectionChange(false)
1032 , m_shouldStartNewKillRingSequence(false)
1033 // This is off by default, since most editors want this behavior (this matches IE but not FF).
1034 , m_shouldStyleWithCSS(false)
1035 , m_killRing(adoptPtr(new KillRing))
1036 , m_spellChecker(adoptPtr(new SpellChecker(frame)))
1037 , m_spellingCorrector(adoptPtr(new SpellingCorrectionController(frame)))
1038 , m_areMarkedTextMatchesHighlighted(false)
1039 {
1040 }
1041
~Editor()1042 Editor::~Editor()
1043 {
1044 }
1045
clear()1046 void Editor::clear()
1047 {
1048 m_compositionNode = 0;
1049 m_customCompositionUnderlines.clear();
1050 m_shouldStyleWithCSS = false;
1051 }
1052
insertText(const String & text,Event * triggeringEvent)1053 bool Editor::insertText(const String& text, Event* triggeringEvent)
1054 {
1055 return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
1056 }
1057
insertTextForConfirmedComposition(const String & text)1058 bool Editor::insertTextForConfirmedComposition(const String& text)
1059 {
1060 return m_frame->eventHandler()->handleTextInputEvent(text, 0, TextEventInputComposition);
1061 }
1062
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,TextEvent * triggeringEvent)1063 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
1064 {
1065 if (text.isEmpty())
1066 return false;
1067
1068 VisibleSelection selection = selectionForCommand(triggeringEvent);
1069 if (!selection.isContentEditable())
1070 return false;
1071 RefPtr<Range> range = selection.toNormalizedRange();
1072
1073 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
1074 return true;
1075
1076 if (!text.isEmpty())
1077 updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
1078
1079 bool shouldConsiderApplyingAutocorrection = false;
1080 if (text == " " || text == "\t")
1081 shouldConsiderApplyingAutocorrection = true;
1082
1083 if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0]))
1084 shouldConsiderApplyingAutocorrection = true;
1085
1086 bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1087
1088 // Get the selection to use for the event that triggered this insertText.
1089 // If the event handler changed the selection, we may want to use a different selection
1090 // that is contained in the event target.
1091 selection = selectionForCommand(triggeringEvent);
1092 if (selection.isContentEditable()) {
1093 if (Node* selectionStart = selection.start().deprecatedNode()) {
1094 RefPtr<Document> document = selectionStart->document();
1095
1096 // Insert the text
1097 TypingCommand::Options options = 0;
1098 if (selectInsertedText)
1099 options |= TypingCommand::SelectInsertedText;
1100 if (autocorrectionWasApplied)
1101 options |= TypingCommand::RetainAutocorrectionIndicator;
1102 TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
1103
1104 // Reveal the current selection
1105 if (Frame* editedFrame = document->frame())
1106 if (Page* page = editedFrame->page())
1107 page->focusController()->focusedOrMainFrame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
1108 }
1109 }
1110
1111 return true;
1112 }
1113
insertLineBreak()1114 bool Editor::insertLineBreak()
1115 {
1116 if (!canEdit())
1117 return false;
1118
1119 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
1120 return true;
1121
1122 bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1123 TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1124 revealSelectionAfterEditingOperation();
1125
1126 return true;
1127 }
1128
insertParagraphSeparator()1129 bool Editor::insertParagraphSeparator()
1130 {
1131 if (!canEdit())
1132 return false;
1133
1134 if (!canEditRichly())
1135 return insertLineBreak();
1136
1137 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
1138 return true;
1139
1140 bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1141 TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1142 revealSelectionAfterEditingOperation();
1143
1144 return true;
1145 }
1146
cut()1147 void Editor::cut()
1148 {
1149 if (tryDHTMLCut())
1150 return; // DHTML did the whole operation
1151 if (!canCut()) {
1152 systemBeep();
1153 return;
1154 }
1155 RefPtr<Range> selection = selectedRange();
1156 if (shouldDeleteRange(selection.get())) {
1157 updateMarkersForWordsAffectedByEditing(true);
1158 if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode()))
1159 Pasteboard::generalPasteboard()->writePlainText(selectedText());
1160 else
1161 Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
1162 didWriteSelectionToPasteboard();
1163 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1164 }
1165 }
1166
copy()1167 void Editor::copy()
1168 {
1169 if (tryDHTMLCopy())
1170 return; // DHTML did the whole operation
1171 if (!canCopy()) {
1172 systemBeep();
1173 return;
1174 }
1175
1176 if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode()))
1177 Pasteboard::generalPasteboard()->writePlainText(selectedText());
1178 else {
1179 Document* document = m_frame->document();
1180 if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
1181 Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
1182 else
1183 Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
1184 }
1185
1186 didWriteSelectionToPasteboard();
1187 }
1188
paste()1189 void Editor::paste()
1190 {
1191 ASSERT(m_frame->document());
1192 if (tryDHTMLPaste())
1193 return; // DHTML did the whole operation
1194 if (!canPaste())
1195 return;
1196 updateMarkersForWordsAffectedByEditing(false);
1197 CachedResourceLoader* loader = m_frame->document()->cachedResourceLoader();
1198 loader->setAllowStaleResources(true);
1199 if (m_frame->selection()->isContentRichlyEditable())
1200 pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
1201 else
1202 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1203 loader->setAllowStaleResources(false);
1204 }
1205
pasteAsPlainText()1206 void Editor::pasteAsPlainText()
1207 {
1208 if (tryDHTMLPaste())
1209 return;
1210 if (!canPaste())
1211 return;
1212 updateMarkersForWordsAffectedByEditing(false);
1213 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1214 }
1215
performDelete()1216 void Editor::performDelete()
1217 {
1218 if (!canDelete()) {
1219 systemBeep();
1220 return;
1221 }
1222
1223 addToKillRing(selectedRange().get(), false);
1224 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1225
1226 // clear the "start new kill ring sequence" setting, because it was set to true
1227 // when the selection was updated by deleting the range
1228 setStartNewKillRingSequence(false);
1229 }
1230
copyURL(const KURL & url,const String & title)1231 void Editor::copyURL(const KURL& url, const String& title)
1232 {
1233 Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1234 }
1235
copyImage(const HitTestResult & result)1236 void Editor::copyImage(const HitTestResult& result)
1237 {
1238 KURL url = result.absoluteLinkURL();
1239 if (url.isEmpty())
1240 url = result.absoluteImageURL();
1241
1242 Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1243 }
1244
isContinuousSpellCheckingEnabled()1245 bool Editor::isContinuousSpellCheckingEnabled()
1246 {
1247 return client() && client()->isContinuousSpellCheckingEnabled();
1248 }
1249
toggleContinuousSpellChecking()1250 void Editor::toggleContinuousSpellChecking()
1251 {
1252 if (client())
1253 client()->toggleContinuousSpellChecking();
1254 }
1255
isGrammarCheckingEnabled()1256 bool Editor::isGrammarCheckingEnabled()
1257 {
1258 return client() && client()->isGrammarCheckingEnabled();
1259 }
1260
toggleGrammarChecking()1261 void Editor::toggleGrammarChecking()
1262 {
1263 if (client())
1264 client()->toggleGrammarChecking();
1265 }
1266
spellCheckerDocumentTag()1267 int Editor::spellCheckerDocumentTag()
1268 {
1269 return client() ? client()->spellCheckerDocumentTag() : 0;
1270 }
1271
1272 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
1273
uppercaseWord()1274 void Editor::uppercaseWord()
1275 {
1276 if (client())
1277 client()->uppercaseWord();
1278 }
1279
lowercaseWord()1280 void Editor::lowercaseWord()
1281 {
1282 if (client())
1283 client()->lowercaseWord();
1284 }
1285
capitalizeWord()1286 void Editor::capitalizeWord()
1287 {
1288 if (client())
1289 client()->capitalizeWord();
1290 }
1291
showSubstitutionsPanel()1292 void Editor::showSubstitutionsPanel()
1293 {
1294 if (!client()) {
1295 LOG_ERROR("No NSSpellChecker");
1296 return;
1297 }
1298
1299 if (client()->substitutionsPanelIsShowing()) {
1300 client()->showSubstitutionsPanel(false);
1301 return;
1302 }
1303 client()->showSubstitutionsPanel(true);
1304 }
1305
substitutionsPanelIsShowing()1306 bool Editor::substitutionsPanelIsShowing()
1307 {
1308 if (!client())
1309 return false;
1310 return client()->substitutionsPanelIsShowing();
1311 }
1312
toggleSmartInsertDelete()1313 void Editor::toggleSmartInsertDelete()
1314 {
1315 if (client())
1316 client()->toggleSmartInsertDelete();
1317 }
1318
isAutomaticQuoteSubstitutionEnabled()1319 bool Editor::isAutomaticQuoteSubstitutionEnabled()
1320 {
1321 return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1322 }
1323
toggleAutomaticQuoteSubstitution()1324 void Editor::toggleAutomaticQuoteSubstitution()
1325 {
1326 if (client())
1327 client()->toggleAutomaticQuoteSubstitution();
1328 }
1329
isAutomaticLinkDetectionEnabled()1330 bool Editor::isAutomaticLinkDetectionEnabled()
1331 {
1332 return client() && client()->isAutomaticLinkDetectionEnabled();
1333 }
1334
toggleAutomaticLinkDetection()1335 void Editor::toggleAutomaticLinkDetection()
1336 {
1337 if (client())
1338 client()->toggleAutomaticLinkDetection();
1339 }
1340
isAutomaticDashSubstitutionEnabled()1341 bool Editor::isAutomaticDashSubstitutionEnabled()
1342 {
1343 return client() && client()->isAutomaticDashSubstitutionEnabled();
1344 }
1345
toggleAutomaticDashSubstitution()1346 void Editor::toggleAutomaticDashSubstitution()
1347 {
1348 if (client())
1349 client()->toggleAutomaticDashSubstitution();
1350 }
1351
isAutomaticTextReplacementEnabled()1352 bool Editor::isAutomaticTextReplacementEnabled()
1353 {
1354 return client() && client()->isAutomaticTextReplacementEnabled();
1355 }
1356
toggleAutomaticTextReplacement()1357 void Editor::toggleAutomaticTextReplacement()
1358 {
1359 if (client())
1360 client()->toggleAutomaticTextReplacement();
1361 }
1362
isAutomaticSpellingCorrectionEnabled()1363 bool Editor::isAutomaticSpellingCorrectionEnabled()
1364 {
1365 return m_spellingCorrector->isAutomaticSpellingCorrectionEnabled();
1366 }
1367
toggleAutomaticSpellingCorrection()1368 void Editor::toggleAutomaticSpellingCorrection()
1369 {
1370 if (client())
1371 client()->toggleAutomaticSpellingCorrection();
1372 }
1373
1374 #endif
1375
shouldEndEditing(Range * range)1376 bool Editor::shouldEndEditing(Range* range)
1377 {
1378 return client() && client()->shouldEndEditing(range);
1379 }
1380
shouldBeginEditing(Range * range)1381 bool Editor::shouldBeginEditing(Range* range)
1382 {
1383 return client() && client()->shouldBeginEditing(range);
1384 }
1385
clearUndoRedoOperations()1386 void Editor::clearUndoRedoOperations()
1387 {
1388 if (client())
1389 client()->clearUndoRedoOperations();
1390 }
1391
canUndo()1392 bool Editor::canUndo()
1393 {
1394 return client() && client()->canUndo();
1395 }
1396
undo()1397 void Editor::undo()
1398 {
1399 if (client())
1400 client()->undo();
1401 }
1402
canRedo()1403 bool Editor::canRedo()
1404 {
1405 return client() && client()->canRedo();
1406 }
1407
redo()1408 void Editor::redo()
1409 {
1410 if (client())
1411 client()->redo();
1412 }
1413
didBeginEditing()1414 void Editor::didBeginEditing()
1415 {
1416 if (client())
1417 client()->didBeginEditing();
1418 }
1419
didEndEditing()1420 void Editor::didEndEditing()
1421 {
1422 if (client())
1423 client()->didEndEditing();
1424 }
1425
didWriteSelectionToPasteboard()1426 void Editor::didWriteSelectionToPasteboard()
1427 {
1428 if (client())
1429 client()->didWriteSelectionToPasteboard();
1430 }
1431
toggleBold()1432 void Editor::toggleBold()
1433 {
1434 command("ToggleBold").execute();
1435 }
1436
toggleUnderline()1437 void Editor::toggleUnderline()
1438 {
1439 command("ToggleUnderline").execute();
1440 }
1441
setBaseWritingDirection(WritingDirection direction)1442 void Editor::setBaseWritingDirection(WritingDirection direction)
1443 {
1444 Node* focusedNode = frame()->document()->focusedNode();
1445 if (focusedNode && (focusedNode->hasTagName(textareaTag) || (focusedNode->hasTagName(inputTag) && static_cast<HTMLInputElement*>(focusedNode)->isTextField()))) {
1446 if (direction == NaturalWritingDirection)
1447 return;
1448 toHTMLElement(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1449 frame()->document()->updateStyleIfNeeded();
1450 return;
1451 }
1452
1453 RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
1454 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1455 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1456 }
1457
selectComposition()1458 void Editor::selectComposition()
1459 {
1460 RefPtr<Range> range = compositionRange();
1461 if (!range)
1462 return;
1463
1464 // The composition can start inside a composed character sequence, so we have to override checks.
1465 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1466 VisibleSelection selection;
1467 selection.setWithoutValidation(range->startPosition(), range->endPosition());
1468 m_frame->selection()->setSelection(selection, 0);
1469 }
1470
confirmComposition()1471 void Editor::confirmComposition()
1472 {
1473 if (!m_compositionNode)
1474 return;
1475 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1476 }
1477
confirmCompositionWithoutDisturbingSelection()1478 void Editor::confirmCompositionWithoutDisturbingSelection()
1479 {
1480 if (!m_compositionNode)
1481 return;
1482 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1483 }
1484
confirmComposition(const String & text)1485 void Editor::confirmComposition(const String& text)
1486 {
1487 confirmComposition(text, false);
1488 }
1489
confirmComposition(const String & text,bool preserveSelection)1490 void Editor::confirmComposition(const String& text, bool preserveSelection)
1491 {
1492 UserTypingGestureIndicator typingGestureIndicator(m_frame);
1493
1494 setIgnoreCompositionSelectionChange(true);
1495
1496 VisibleSelection oldSelection = m_frame->selection()->selection();
1497
1498 selectComposition();
1499
1500 if (m_frame->selection()->isNone()) {
1501 setIgnoreCompositionSelectionChange(false);
1502 return;
1503 }
1504
1505 // Dispatch a compositionend event to the focused node.
1506 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
1507 // the DOM Event specification.
1508 Node* target = m_frame->document()->focusedNode();
1509 if (target) {
1510 RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1511 ExceptionCode ec = 0;
1512 target->dispatchEvent(event, ec);
1513 }
1514
1515 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1516 // will delete the old composition with an optimized replace operation.
1517 if (text.isEmpty())
1518 TypingCommand::deleteSelection(m_frame->document(), 0);
1519
1520 m_compositionNode = 0;
1521 m_customCompositionUnderlines.clear();
1522
1523 insertTextForConfirmedComposition(text);
1524
1525 if (preserveSelection) {
1526 m_frame->selection()->setSelection(oldSelection, 0);
1527 // An open typing command that disagrees about current selection would cause issues with typing later on.
1528 TypingCommand::closeTyping(m_lastEditCommand.get());
1529 }
1530
1531 setIgnoreCompositionSelectionChange(false);
1532 }
1533
setComposition(const String & text,const Vector<CompositionUnderline> & underlines,unsigned selectionStart,unsigned selectionEnd)1534 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1535 {
1536 UserTypingGestureIndicator typingGestureIndicator(m_frame);
1537
1538 setIgnoreCompositionSelectionChange(true);
1539
1540 // Updates styles before setting selection for composition to prevent
1541 // inserting the previous composition text into text nodes oddly.
1542 // See https://bugs.webkit.org/show_bug.cgi?id=46868
1543 m_frame->document()->updateStyleIfNeeded();
1544
1545 selectComposition();
1546
1547 if (m_frame->selection()->isNone()) {
1548 setIgnoreCompositionSelectionChange(false);
1549 return;
1550 }
1551
1552 Node* target = m_frame->document()->focusedNode();
1553 if (target) {
1554 // Dispatch an appropriate composition event to the focused node.
1555 // We check the composition status and choose an appropriate composition event since this
1556 // function is used for three purposes:
1557 // 1. Starting a new composition.
1558 // Send a compositionstart event when this function creates a new composition node, i.e.
1559 // m_compositionNode == 0 && !text.isEmpty().
1560 // 2. Updating the existing composition node.
1561 // Send a compositionupdate event when this function updates the existing composition
1562 // node, i.e. m_compositionNode != 0 && !text.isEmpty().
1563 // 3. Canceling the ongoing composition.
1564 // Send a compositionend event when function deletes the existing composition node, i.e.
1565 // m_compositionNode != 0 && test.isEmpty().
1566 RefPtr<CompositionEvent> event;
1567 if (!m_compositionNode) {
1568 // We should send a compositionstart event only when the given text is not empty because this
1569 // function doesn't create a composition node when the text is empty.
1570 if (!text.isEmpty())
1571 event = CompositionEvent::create(eventNames().compositionstartEvent, m_frame->domWindow(), text);
1572 } else {
1573 if (!text.isEmpty())
1574 event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text);
1575 else
1576 event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1577 }
1578 ExceptionCode ec = 0;
1579 if (event.get())
1580 target->dispatchEvent(event, ec);
1581 }
1582
1583 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1584 // will delete the old composition with an optimized replace operation.
1585 if (text.isEmpty())
1586 TypingCommand::deleteSelection(m_frame->document(), TypingCommand::PreventSpellChecking);
1587
1588 m_compositionNode = 0;
1589 m_customCompositionUnderlines.clear();
1590
1591 if (!text.isEmpty()) {
1592 TypingCommand::insertText(m_frame->document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
1593
1594 // Find out what node has the composition now.
1595 Position base = m_frame->selection()->base().downstream();
1596 Position extent = m_frame->selection()->extent();
1597 Node* baseNode = base.deprecatedNode();
1598 unsigned baseOffset = base.deprecatedEditingOffset();
1599 Node* extentNode = extent.deprecatedNode();
1600 unsigned extentOffset = extent.deprecatedEditingOffset();
1601
1602 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1603 m_compositionNode = static_cast<Text*>(baseNode);
1604 m_compositionStart = baseOffset;
1605 m_compositionEnd = extentOffset;
1606 m_customCompositionUnderlines = underlines;
1607 size_t numUnderlines = m_customCompositionUnderlines.size();
1608 for (size_t i = 0; i < numUnderlines; ++i) {
1609 m_customCompositionUnderlines[i].startOffset += baseOffset;
1610 m_customCompositionUnderlines[i].endOffset += baseOffset;
1611 }
1612 if (baseNode->renderer())
1613 baseNode->renderer()->repaint();
1614
1615 unsigned start = min(baseOffset + selectionStart, extentOffset);
1616 unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1617 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1618 m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1619 }
1620 }
1621
1622 setIgnoreCompositionSelectionChange(false);
1623 }
1624
ignoreSpelling()1625 void Editor::ignoreSpelling()
1626 {
1627 if (!client())
1628 return;
1629
1630 RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1631 if (selectedRange)
1632 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1633
1634 String text = selectedText();
1635 ASSERT(text.length());
1636 textChecker()->ignoreWordInSpellDocument(text);
1637 }
1638
learnSpelling()1639 void Editor::learnSpelling()
1640 {
1641 if (!client())
1642 return;
1643
1644 // FIXME: On Mac OS X, when use "learn" button on "Spelling and Grammar" panel, we don't call this function. It should remove misspelling markers around the learned word, see <rdar://problem/5396072>.
1645
1646 RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1647 if (selectedRange)
1648 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1649
1650 String text = selectedText();
1651 ASSERT(text.length());
1652 textChecker()->learnWord(text);
1653 }
1654
advanceToNextMisspelling(bool startBeforeSelection)1655 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1656 {
1657 ExceptionCode ec = 0;
1658
1659 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1660 // then we wrap and search from the doc start to (approximately) where we started.
1661
1662 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
1663 // repeated "check spelling" commands work.
1664 VisibleSelection selection(frame()->selection()->selection());
1665 RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1666
1667 bool startedWithSelection = false;
1668 if (selection.start().deprecatedNode()) {
1669 startedWithSelection = true;
1670 if (startBeforeSelection) {
1671 VisiblePosition start(selection.visibleStart());
1672 // We match AppKit's rule: Start 1 character before the selection.
1673 VisiblePosition oneBeforeStart = start.previous();
1674 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1675 } else
1676 setStart(spellingSearchRange.get(), selection.visibleEnd());
1677 }
1678
1679 Position position = spellingSearchRange->startPosition();
1680 if (!isEditablePosition(position)) {
1681 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1682 // selection is editable.
1683 // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1684 // when spell checking the whole document before sending the message.
1685 // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1686
1687 position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1688 if (position.isNull())
1689 return;
1690
1691 Position rangeCompliantPosition = position.parentAnchoredEquivalent();
1692 spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), ec);
1693 startedWithSelection = false; // won't need to wrap
1694 }
1695
1696 // topNode defines the whole range we want to operate on
1697 Node* topNode = highestEditableRoot(position);
1698 // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
1699 spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec);
1700
1701 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1702 // at a word boundary. Going back by one char and then forward by a word does the trick.
1703 if (startedWithSelection) {
1704 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1705 if (oneBeforeStart.isNotNull())
1706 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1707 // else we were already at the start of the editable node
1708 }
1709
1710 if (spellingSearchRange->collapsed(ec))
1711 return; // nothing to search in
1712
1713 // Get the spell checker if it is available
1714 if (!client())
1715 return;
1716
1717 // We go to the end of our first range instead of the start of it, just to be sure
1718 // we don't get foiled by any word boundary problems at the start. It means we might
1719 // do a tiny bit more searching.
1720 Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1721 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1722
1723 int misspellingOffset = 0;
1724 GrammarDetail grammarDetail;
1725 int grammarPhraseOffset = 0;
1726 RefPtr<Range> grammarSearchRange;
1727 String badGrammarPhrase;
1728 String misspelledWord;
1729
1730 #if USE(UNIFIED_TEXT_CHECKING)
1731 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1732 bool isSpelling = true;
1733 int foundOffset = 0;
1734 String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1735 if (isSpelling) {
1736 misspelledWord = foundItem;
1737 misspellingOffset = foundOffset;
1738 } else {
1739 badGrammarPhrase = foundItem;
1740 grammarPhraseOffset = foundOffset;
1741 }
1742 #else
1743 RefPtr<Range> firstMisspellingRange;
1744 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
1745
1746 #if USE(GRAMMAR_CHECKING)
1747 // Search for bad grammar that occurs prior to the next misspelled word (if any)
1748 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1749 if (!misspelledWord.isEmpty()) {
1750 // Stop looking at start of next misspelled word
1751 CharacterIterator chars(grammarSearchRange.get());
1752 chars.advance(misspellingOffset);
1753 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1754 }
1755
1756 if (isGrammarCheckingEnabled())
1757 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
1758 #endif
1759 #endif
1760
1761 // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
1762 // block rather than at a selection).
1763 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1764 spellingSearchRange->setStart(topNode, 0, ec);
1765 // going until the end of the very first chunk we tested is far enough
1766 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1767
1768 #if USE(UNIFIED_TEXT_CHECKING)
1769 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1770 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1771 if (isSpelling) {
1772 misspelledWord = foundItem;
1773 misspellingOffset = foundOffset;
1774 } else {
1775 badGrammarPhrase = foundItem;
1776 grammarPhraseOffset = foundOffset;
1777 }
1778 #else
1779 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
1780
1781 #if USE(GRAMMAR_CHECKING)
1782 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1783 if (!misspelledWord.isEmpty()) {
1784 // Stop looking at start of next misspelled word
1785 CharacterIterator chars(grammarSearchRange.get());
1786 chars.advance(misspellingOffset);
1787 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1788 }
1789
1790 if (isGrammarCheckingEnabled())
1791 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
1792 #endif
1793 #endif
1794 }
1795
1796 if (!badGrammarPhrase.isEmpty()) {
1797 ASSERT(WTF_USE_GRAMMAR_CHECKING);
1798 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1799 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1800 // panel, and store a marker so we draw the green squiggle later.
1801
1802 ASSERT(badGrammarPhrase.length() > 0);
1803 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1804
1805 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1806 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1807 frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1808 frame()->selection()->revealSelection();
1809
1810 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1811 frame()->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1812 } else if (!misspelledWord.isEmpty()) {
1813 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1814 // a marker so we draw the red squiggle later.
1815
1816 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1817 frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
1818 frame()->selection()->revealSelection();
1819
1820 client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1821 frame()->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1822 }
1823 }
1824
isSelectionMisspelled()1825 bool Editor::isSelectionMisspelled()
1826 {
1827 String selectedString = selectedText();
1828 int length = selectedString.length();
1829 if (!length)
1830 return false;
1831
1832 if (!client())
1833 return false;
1834
1835 int misspellingLocation = -1;
1836 int misspellingLength = 0;
1837 textChecker()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
1838
1839 // The selection only counts as misspelled if the selected text is exactly one misspelled word
1840 if (misspellingLength != length)
1841 return false;
1842
1843 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1844 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1845 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1846 // or a grammar error.
1847 client()->updateSpellingUIWithMisspelledWord(selectedString);
1848
1849 return true;
1850 }
1851
isSelectionUngrammatical()1852 bool Editor::isSelectionUngrammatical()
1853 {
1854 #if USE(GRAMMAR_CHECKING)
1855 Vector<String> ignoredGuesses;
1856 return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(ignoredGuesses);
1857 #else
1858 return false;
1859 #endif
1860 }
1861
guessesForUngrammaticalSelection()1862 Vector<String> Editor::guessesForUngrammaticalSelection()
1863 {
1864 #if USE(GRAMMAR_CHECKING)
1865 Vector<String> guesses;
1866 // Ignore the result of isUngrammatical; we just want the guesses, whether or not there are any
1867 TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(guesses);
1868 return guesses;
1869 #else
1870 return Vector<String>();
1871 #endif
1872 }
1873
guessesForMisspelledSelection()1874 Vector<String> Editor::guessesForMisspelledSelection()
1875 {
1876 String selectedString = selectedText();
1877 ASSERT(selectedString.length());
1878
1879 Vector<String> guesses;
1880 if (client())
1881 textChecker()->getGuessesForWord(selectedString, String(), guesses);
1882 return guesses;
1883 }
1884
guessesForMisspelledOrUngrammaticalSelection(bool & misspelled,bool & ungrammatical)1885 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
1886 {
1887 #if USE(UNIFIED_TEXT_CHECKING)
1888 return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical);
1889 #else
1890 misspelled = isSelectionMisspelled();
1891 if (misspelled) {
1892 ungrammatical = false;
1893 return guessesForMisspelledSelection();
1894 }
1895 if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) {
1896 ungrammatical = true;
1897 return guessesForUngrammaticalSelection();
1898 }
1899 ungrammatical = false;
1900 return Vector<String>();
1901 #endif
1902 }
1903
showSpellingGuessPanel()1904 void Editor::showSpellingGuessPanel()
1905 {
1906 if (!client()) {
1907 LOG_ERROR("No NSSpellChecker");
1908 return;
1909 }
1910
1911 if (client()->spellingUIIsShowing()) {
1912 client()->showSpellingUI(false);
1913 return;
1914 }
1915
1916 advanceToNextMisspelling(true);
1917 client()->showSpellingUI(true);
1918 }
1919
spellingPanelIsShowing()1920 bool Editor::spellingPanelIsShowing()
1921 {
1922 if (!client())
1923 return false;
1924 return client()->spellingUIIsShowing();
1925 }
1926
clearMisspellingsAndBadGrammar(const VisibleSelection & movingSelection)1927 void Editor::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
1928 {
1929 RefPtr<Range> selectedRange = movingSelection.toNormalizedRange();
1930 if (selectedRange) {
1931 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1932 frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Grammar);
1933 }
1934 }
1935
markMisspellingsAndBadGrammar(const VisibleSelection & movingSelection)1936 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
1937 {
1938 bool markSpelling = isContinuousSpellCheckingEnabled();
1939 bool markGrammar = markSpelling && isGrammarCheckingEnabled();
1940
1941 if (markSpelling) {
1942 RefPtr<Range> unusedFirstMisspellingRange;
1943 markMisspellings(movingSelection, unusedFirstMisspellingRange);
1944 }
1945
1946 if (markGrammar)
1947 markBadGrammar(movingSelection);
1948 }
1949
markMisspellingsAfterTypingToWord(const VisiblePosition & wordStart,const VisibleSelection & selectionAfterTyping,bool doReplacement)1950 void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement)
1951 {
1952 #if USE(UNIFIED_TEXT_CHECKING)
1953 m_spellingCorrector->applyPendingCorrection(selectionAfterTyping);
1954
1955 TextCheckingOptions textCheckingOptions = 0;
1956 if (isContinuousSpellCheckingEnabled())
1957 textCheckingOptions |= MarkSpelling;
1958
1959 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
1960 if (doReplacement
1961 && (isAutomaticQuoteSubstitutionEnabled()
1962 || isAutomaticLinkDetectionEnabled()
1963 || isAutomaticDashSubstitutionEnabled()
1964 || isAutomaticTextReplacementEnabled()
1965 || ((textCheckingOptions & MarkSpelling) && isAutomaticSpellingCorrectionEnabled())))
1966 textCheckingOptions |= PerformReplacement;
1967 #endif
1968 if (!textCheckingOptions & (MarkSpelling | PerformReplacement))
1969 return;
1970
1971 if (isGrammarCheckingEnabled())
1972 textCheckingOptions |= MarkGrammar;
1973
1974 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
1975 if (textCheckingOptions & MarkGrammar) {
1976 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
1977 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
1978 } else {
1979 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
1980 }
1981
1982 #else
1983 UNUSED_PARAM(selectionAfterTyping);
1984 UNUSED_PARAM(doReplacement);
1985
1986 if (!isContinuousSpellCheckingEnabled())
1987 return;
1988
1989 // Check spelling of one word
1990 RefPtr<Range> misspellingRange;
1991 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
1992
1993 // Autocorrect the misspelled word.
1994 if (!misspellingRange)
1995 return;
1996
1997 // Get the misspelled word.
1998 const String misspelledWord = plainText(misspellingRange.get());
1999 String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
2000
2001 // If autocorrected word is non empty, replace the misspelled word by this word.
2002 if (!autocorrectedString.isEmpty()) {
2003 VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
2004 if (newSelection != frame()->selection()->selection()) {
2005 if (!frame()->selection()->shouldChangeSelection(newSelection))
2006 return;
2007 frame()->selection()->setSelection(newSelection);
2008 }
2009
2010 if (!frame()->editor()->shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
2011 return;
2012 frame()->editor()->replaceSelectionWithText(autocorrectedString, false, false);
2013
2014 // Reset the charet one character further.
2015 frame()->selection()->moveTo(frame()->selection()->end());
2016 frame()->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2017 }
2018
2019 if (!isGrammarCheckingEnabled())
2020 return;
2021
2022 // Check grammar of entire sentence
2023 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
2024 #endif
2025 }
2026
markMisspellingsOrBadGrammar(const VisibleSelection & selection,bool checkSpelling,RefPtr<Range> & firstMisspellingRange)2027 void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
2028 {
2029 // This function is called with a selection already expanded to word boundaries.
2030 // Might be nice to assert that here.
2031
2032 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
2033 // grammar checking can only be on if spell checking is also on.
2034 if (!isContinuousSpellCheckingEnabled())
2035 return;
2036
2037 RefPtr<Range> searchRange(selection.toNormalizedRange());
2038 if (!searchRange)
2039 return;
2040
2041 // If we're not in an editable node, bail.
2042 Node* editableNode = searchRange->startContainer();
2043 if (!editableNode || !editableNode->rendererIsEditable())
2044 return;
2045
2046 if (!isSpellCheckingEnabledFor(editableNode))
2047 return;
2048
2049 // Get the spell checker if it is available
2050 if (!client())
2051 return;
2052
2053 TextCheckingHelper checker(client(), searchRange);
2054 if (checkSpelling)
2055 checker.markAllMisspellings(firstMisspellingRange);
2056 else {
2057 ASSERT(WTF_USE_GRAMMAR_CHECKING);
2058 if (isGrammarCheckingEnabled())
2059 checker.markAllBadGrammar();
2060 }
2061 }
2062
isSpellCheckingEnabledFor(Node * node) const2063 bool Editor::isSpellCheckingEnabledFor(Node* node) const
2064 {
2065 if (!node)
2066 return false;
2067 const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
2068 if (!focusedElement)
2069 return false;
2070 return focusedElement->isSpellCheckingEnabled();
2071 }
2072
isSpellCheckingEnabledInFocusedNode() const2073 bool Editor::isSpellCheckingEnabledInFocusedNode() const
2074 {
2075 return isSpellCheckingEnabledFor(m_frame->selection()->start().deprecatedNode());
2076 }
2077
markMisspellings(const VisibleSelection & selection,RefPtr<Range> & firstMisspellingRange)2078 void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2079 {
2080 markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
2081 }
2082
markBadGrammar(const VisibleSelection & selection)2083 void Editor::markBadGrammar(const VisibleSelection& selection)
2084 {
2085 ASSERT(WTF_USE_GRAMMAR_CHECKING);
2086 RefPtr<Range> firstMisspellingRange;
2087 markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
2088 }
2089
markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCheckingOptions,Range * spellingRange,Range * grammarRange)2090 void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCheckingOptions, Range* spellingRange, Range* grammarRange)
2091 {
2092 #if USE(UNIFIED_TEXT_CHECKING)
2093 // There shouldn't be pending autocorrection at this moment.
2094 ASSERT(!m_spellingCorrector->hasPendingCorrection());
2095
2096 bool shouldMarkSpelling = textCheckingOptions & MarkSpelling;
2097 bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
2098 bool shouldPerformReplacement = textCheckingOptions & PerformReplacement;
2099 bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
2100 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
2101
2102 // This function is called with selections already expanded to word boundaries.
2103 ExceptionCode ec = 0;
2104 if (!client() || !spellingRange || (shouldMarkGrammar && !grammarRange))
2105 return;
2106
2107 // If we're not in an editable node, bail.
2108 Node* editableNode = spellingRange->startContainer();
2109 if (!editableNode || !editableNode->rendererIsEditable())
2110 return;
2111
2112 if (!isSpellCheckingEnabledFor(editableNode))
2113 return;
2114
2115 // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2116 int selectionOffset = 0;
2117 int ambiguousBoundaryOffset = -1;
2118 bool selectionChanged = false;
2119 bool restoreSelectionAfterChange = false;
2120 bool adjustSelectionForParagraphBoundaries = false;
2121
2122 TextCheckingParagraph spellingParagraph(spellingRange);
2123 TextCheckingParagraph grammarParagraph(shouldMarkGrammar ? grammarRange : 0);
2124
2125 if (shouldMarkGrammar ? (spellingParagraph.isRangeEmpty() && grammarParagraph.isEmpty()) : spellingParagraph.isEmpty())
2126 return;
2127
2128 if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) {
2129 if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) {
2130 // Attempt to save the caret position so we can restore it later if needed
2131 Position caretPosition = m_frame->selection()->end();
2132 int offset = spellingParagraph.offsetTo(caretPosition, ec);
2133 if (!ec) {
2134 selectionOffset = offset;
2135 restoreSelectionAfterChange = true;
2136 if (selectionOffset > 0 && (selectionOffset > spellingParagraph.textLength() || spellingParagraph.textCharAt(selectionOffset - 1) == newlineCharacter))
2137 adjustSelectionForParagraphBoundaries = true;
2138 if (selectionOffset > 0 && selectionOffset <= spellingParagraph.textLength() && isAmbiguousBoundaryCharacter(spellingParagraph.textCharAt(selectionOffset - 1)))
2139 ambiguousBoundaryOffset = selectionOffset - 1;
2140 }
2141 }
2142 }
2143
2144 Vector<TextCheckingResult> results;
2145 if (shouldMarkGrammar)
2146 textChecker()->checkTextOfParagraph(grammarParagraph.textCharacters(), grammarParagraph.textLength(),
2147 textCheckingTypeMaskFor(textCheckingOptions), results);
2148 else
2149 textChecker()->checkTextOfParagraph(spellingParagraph.textCharacters(), spellingParagraph.textLength(),
2150 textCheckingTypeMaskFor(textCheckingOptions), results);
2151
2152
2153 // If this checking is only for showing correction panel, we shouldn't bother to mark misspellings.
2154 if (shouldShowCorrectionPanel)
2155 shouldMarkSpelling = false;
2156
2157 int offsetDueToReplacement = 0;
2158
2159 for (unsigned i = 0; i < results.size(); i++) {
2160 int spellingRangeEndOffset = spellingParagraph.checkingEnd() + offsetDueToReplacement;
2161 const TextCheckingResult* result = &results[i];
2162 int resultLocation = result->location + offsetDueToReplacement;
2163 int resultLength = result->length;
2164 bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset;
2165
2166 // Only mark misspelling if:
2167 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
2168 // 2. Result falls within spellingRange.
2169 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
2170 // "wouldn'" as misspelled right after apostrophe is typed.
2171 if (shouldMarkSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingParagraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
2172 ASSERT(resultLength > 0 && resultLocation >= 0);
2173 RefPtr<Range> misspellingRange = spellingParagraph.subrange(resultLocation, resultLength);
2174 if (!m_spellingCorrector->isSpellingMarkerAllowed(misspellingRange))
2175 continue;
2176 misspellingRange->startContainer(ec)->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2177 } else if (shouldMarkGrammar && result->type == TextCheckingTypeGrammar && grammarParagraph.checkingRangeCovers(resultLocation, resultLength)) {
2178 ASSERT(resultLength > 0 && resultLocation >= 0);
2179 for (unsigned j = 0; j < result->details.size(); j++) {
2180 const GrammarDetail* detail = &result->details[j];
2181 ASSERT(detail->length > 0 && detail->location >= 0);
2182 if (grammarParagraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) {
2183 RefPtr<Range> badGrammarRange = grammarParagraph.subrange(resultLocation + detail->location, detail->length);
2184 grammarRange->startContainer(ec)->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
2185 }
2186 }
2187 } else if (resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingParagraph.checkingStart()
2188 && (result->type == TextCheckingTypeLink
2189 || result->type == TextCheckingTypeQuote
2190 || result->type == TextCheckingTypeDash
2191 || result->type == TextCheckingTypeReplacement
2192 || result->type == TextCheckingTypeCorrection)) {
2193 // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
2194 ASSERT(resultLength > 0 && resultLocation >= 0);
2195
2196 if (shouldShowCorrectionPanel && (resultLocation + resultLength < spellingRangeEndOffset || result->type != TextCheckingTypeCorrection))
2197 continue;
2198
2199 int replacementLength = result->replacement.length();
2200
2201 // Apply replacement if:
2202 // 1. The replacement length is non-zero.
2203 // 2. The result doesn't end at an ambiguous boundary.
2204 // (FIXME: this is required until 6853027 is fixed and text checking can do this for us
2205 bool doReplacement = replacementLength > 0 && !resultEndsAtAmbiguousBoundary;
2206 RefPtr<Range> rangeToReplace = spellingParagraph.subrange(resultLocation, resultLength);
2207 VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM);
2208
2209 // adding links should be done only immediately after they are typed
2210 if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1)
2211 continue;
2212
2213 String replacedString = plainText(rangeToReplace.get());
2214
2215 // Don't correct spelling in an already-corrected word.
2216 DocumentMarkerController* markers = m_frame->document()->markers();
2217 if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::Replacement)) {
2218 doReplacement = false;
2219 if (result->type == TextCheckingTypeCorrection)
2220 m_spellingCorrector->recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement);
2221 } else if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::RejectedCorrection))
2222 doReplacement = false;
2223
2224 if (!(shouldPerformReplacement || shouldShowCorrectionPanel) || !doReplacement)
2225 continue;
2226
2227 if (shouldShowCorrectionPanel) {
2228 ASSERT(SUPPORT_AUTOCORRECTION_PANEL);
2229 // shouldShowCorrectionPanel can be true only when the panel is available.
2230 if (resultLocation + resultLength == spellingRangeEndOffset) {
2231 // We only show the correction panel on the last word.
2232 m_spellingCorrector->show(rangeToReplace, result->replacement);
2233 break;
2234 }
2235 // If this function is called for showing correction panel, we ignore other correction or replacement.
2236 continue;
2237 }
2238
2239 if (selectionToReplace != m_frame->selection()->selection()) {
2240 if (!m_frame->selection()->shouldChangeSelection(selectionToReplace))
2241 continue;
2242 }
2243
2244 if (result->type == TextCheckingTypeLink) {
2245 m_frame->selection()->setSelection(selectionToReplace);
2246 selectionChanged = true;
2247 restoreSelectionAfterChange = false;
2248 if (canEditRichly())
2249 applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
2250 } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
2251 applyCommand(SpellingCorrectionCommand::create(rangeToReplace, result->replacement));
2252
2253 if (AXObjectCache::accessibilityEnabled()) {
2254 if (Element* root = m_frame->selection()->selection().rootEditableElement())
2255 m_frame->document()->axObjectCache()->postNotification(root->renderer(), AXObjectCache::AXAutocorrectionOccured, true);
2256 }
2257
2258 selectionChanged = true;
2259 offsetDueToReplacement += replacementLength - resultLength;
2260 if (resultLocation < selectionOffset) {
2261 selectionOffset += replacementLength - resultLength;
2262 if (ambiguousBoundaryOffset >= 0)
2263 ambiguousBoundaryOffset = selectionOffset - 1;
2264 }
2265
2266 // Add a marker so that corrections can easily be undone and won't be re-corrected.
2267 if (result->type == TextCheckingTypeCorrection)
2268 m_spellingCorrector->markCorrection(spellingParagraph.subrange(resultLocation, replacementLength), replacedString);
2269 }
2270 }
2271 }
2272
2273 if (selectionChanged) {
2274 // Restore the caret position if we have made any replacements
2275 spellingParagraph.expandRangeToNextEnd();
2276 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= spellingParagraph.rangeLength()) {
2277 RefPtr<Range> selectionRange = spellingParagraph.subrange(0, selectionOffset);
2278 m_frame->selection()->moveTo(selectionRange->endPosition(), DOWNSTREAM);
2279 if (adjustSelectionForParagraphBoundaries)
2280 m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2281 } else {
2282 // If this fails for any reason, the fallback is to go one position beyond the last replacement
2283 m_frame->selection()->moveTo(m_frame->selection()->end());
2284 m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2285 }
2286 }
2287 #else
2288 ASSERT_NOT_REACHED();
2289 UNUSED_PARAM(textCheckingOptions);
2290 UNUSED_PARAM(spellingRange);
2291 UNUSED_PARAM(grammarRange);
2292 #endif // USE(UNIFIED_TEXT_CHECKING)
2293 }
2294
changeBackToReplacedString(const String & replacedString)2295 void Editor::changeBackToReplacedString(const String& replacedString)
2296 {
2297 #if USE(UNIFIED_TEXT_CHECKING)
2298 if (replacedString.isEmpty())
2299 return;
2300
2301 RefPtr<Range> selection = selectedRange();
2302 if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
2303 return;
2304
2305 m_spellingCorrector->recordAutocorrectionResponseReversed(replacedString, selection);
2306 TextCheckingParagraph paragraph(selection);
2307 replaceSelectionWithText(replacedString, false, false);
2308 RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
2309 changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
2310 m_spellingCorrector->markReversed(changedRange.get());
2311 #else
2312 ASSERT_NOT_REACHED();
2313 UNUSED_PARAM(replacedString);
2314 #endif // USE(UNIFIED_TEXT_CHECKING)
2315 }
2316
2317
markMisspellingsAndBadGrammar(const VisibleSelection & spellingSelection,bool markGrammar,const VisibleSelection & grammarSelection)2318 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
2319 {
2320 #if USE(UNIFIED_TEXT_CHECKING)
2321 if (!isContinuousSpellCheckingEnabled())
2322 return;
2323 TextCheckingOptions textCheckingOptions = MarkSpelling | CheckForCorrection;
2324 if (markGrammar && isGrammarCheckingEnabled())
2325 textCheckingOptions |= MarkGrammar;
2326 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
2327 #else
2328 RefPtr<Range> firstMisspellingRange;
2329 markMisspellings(spellingSelection, firstMisspellingRange);
2330 if (markGrammar)
2331 markBadGrammar(grammarSelection);
2332 #endif
2333 }
2334
unappliedSpellCorrection(const VisibleSelection & selectionOfCorrected,const String & corrected,const String & correction)2335 void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
2336 {
2337 m_spellingCorrector->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
2338 }
2339
updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)2340 void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
2341 {
2342 if (!m_spellingCorrector->shouldRemoveMarkersUponEditing())
2343 return;
2344
2345 // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
2346 // several scenarios:
2347 // 1. Insert in the middle of a word.
2348 // 2. Appending non whitespace at the beginning of word.
2349 // 3. Appending non whitespace at the end of word.
2350 // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
2351 // remove the markers on that word.
2352 // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
2353 // selection, and remove words between the selection boundaries.
2354 //
2355 VisiblePosition startOfSelection = frame()->selection()->selection().start();
2356 VisiblePosition endOfSelection = frame()->selection()->selection().end();
2357 if (startOfSelection.isNull())
2358 return;
2359 // First word is the word that ends after or on the start of selection.
2360 VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
2361 VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
2362 // Last word is the word that begins before or on the end of selection
2363 VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
2364 VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
2365
2366 if (startOfFirstWord.isNull()) {
2367 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
2368 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
2369 }
2370
2371 if (endOfLastWord.isNull()) {
2372 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
2373 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
2374 }
2375
2376 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
2377 // we choose next word as the first word.
2378 if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) {
2379 startOfFirstWord = nextWordPosition(startOfFirstWord);
2380 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
2381 if (startOfFirstWord == endOfSelection)
2382 return;
2383 }
2384
2385 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
2386 // we choose previous word as the last word.
2387 if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) {
2388 startOfLastWord = previousWordPosition(startOfLastWord);
2389 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
2390 if (endOfLastWord == startOfSelection)
2391 return;
2392 }
2393
2394 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
2395 return;
2396
2397 // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
2398 // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
2399 // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
2400 // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
2401 // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
2402 // of marker that contains the word in question, and remove marker on that whole range.
2403 Document* document = m_frame->document();
2404 RefPtr<Range> wordRange = Range::create(document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
2405
2406 document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption, DocumentMarkerController::RemovePartiallyOverlappingMarker);
2407 document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement);
2408 }
2409
rangeForPoint(const IntPoint & windowPoint)2410 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
2411 {
2412 Document* document = m_frame->documentAtPoint(windowPoint);
2413 if (!document)
2414 return 0;
2415
2416 Frame* frame = document->frame();
2417 ASSERT(frame);
2418 FrameView* frameView = frame->view();
2419 if (!frameView)
2420 return 0;
2421 IntPoint framePoint = frameView->windowToContents(windowPoint);
2422 VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
2423 return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
2424 }
2425
revealSelectionAfterEditingOperation()2426 void Editor::revealSelectionAfterEditingOperation()
2427 {
2428 if (m_ignoreCompositionSelectionChange)
2429 return;
2430
2431 m_frame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2432 }
2433
setIgnoreCompositionSelectionChange(bool ignore)2434 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
2435 {
2436 if (m_ignoreCompositionSelectionChange == ignore)
2437 return;
2438
2439 m_ignoreCompositionSelectionChange = ignore;
2440 if (!ignore)
2441 revealSelectionAfterEditingOperation();
2442 }
2443
compositionRange() const2444 PassRefPtr<Range> Editor::compositionRange() const
2445 {
2446 if (!m_compositionNode)
2447 return 0;
2448 unsigned length = m_compositionNode->length();
2449 unsigned start = min(m_compositionStart, length);
2450 unsigned end = min(max(start, m_compositionEnd), length);
2451 if (start >= end)
2452 return 0;
2453 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
2454 }
2455
getCompositionSelection(unsigned & selectionStart,unsigned & selectionEnd) const2456 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
2457 {
2458 if (!m_compositionNode)
2459 return false;
2460 Position start = m_frame->selection()->start();
2461 if (start.deprecatedNode() != m_compositionNode)
2462 return false;
2463 Position end = m_frame->selection()->end();
2464 if (end.deprecatedNode() != m_compositionNode)
2465 return false;
2466
2467 if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart)
2468 return false;
2469 if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd)
2470 return false;
2471
2472 selectionStart = start.deprecatedEditingOffset() - m_compositionStart;
2473 selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd;
2474 return true;
2475 }
2476
transpose()2477 void Editor::transpose()
2478 {
2479 if (!canEdit())
2480 return;
2481
2482 VisibleSelection selection = m_frame->selection()->selection();
2483 if (!selection.isCaret())
2484 return;
2485
2486 // Make a selection that goes back one character and forward two characters.
2487 VisiblePosition caret = selection.visibleStart();
2488 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
2489 VisiblePosition previous = next.previous();
2490 if (next == previous)
2491 return;
2492 previous = previous.previous();
2493 if (!inSameParagraph(next, previous))
2494 return;
2495 RefPtr<Range> range = makeRange(previous, next);
2496 if (!range)
2497 return;
2498 VisibleSelection newSelection(range.get(), DOWNSTREAM);
2499
2500 // Transpose the two characters.
2501 String text = plainText(range.get());
2502 if (text.length() != 2)
2503 return;
2504 String transposed = text.right(1) + text.left(1);
2505
2506 // Select the two characters.
2507 if (newSelection != m_frame->selection()->selection()) {
2508 if (!m_frame->selection()->shouldChangeSelection(newSelection))
2509 return;
2510 m_frame->selection()->setSelection(newSelection);
2511 }
2512
2513 // Insert the transposed characters.
2514 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2515 return;
2516 replaceSelectionWithText(transposed, false, false);
2517 }
2518
addToKillRing(Range * range,bool prepend)2519 void Editor::addToKillRing(Range* range, bool prepend)
2520 {
2521 if (m_shouldStartNewKillRingSequence)
2522 killRing()->startNewSequence();
2523
2524 String text = plainText(range);
2525 if (prepend)
2526 killRing()->prepend(text);
2527 else
2528 killRing()->append(text);
2529 m_shouldStartNewKillRingSequence = false;
2530 }
2531
startCorrectionPanelTimer()2532 void Editor::startCorrectionPanelTimer()
2533 {
2534 m_spellingCorrector->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
2535 }
2536
handleCorrectionPanelResult(const String & correction)2537 void Editor::handleCorrectionPanelResult(const String& correction)
2538 {
2539 m_spellingCorrector->handleCorrectionPanelResult(correction);
2540 }
2541
2542
dismissCorrectionPanelAsIgnored()2543 void Editor::dismissCorrectionPanelAsIgnored()
2544 {
2545 m_spellingCorrector->dismiss(ReasonForDismissingCorrectionPanelIgnored);
2546 }
2547
insideVisibleArea(const IntPoint & point) const2548 bool Editor::insideVisibleArea(const IntPoint& point) const
2549 {
2550 if (m_frame->excludeFromTextSearch())
2551 return false;
2552
2553 // Right now, we only check the visibility of a point for disconnected frames. For all other
2554 // frames, we assume visibility.
2555 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2556 if (!frame->isDisconnected())
2557 return true;
2558
2559 RenderPart* renderer = frame->ownerRenderer();
2560 if (!renderer)
2561 return false;
2562
2563 RenderBlock* container = renderer->containingBlock();
2564 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2565 return true;
2566
2567 IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2568 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2569 rectInPageCoords.width(), rectInPageCoords.height());
2570
2571 return rectInFrameCoords.contains(point);
2572 }
2573
insideVisibleArea(Range * range) const2574 bool Editor::insideVisibleArea(Range* range) const
2575 {
2576 if (!range)
2577 return true;
2578
2579 if (m_frame->excludeFromTextSearch())
2580 return false;
2581
2582 // Right now, we only check the visibility of a range for disconnected frames. For all other
2583 // frames, we assume visibility.
2584 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2585 if (!frame->isDisconnected())
2586 return true;
2587
2588 RenderPart* renderer = frame->ownerRenderer();
2589 if (!renderer)
2590 return false;
2591
2592 RenderBlock* container = renderer->containingBlock();
2593 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2594 return true;
2595
2596 IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2597 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2598 rectInPageCoords.width(), rectInPageCoords.height());
2599 IntRect resultRect = range->boundingBox();
2600
2601 return rectInFrameCoords.contains(resultRect);
2602 }
2603
firstVisibleRange(const String & target,FindOptions options)2604 PassRefPtr<Range> Editor::firstVisibleRange(const String& target, FindOptions options)
2605 {
2606 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2607 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
2608 ExceptionCode ec = 0;
2609
2610 while (!insideVisibleArea(resultRange.get())) {
2611 searchRange->setStartAfter(resultRange->endContainer(), ec);
2612 if (searchRange->startContainer() == searchRange->endContainer())
2613 return Range::create(m_frame->document());
2614 resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
2615 }
2616
2617 return resultRange;
2618 }
2619
lastVisibleRange(const String & target,FindOptions options)2620 PassRefPtr<Range> Editor::lastVisibleRange(const String& target, FindOptions options)
2621 {
2622 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2623 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options | Backwards);
2624 ExceptionCode ec = 0;
2625
2626 while (!insideVisibleArea(resultRange.get())) {
2627 searchRange->setEndBefore(resultRange->startContainer(), ec);
2628 if (searchRange->startContainer() == searchRange->endContainer())
2629 return Range::create(m_frame->document());
2630 resultRange = findPlainText(searchRange.get(), target, options | Backwards);
2631 }
2632
2633 return resultRange;
2634 }
2635
nextVisibleRange(Range * currentRange,const String & target,FindOptions options)2636 PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, FindOptions options)
2637 {
2638 if (m_frame->excludeFromTextSearch())
2639 return Range::create(m_frame->document());
2640
2641 RefPtr<Range> resultRange = currentRange;
2642 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2643 ExceptionCode ec = 0;
2644 bool forward = !(options & Backwards);
2645 for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, options)) {
2646 if (resultRange->collapsed(ec)) {
2647 if (!resultRange->startContainer()->isInShadowTree())
2648 break;
2649 searchRange = rangeOfContents(m_frame->document());
2650 if (forward)
2651 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
2652 else
2653 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec);
2654 continue;
2655 }
2656
2657 if (forward)
2658 searchRange->setStartAfter(resultRange->endContainer(), ec);
2659 else
2660 searchRange->setEndBefore(resultRange->startContainer(), ec);
2661
2662 Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
2663 if (searchRange->collapsed(ec) && shadowTreeRoot) {
2664 if (forward)
2665 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
2666 else
2667 searchRange->setStartBefore(shadowTreeRoot, ec);
2668 }
2669
2670 if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode())
2671 break;
2672 }
2673
2674 if (insideVisibleArea(resultRange.get()))
2675 return resultRange;
2676
2677 if (!(options & WrapAround))
2678 return Range::create(m_frame->document());
2679
2680 if (options & Backwards)
2681 return lastVisibleRange(target, options);
2682
2683 return firstVisibleRange(target, options);
2684 }
2685
changeSelectionAfterCommand(const VisibleSelection & newSelection,bool closeTyping,bool clearTypingStyle)2686 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle)
2687 {
2688 // If the new selection is orphaned, then don't update the selection.
2689 if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
2690 return;
2691
2692 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
2693 // because there is work that it must do in this situation.
2694 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
2695 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
2696 bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection();
2697 if (selectionDidNotChangeDOMPosition || m_frame->selection()->shouldChangeSelection(newSelection)) {
2698 SelectionController::SetSelectionOptions options = 0;
2699 if (closeTyping)
2700 options |= SelectionController::CloseTyping;
2701 if (clearTypingStyle)
2702 options |= SelectionController::ClearTypingStyle;
2703 m_frame->selection()->setSelection(newSelection, options);
2704 }
2705
2706 // Some editing operations change the selection visually without affecting its position within the DOM.
2707 // For example when you press return in the following (the caret is marked by ^):
2708 // <div contentEditable="true"><div>^Hello</div></div>
2709 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
2710 // change the caret's DOM position (["hello", 0]). In these situations the above SelectionController::setSelection call
2711 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
2712 // starts a new kill ring sequence, but we want to do these things (matches AppKit).
2713 if (selectionDidNotChangeDOMPosition)
2714 client()->respondToChangedSelection();
2715 }
2716
selectedText() const2717 String Editor::selectedText() const
2718 {
2719 // We remove '\0' characters because they are not visibly rendered to the user.
2720 return plainText(m_frame->selection()->toNormalizedRange().get()).replace(0, "");
2721 }
2722
firstRectForRange(Range * range) const2723 IntRect Editor::firstRectForRange(Range* range) const
2724 {
2725 int extraWidthToEndOfLine = 0;
2726 ASSERT(range->startContainer());
2727 ASSERT(range->endContainer());
2728
2729 InlineBox* startInlineBox;
2730 int startCaretOffset;
2731 Position startPosition = VisiblePosition(range->startPosition()).deepEquivalent();
2732 if (startPosition.isNull())
2733 return IntRect();
2734 startPosition.getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
2735
2736 RenderObject* startRenderer = startPosition.deprecatedNode()->renderer();
2737 ASSERT(startRenderer);
2738 IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
2739 if (startCaretRect != IntRect())
2740 startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
2741
2742 InlineBox* endInlineBox;
2743 int endCaretOffset;
2744 Position endPosition = VisiblePosition(range->endPosition()).deepEquivalent();
2745 if (endPosition.isNull())
2746 return IntRect();
2747 endPosition.getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
2748
2749 RenderObject* endRenderer = endPosition.deprecatedNode()->renderer();
2750 ASSERT(endRenderer);
2751 IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset);
2752 if (endCaretRect != IntRect())
2753 endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
2754
2755 if (startCaretRect.y() == endCaretRect.y()) {
2756 // start and end are on the same line
2757 return IntRect(min(startCaretRect.x(), endCaretRect.x()),
2758 startCaretRect.y(),
2759 abs(endCaretRect.x() - startCaretRect.x()),
2760 max(startCaretRect.height(), endCaretRect.height()));
2761 }
2762
2763 // start and end aren't on the same line, so go from start to the end of its line
2764 return IntRect(startCaretRect.x(),
2765 startCaretRect.y(),
2766 startCaretRect.width() + extraWidthToEndOfLine,
2767 startCaretRect.height());
2768 }
2769
shouldChangeSelection(const VisibleSelection & oldSelection,const VisibleSelection & newSelection,EAffinity affinity,bool stillSelecting) const2770 bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const
2771 {
2772 return client() && client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting);
2773 }
2774
computeAndSetTypingStyle(CSSStyleDeclaration * style,EditAction editingAction)2775 void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction editingAction)
2776 {
2777 if (!style || !style->length()) {
2778 m_frame->selection()->clearTypingStyle();
2779 return;
2780 }
2781
2782 // Calculate the current typing style.
2783 RefPtr<EditingStyle> typingStyle;
2784 if (m_frame->selection()->typingStyle()) {
2785 typingStyle = m_frame->selection()->typingStyle()->copy();
2786 typingStyle->overrideWithStyle(style->makeMutable().get());
2787 } else
2788 typingStyle = EditingStyle::create(style);
2789
2790 typingStyle->prepareToApplyAt(m_frame->selection()->selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection);
2791
2792 // Handle block styles, substracting these from the typing style.
2793 RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties();
2794 if (!blockStyle->isEmpty())
2795 applyCommand(ApplyStyleCommand::create(m_frame->document(), blockStyle.get(), editingAction));
2796
2797 // Set the remaining style as the typing style.
2798 m_frame->selection()->setTypingStyle(typingStyle);
2799 }
2800
selectionStartStyle() const2801 PassRefPtr<EditingStyle> Editor::selectionStartStyle() const
2802 {
2803 if (m_frame->selection()->isNone())
2804 return 0;
2805
2806 RefPtr<Range> range(m_frame->selection()->toNormalizedRange());
2807 Position position = range->editingStartPosition();
2808
2809 // If the pos is at the end of a text node, then this node is not fully selected.
2810 // Move it to the next deep equivalent position to avoid removing the style from this node.
2811 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
2812 // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold.
2813 Node* positionNode = position.containerNode();
2814 if (m_frame->selection()->isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset())
2815 position = nextVisuallyDistinctCandidate(position);
2816
2817 Element* element = position.element();
2818 if (!element)
2819 return 0;
2820
2821 RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
2822 style->mergeTypingStyle(m_frame->document());
2823 return style;
2824 }
2825
textFieldDidBeginEditing(Element * e)2826 void Editor::textFieldDidBeginEditing(Element* e)
2827 {
2828 if (client())
2829 client()->textFieldDidBeginEditing(e);
2830 }
2831
textFieldDidEndEditing(Element * e)2832 void Editor::textFieldDidEndEditing(Element* e)
2833 {
2834 if (client())
2835 client()->textFieldDidEndEditing(e);
2836 }
2837
textDidChangeInTextField(Element * e)2838 void Editor::textDidChangeInTextField(Element* e)
2839 {
2840 if (client())
2841 client()->textDidChangeInTextField(e);
2842 }
2843
doTextFieldCommandFromEvent(Element * e,KeyboardEvent * ke)2844 bool Editor::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
2845 {
2846 if (client())
2847 return client()->doTextFieldCommandFromEvent(e, ke);
2848
2849 return false;
2850 }
2851
textWillBeDeletedInTextField(Element * input)2852 void Editor::textWillBeDeletedInTextField(Element* input)
2853 {
2854 if (client())
2855 client()->textWillBeDeletedInTextField(input);
2856 }
2857
textDidChangeInTextArea(Element * e)2858 void Editor::textDidChangeInTextArea(Element* e)
2859 {
2860 if (client())
2861 client()->textDidChangeInTextArea(e);
2862 }
2863
applyEditingStyleToBodyElement() const2864 void Editor::applyEditingStyleToBodyElement() const
2865 {
2866 RefPtr<NodeList> list = m_frame->document()->getElementsByTagName("body");
2867 unsigned len = list->length();
2868 for (unsigned i = 0; i < len; i++)
2869 applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
2870 }
2871
applyEditingStyleToElement(Element * element) const2872 void Editor::applyEditingStyleToElement(Element* element) const
2873 {
2874 if (!element)
2875 return;
2876
2877 CSSStyleDeclaration* style = element->style();
2878 ASSERT(style);
2879
2880 ExceptionCode ec = 0;
2881 style->setProperty(CSSPropertyWordWrap, "break-word", false, ec);
2882 ASSERT(!ec);
2883 style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec);
2884 ASSERT(!ec);
2885 style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec);
2886 ASSERT(!ec);
2887 }
2888
styleForSelectionStart(Node * & nodeToRemove) const2889 RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const
2890 {
2891 nodeToRemove = 0;
2892
2893 if (m_frame->selection()->isNone())
2894 return 0;
2895
2896 Position position = m_frame->selection()->selection().visibleStart().deepEquivalent();
2897 if (!position.isCandidate())
2898 return 0;
2899 if (!position.deprecatedNode())
2900 return 0;
2901
2902 RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle();
2903 if (!typingStyle || !typingStyle->style())
2904 return position.deprecatedNode()->renderer()->style();
2905
2906 RefPtr<Element> styleElement = m_frame->document()->createElement(spanTag, false);
2907
2908 ExceptionCode ec = 0;
2909 String styleText = typingStyle->style()->cssText() + " display: inline";
2910 styleElement->setAttribute(styleAttr, styleText.impl(), ec);
2911 ASSERT(!ec);
2912
2913 styleElement->appendChild(m_frame->document()->createEditingTextNode(""), ec);
2914 ASSERT(!ec);
2915
2916 position.deprecatedNode()->parentNode()->appendChild(styleElement, ec);
2917 ASSERT(!ec);
2918
2919 nodeToRemove = styleElement.get();
2920 return styleElement->renderer() ? styleElement->renderer()->style() : 0;
2921 }
2922
2923 // Searches from the beginning of the document if nothing is selected.
findString(const String & target,bool forward,bool caseFlag,bool wrapFlag,bool startInSelection)2924 bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
2925 {
2926 FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0);
2927 return findString(target, options);
2928 }
2929
findString(const String & target,FindOptions options)2930 bool Editor::findString(const String& target, FindOptions options)
2931 {
2932 if (target.isEmpty())
2933 return false;
2934
2935 if (m_frame->excludeFromTextSearch())
2936 return false;
2937
2938 // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
2939 // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
2940 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2941 VisibleSelection selection = m_frame->selection()->selection();
2942
2943 bool forward = !(options & Backwards);
2944 bool startInSelection = options & StartInSelection;
2945 if (forward)
2946 setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
2947 else
2948 setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
2949
2950 RefPtr<Node> shadowTreeRoot = selection.shadowTreeRootNode();
2951 if (shadowTreeRoot) {
2952 ExceptionCode ec = 0;
2953 if (forward)
2954 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
2955 else
2956 searchRange->setStart(shadowTreeRoot.get(), 0, ec);
2957 }
2958
2959 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options));
2960 // If we started in the selection and the found range exactly matches the existing selection, find again.
2961 // Build a selection with the found range to remove collapsed whitespace.
2962 // Compare ranges instead of selection objects to ignore the way that the current selection was made.
2963 if (startInSelection && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), selection.toNormalizedRange().get())) {
2964 searchRange = rangeOfContents(m_frame->document());
2965 if (forward)
2966 setStart(searchRange.get(), selection.visibleEnd());
2967 else
2968 setEnd(searchRange.get(), selection.visibleStart());
2969
2970 if (shadowTreeRoot) {
2971 ExceptionCode ec = 0;
2972 if (forward)
2973 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
2974 else
2975 searchRange->setStart(shadowTreeRoot.get(), 0, ec);
2976 }
2977
2978 resultRange = findPlainText(searchRange.get(), target, options);
2979 }
2980
2981 ExceptionCode exception = 0;
2982
2983 // If nothing was found in the shadow tree, search in main content following the shadow tree.
2984 if (resultRange->collapsed(exception) && shadowTreeRoot) {
2985 searchRange = rangeOfContents(m_frame->document());
2986 if (forward)
2987 searchRange->setStartAfter(shadowTreeRoot->shadowHost(), exception);
2988 else
2989 searchRange->setEndBefore(shadowTreeRoot->shadowHost(), exception);
2990
2991 resultRange = findPlainText(searchRange.get(), target, options);
2992 }
2993
2994 if (!insideVisibleArea(resultRange.get())) {
2995 resultRange = nextVisibleRange(resultRange.get(), target, options);
2996 if (!resultRange)
2997 return false;
2998 }
2999
3000 // If we didn't find anything and we're wrapping, search again in the entire document (this will
3001 // redundantly re-search the area already searched in some cases).
3002 if (resultRange->collapsed(exception) && options & WrapAround) {
3003 searchRange = rangeOfContents(m_frame->document());
3004 resultRange = findPlainText(searchRange.get(), target, options);
3005 // We used to return false here if we ended up with the same range that we started with
3006 // (e.g., the selection was already the only instance of this text). But we decided that
3007 // this should be a success case instead, so we'll just fall through in that case.
3008 }
3009
3010 if (resultRange->collapsed(exception))
3011 return false;
3012
3013 m_frame->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
3014 m_frame->selection()->revealSelection();
3015 return true;
3016 }
3017
isFrameInRange(Frame * frame,Range * range)3018 static bool isFrameInRange(Frame* frame, Range* range)
3019 {
3020 bool inRange = false;
3021 for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document()->ownerElement()) {
3022 if (ownerElement->document() == range->ownerDocument()) {
3023 ExceptionCode ec = 0;
3024 inRange = range->intersectsNode(ownerElement, ec);
3025 break;
3026 }
3027 }
3028 return inRange;
3029 }
3030
countMatchesForText(const String & target,FindOptions options,unsigned limit,bool markMatches)3031 unsigned Editor::countMatchesForText(const String& target, FindOptions options, unsigned limit, bool markMatches)
3032 {
3033 return countMatchesForText(target, 0, options, limit, markMatches);
3034 }
3035
countMatchesForText(const String & target,Range * range,FindOptions options,unsigned limit,bool markMatches)3036 unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches)
3037 {
3038 if (target.isEmpty())
3039 return 0;
3040
3041 RefPtr<Range> searchRange;
3042 if (range) {
3043 if (range->ownerDocument() == m_frame->document())
3044 searchRange = range;
3045 else if (!isFrameInRange(m_frame, range))
3046 return 0;
3047 }
3048 if (!searchRange)
3049 searchRange = rangeOfContents(m_frame->document());
3050
3051 Node* originalEndContainer = searchRange->endContainer();
3052 int originalEndOffset = searchRange->endOffset();
3053
3054 ExceptionCode exception = 0;
3055 unsigned matchCount = 0;
3056 do {
3057 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options & ~Backwards));
3058 if (resultRange->collapsed(exception)) {
3059 if (!resultRange->startContainer()->isInShadowTree())
3060 break;
3061
3062 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception);
3063 searchRange->setEnd(originalEndContainer, originalEndOffset, exception);
3064 continue;
3065 }
3066
3067 // Only treat the result as a match if it is visible
3068 if (insideVisibleArea(resultRange.get())) {
3069 ++matchCount;
3070 if (markMatches)
3071 m_frame->document()->markers()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
3072 }
3073
3074 // Stop looking if we hit the specified limit. A limit of 0 means no limit.
3075 if (limit > 0 && matchCount >= limit)
3076 break;
3077
3078 // Set the new start for the search range to be the end of the previous
3079 // result range. There is no need to use a VisiblePosition here,
3080 // since findPlainText will use a TextIterator to go over the visible
3081 // text nodes.
3082 searchRange->setStart(resultRange->endContainer(exception), resultRange->endOffset(exception), exception);
3083
3084 Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
3085 if (searchRange->collapsed(exception) && shadowTreeRoot)
3086 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception);
3087 } while (true);
3088
3089 if (markMatches) {
3090 // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match.
3091 if (m_frame->view() && m_frame->contentRenderer()) {
3092 m_frame->document()->updateLayout(); // Ensure layout is up to date.
3093 IntRect visibleRect = m_frame->view()->visibleContentRect();
3094 if (!visibleRect.isEmpty()) {
3095 GraphicsContext context((PlatformGraphicsContext*)0);
3096 context.setPaintingDisabled(true);
3097
3098 PaintBehavior oldBehavior = m_frame->view()->paintBehavior();
3099 m_frame->view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
3100 m_frame->view()->paintContents(&context, visibleRect);
3101 m_frame->view()->setPaintBehavior(oldBehavior);
3102 }
3103 }
3104 }
3105
3106 return matchCount;
3107 }
3108
setMarkedTextMatchesAreHighlighted(bool flag)3109 void Editor::setMarkedTextMatchesAreHighlighted(bool flag)
3110 {
3111 if (flag == m_areMarkedTextMatchesHighlighted)
3112 return;
3113
3114 m_areMarkedTextMatchesHighlighted = flag;
3115 m_frame->document()->markers()->repaintMarkers(DocumentMarker::TextMatch);
3116 }
3117
respondToChangedSelection(const VisibleSelection & oldSelection,SelectionController::SetSelectionOptions options)3118 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions options)
3119 {
3120 m_spellingCorrector->stopPendingCorrection(oldSelection);
3121
3122 bool closeTyping = options & SelectionController::CloseTyping;
3123 bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
3124 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
3125 if (isContinuousSpellCheckingEnabled) {
3126 VisibleSelection newAdjacentWords;
3127 VisibleSelection newSelectedSentence;
3128 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
3129 if (m_frame->selection()->selection().isContentEditable() || caretBrowsing) {
3130 VisiblePosition newStart(m_frame->selection()->selection().visibleStart());
3131 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3132 if (isContinuousGrammarCheckingEnabled)
3133 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
3134 }
3135
3136 // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
3137 bool shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered);
3138
3139 // When typing we check spelling elsewhere, so don't redo it here.
3140 // If this is a change in selection resulting from a delete operation,
3141 // oldSelection may no longer be in the document.
3142 if (shouldCheckSpellingAndGrammar && closeTyping && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) {
3143 VisiblePosition oldStart(oldSelection.visibleStart());
3144 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
3145 if (oldAdjacentWords != newAdjacentWords) {
3146 if (isContinuousGrammarCheckingEnabled) {
3147 VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
3148 markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence);
3149 } else
3150 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
3151 }
3152 }
3153
3154 #if !PLATFORM(MAC) || (PLATFORM(MAC) && (defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)))
3155 // This only erases markers that are in the first unit (word or sentence) of the selection.
3156 // Perhaps peculiar, but it matches AppKit on these Mac OS X versions.
3157 if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
3158 m_frame->document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
3159 #endif
3160 if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
3161 m_frame->document()->markers()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
3162 }
3163
3164 // When continuous spell checking is off, existing markers disappear after the selection changes.
3165 if (!isContinuousSpellCheckingEnabled)
3166 m_frame->document()->markers()->removeMarkers(DocumentMarker::Spelling);
3167 if (!isContinuousGrammarCheckingEnabled)
3168 m_frame->document()->markers()->removeMarkers(DocumentMarker::Grammar);
3169
3170 respondToChangedSelection(oldSelection);
3171 }
3172
findFirstMarkable(Node * node)3173 static Node* findFirstMarkable(Node* node)
3174 {
3175 while (node) {
3176 if (!node->renderer())
3177 return 0;
3178 if (node->renderer()->isText())
3179 return node;
3180 if (node->renderer()->isTextControl())
3181 node = toRenderTextControl(node->renderer())->visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
3182 else if (node->firstChild())
3183 node = node->firstChild();
3184 else
3185 node = node->nextSibling();
3186 }
3187
3188 return 0;
3189 }
3190
selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType,int from,int length) const3191 bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
3192 {
3193 Node* node = findFirstMarkable(m_frame->selection()->start().deprecatedNode());
3194 if (!node)
3195 return false;
3196
3197 unsigned int startOffset = static_cast<unsigned int>(from);
3198 unsigned int endOffset = static_cast<unsigned int>(from + length);
3199 Vector<DocumentMarker> markers = m_frame->document()->markers()->markersForNode(node);
3200 for (size_t i = 0; i < markers.size(); ++i) {
3201 DocumentMarker marker = markers[i];
3202 if (marker.startOffset <= startOffset && endOffset <= marker.endOffset && marker.type == markerType)
3203 return true;
3204 }
3205
3206 return false;
3207 }
3208
textCheckingTypeMaskFor(TextCheckingOptions textCheckingOptions)3209 TextCheckingTypeMask Editor::textCheckingTypeMaskFor(TextCheckingOptions textCheckingOptions)
3210 {
3211 bool shouldMarkSpelling = textCheckingOptions & MarkSpelling;
3212 bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
3213 bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
3214 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
3215
3216 TextCheckingTypeMask checkingTypes = 0;
3217 if (shouldMarkSpelling)
3218 checkingTypes |= TextCheckingTypeSpelling;
3219 if (shouldMarkGrammar)
3220 checkingTypes |= TextCheckingTypeGrammar;
3221 if (shouldCheckForCorrection)
3222 checkingTypes |= TextCheckingTypeCorrection;
3223
3224 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
3225 bool shouldPerformReplacement = textCheckingOptions & PerformReplacement;
3226 if (shouldPerformReplacement) {
3227 if (isAutomaticLinkDetectionEnabled())
3228 checkingTypes |= TextCheckingTypeLink;
3229 if (isAutomaticQuoteSubstitutionEnabled())
3230 checkingTypes |= TextCheckingTypeQuote;
3231 if (isAutomaticDashSubstitutionEnabled())
3232 checkingTypes |= TextCheckingTypeDash;
3233 if (isAutomaticTextReplacementEnabled())
3234 checkingTypes |= TextCheckingTypeReplacement;
3235 if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled())
3236 checkingTypes |= TextCheckingTypeCorrection;
3237 }
3238 #endif
3239
3240 return checkingTypes;
3241 }
3242
3243 } // namespace WebCore
3244