2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Trolltech ASA
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include "AXObjectCache.h"
31 #include "ApplyStyleCommand.h"
32 #include "CSSComputedStyleDeclaration.h"
33 #include "CSSProperty.h"
34 #include "CSSPropertyNames.h"
35 #include "ClipboardEvent.h"
36 #include "DeleteButtonController.h"
37 #include "DeleteSelectionCommand.h"
38 #include "DocLoader.h"
39 #include "DocumentFragment.h"
40 #include "EditorClient.h"
41 #include "EventHandler.h"
42 #include "EventNames.h"
43 #include "FocusController.h"
45 #include "FrameView.h"
46 #include "HTMLInputElement.h"
47 #include "HTMLTextAreaElement.h"
48 #include "HitTestResult.h"
49 #include "IndentOutdentCommand.h"
50 #include "InsertListCommand.h"
51 #include "KeyboardEvent.h"
52 #include "ModifySelectionListLevel.h"
54 #include "Pasteboard.h"
55 #include "RemoveFormatCommand.h"
56 #include "ReplaceSelectionCommand.h"
59 #include "TextIterator.h"
60 #include "TypingCommand.h"
61 #include "htmlediting.h"
63 #include "visible_units.h"
68 using namespace EventNames;
69 using namespace HTMLNames;
71 // When an event handler has moved the selection outside of a text control
72 // we should use the target control's selection for this editing operation.
73 Selection Editor::selectionForCommand(Event* event)
75 Selection selection = m_frame->selectionController()->selection();
78 // If the target is a text control, and the current selection is outside of its shadow tree,
79 // then use the saved selection for that text control.
80 Node* target = event->target()->toNode();
81 Node* selectionStart = selection.start().node();
82 if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
83 if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
84 return static_cast<HTMLInputElement*>(target)->selection();
85 if (target->hasTagName(textareaTag))
86 return static_cast<HTMLTextAreaElement*>(target)->selection();
91 EditorClient* Editor::client() const
93 if (Page* page = m_frame->page())
94 return page->editorClient();
98 void Editor::handleKeyboardEvent(KeyboardEvent* event)
100 if (EditorClient* c = client())
101 if (selectionForCommand(event).isContentEditable())
102 c->handleKeyboardEvent(event);
105 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
107 if (EditorClient* c = client())
108 if (selectionForCommand(event).isContentEditable())
109 c->handleInputMethodKeydown(event);
112 bool Editor::canEdit() const
114 return m_frame->selectionController()->isContentEditable();
117 bool Editor::canEditRichly() const
119 return m_frame->selectionController()->isContentRichlyEditable();
122 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
123 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
124 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
125 // normally selectable to implement copy/paste (like divs, or a document body).
127 bool Editor::canDHTMLCut()
129 return !m_frame->selectionController()->isInPasswordField() && !dispatchCPPEvent(beforecutEvent, ClipboardNumb);
132 bool Editor::canDHTMLCopy()
134 return !m_frame->selectionController()->isInPasswordField() && !dispatchCPPEvent(beforecopyEvent, ClipboardNumb);
137 bool Editor::canDHTMLPaste()
139 return !dispatchCPPEvent(beforepasteEvent, ClipboardNumb);
142 bool Editor::canCut() const
144 return canCopy() && canDelete();
147 static HTMLImageElement* imageElementFromImageDocument(Document* document)
151 if (!document->isImageDocument())
154 HTMLElement* body = document->body();
158 Node* node = body->firstChild();
161 if (!node->hasTagName(imgTag))
163 return static_cast<HTMLImageElement*>(node);
166 bool Editor::canCopy() const
168 if (imageElementFromImageDocument(m_frame->document()))
170 SelectionController* selectionController = m_frame->selectionController();
171 return selectionController->isRange() && !selectionController->isInPasswordField();
174 bool Editor::canPaste() const
179 bool Editor::canDelete() const
181 SelectionController* selectionController = m_frame->selectionController();
182 return selectionController->isRange() && selectionController->isContentEditable();
185 bool Editor::canDeleteRange(Range* range) const
187 ExceptionCode ec = 0;
188 Node* startContainer = range->startContainer(ec);
189 Node* endContainer = range->endContainer(ec);
190 if (!startContainer || !endContainer)
193 if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
196 if (range->collapsed(ec)) {
197 VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
198 VisiblePosition previous = start.previous();
199 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
200 if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
206 bool Editor::smartInsertDeleteEnabled()
208 return client() && client()->smartInsertDeleteEnabled();
211 bool Editor::canSmartCopyOrDelete()
213 return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
216 bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
218 // Delete the selection, if there is one.
219 // If not, make a selection using the passed-in direction and granularity.
224 if (m_frame->selectionController()->isRange()) {
226 addToKillRing(selectedRange().get(), false);
227 if (isTypingAction) {
228 if (m_frame->document()) {
229 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity);
230 revealSelectionAfterEditingOperation();
233 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
234 // Implicitly calls revealSelectionAfterEditingOperation().
237 SelectionController selectionToDelete;
238 selectionToDelete.setSelection(m_frame->selectionController()->selection());
239 selectionToDelete.modify(SelectionController::EXTEND, direction, granularity);
240 if (killRing && selectionToDelete.isCaret() && granularity != CharacterGranularity)
241 selectionToDelete.modify(SelectionController::EXTEND, direction, CharacterGranularity);
243 RefPtr<Range> range = selectionToDelete.toRange();
246 addToKillRing(range.get(), false);
248 if (!m_frame->selectionController()->setSelectedRange(range.get(), DOWNSTREAM, (granularity != CharacterGranularity)))
252 case SelectionController::FORWARD:
253 case SelectionController::RIGHT:
254 if (m_frame->document())
255 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), false, granularity);
257 case SelectionController::BACKWARD:
258 case SelectionController::LEFT:
259 if (m_frame->document())
260 TypingCommand::deleteKeyPressed(m_frame->document(), false, granularity);
263 revealSelectionAfterEditingOperation();
266 // clear the "start new kill ring sequence" setting, because it was set to true
267 // when the selection was updated by deleting the range
269 setStartNewKillRingSequence(false);
274 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
276 if (m_frame->selectionController()->isNone())
279 applyCommand(new DeleteSelectionCommand(m_frame->document(), smartDelete));
282 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
284 String text = pasteboard->plainText(m_frame);
285 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
286 replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
289 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
291 RefPtr<Range> range = selectedRange();
293 RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
294 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
295 replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
298 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
300 return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
303 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
308 Node* child = fragment->firstChild();
309 if (child && fragment->lastChild() == child && child->isCharacterDataNode())
310 return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
312 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
315 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
317 if (m_frame->selectionController()->isNone() || !fragment)
320 applyCommand(new ReplaceSelectionCommand(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle));
321 revealSelectionAfterEditingOperation();
324 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
326 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
329 PassRefPtr<Range> Editor::selectedRange()
333 return m_frame->selectionController()->toRange();
336 bool Editor::shouldDeleteRange(Range* range) const
339 if (!range || range->collapsed(ec))
342 if (!canDeleteRange(range))
345 return client() && client()->shouldDeleteRange(range);
348 bool Editor::tryDHTMLCopy()
350 if (m_frame->selectionController()->isInPasswordField())
353 // Must be done before oncopy adds types and data to the pboard,
354 // also done for security, as it erases data from the last copy/paste.
355 Pasteboard::generalPasteboard()->clear();
357 return !dispatchCPPEvent(copyEvent, ClipboardWritable);
360 bool Editor::tryDHTMLCut()
362 if (m_frame->selectionController()->isInPasswordField())
365 // Must be done before oncut adds types and data to the pboard,
366 // also done for security, as it erases data from the last copy/paste.
367 Pasteboard::generalPasteboard()->clear();
369 return !dispatchCPPEvent(cutEvent, ClipboardWritable);
372 bool Editor::tryDHTMLPaste()
374 return !dispatchCPPEvent(pasteEvent, ClipboardReadable);
377 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
379 pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
382 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
384 return client() && client()->shouldInsertText(text, range, action);
387 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
389 return client() && client()->shouldShowDeleteInterface(element);
392 void Editor::respondToChangedSelection(const Selection& oldSelection)
395 client()->respondToChangedSelection();
396 m_deleteButtonController->respondToChangedSelection(oldSelection);
399 void Editor::respondToChangedContents(const Selection& endingSelection)
401 if (AXObjectCache::accessibilityEnabled()) {
402 Node* node = endingSelection.start().node();
404 m_frame->renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
408 client()->respondToChangedContents();
411 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
414 hasMultipleFonts = false;
416 if (!m_frame->selectionController()->isRange()) {
418 RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove); // sets nodeToRemove
420 const SimpleFontData* result = 0;
422 result = style->font().primaryFont();
426 nodeToRemove->remove(ec);
433 const SimpleFontData* font = 0;
435 RefPtr<Range> range = m_frame->selectionController()->toRange();
436 Node* startNode = range->editingStartPosition().node();
438 Node* pastEnd = range->pastEndNode();
439 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
440 // unreproducible case where this didn't happen, so check for nil also.
441 for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
442 RenderObject *renderer = n->renderer();
445 // FIXME: Are there any node types that have renderers, but that we should be skipping?
446 const SimpleFontData* f = renderer->style()->font().primaryFont();
449 else if (font != f) {
450 hasMultipleFonts = true;
462 TriState Editor::selectionUnorderedListState() const
464 if (m_frame->selectionController()->isCaret()) {
465 if (enclosingNodeWithTag(m_frame->selectionController()->selection().start(), ulTag))
467 } else if (m_frame->selectionController()->isRange()) {
468 Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start(), ulTag);
469 Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end(), ulTag);
470 if (startNode && endNode && startNode == endNode)
474 return FalseTriState;
477 TriState Editor::selectionOrderedListState() const
479 if (m_frame->selectionController()->isCaret()) {
480 if (enclosingNodeWithTag(m_frame->selectionController()->selection().start(), olTag))
482 } else if (m_frame->selectionController()->isRange()) {
483 Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start(), olTag);
484 Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end(), olTag);
485 if (startNode && endNode && startNode == endNode)
489 return FalseTriState;
492 PassRefPtr<Node> Editor::insertOrderedList()
494 if (!canEditRichly())
497 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
498 revealSelectionAfterEditingOperation();
502 PassRefPtr<Node> Editor::insertUnorderedList()
504 if (!canEditRichly())
507 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
508 revealSelectionAfterEditingOperation();
512 bool Editor::canIncreaseSelectionListLevel()
514 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
517 bool Editor::canDecreaseSelectionListLevel()
519 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
522 PassRefPtr<Node> Editor::increaseSelectionListLevel()
524 if (!canEditRichly() || m_frame->selectionController()->isNone())
527 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
528 revealSelectionAfterEditingOperation();
532 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
534 if (!canEditRichly() || m_frame->selectionController()->isNone())
537 PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
538 revealSelectionAfterEditingOperation();
542 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
544 if (!canEditRichly() || m_frame->selectionController()->isNone())
547 PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
548 revealSelectionAfterEditingOperation();
552 void Editor::decreaseSelectionListLevel()
554 if (!canEditRichly() || m_frame->selectionController()->isNone())
557 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
558 revealSelectionAfterEditingOperation();
561 void Editor::removeFormattingAndStyle()
563 applyCommand(new RemoveFormatCommand(m_frame->document()));
566 void Editor::setLastEditCommand(PassRefPtr<EditCommand> lastEditCommand)
568 m_lastEditCommand = lastEditCommand;
571 // Returns whether caller should continue with "the default processing", which is the same as
572 // the event handler NOT setting the return value to false
573 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
575 Node* target = m_frame->selectionController()->start().element();
576 if (!target && m_frame->document())
577 target = m_frame->document()->body();
580 target = target->shadowAncestorNode();
582 RefPtr<Clipboard> clipboard = newGeneralClipboard(policy);
584 ExceptionCode ec = 0;
585 RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
586 EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
587 bool noDefaultProcessing = evt->defaultPrevented();
589 // invalidate clipboard here for security
590 clipboard->setAccessPolicy(ClipboardNumb);
592 return !noDefaultProcessing;
595 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
597 switch (m_frame->selectionController()->state()) {
598 case Selection::NONE:
601 case Selection::CARET:
602 m_frame->computeAndSetTypingStyle(style, editingAction);
604 case Selection::RANGE:
605 if (m_frame->document() && style)
606 applyCommand(new ApplyStyleCommand(m_frame->document(), style, editingAction));
611 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
613 return client()->shouldApplyStyle(style, range);
616 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
618 switch (m_frame->selectionController()->state()) {
619 case Selection::NONE:
622 case Selection::CARET:
623 case Selection::RANGE:
624 if (m_frame->document() && style)
625 applyCommand(new ApplyStyleCommand(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
630 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
632 if (!style || style->length() == 0 || !canEditRichly())
635 if (client() && client()->shouldApplyStyle(style, m_frame->selectionController()->toRange().get()))
636 applyStyle(style, editingAction);
639 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
641 if (!style || style->length() == 0 || !canEditRichly())
644 if (client() && client()->shouldApplyStyle(style, m_frame->selectionController()->toRange().get()))
645 applyParagraphStyle(style, editingAction);
648 bool Editor::clientIsEditable() const
650 return client() && client()->isEditable();
653 bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const
656 RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
660 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
663 DeprecatedValueListConstIterator<CSSProperty> end;
664 for (DeprecatedValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
665 int propertyID = (*it).id();
666 if (!equalIgnoringCase(mutableStyle->getPropertyValue(propertyID), selectionStyle->getPropertyValue(propertyID))) {
673 ExceptionCode ec = 0;
674 nodeToRemove->remove(ec);
681 static void updateState(CSSMutableStyleDeclaration* desiredStyle, CSSComputedStyleDeclaration* computedStyle, bool& atStart, TriState& state)
683 DeprecatedValueListConstIterator<CSSProperty> end;
684 for (DeprecatedValueListConstIterator<CSSProperty> it = desiredStyle->valuesIterator(); it != end; ++it) {
685 int propertyID = (*it).id();
686 String desiredProperty = desiredStyle->getPropertyValue(propertyID);
687 String computedProperty = computedStyle->getPropertyValue(propertyID);
688 TriState propertyState = equalIgnoringCase(desiredProperty, computedProperty)
689 ? TrueTriState : FalseTriState;
691 state = propertyState;
693 } else if (state != propertyState) {
694 state = MixedTriState;
700 TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const
703 TriState state = FalseTriState;
705 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
707 if (!m_frame->selectionController()->isRange()) {
709 RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
711 return FalseTriState;
712 updateState(mutableStyle.get(), selectionStyle.get(), atStart, state);
714 ExceptionCode ec = 0;
715 nodeToRemove->remove(ec);
719 for (Node* node = m_frame->selectionController()->start().node(); node; node = node->traverseNextNode()) {
720 RefPtr<CSSComputedStyleDeclaration> computedStyle = new CSSComputedStyleDeclaration(node);
722 updateState(mutableStyle.get(), computedStyle.get(), atStart, state);
723 if (state == MixedTriState)
725 if (node == m_frame->selectionController()->end().node())
732 void Editor::indent()
734 applyCommand(new IndentOutdentCommand(m_frame->document(), IndentOutdentCommand::Indent));
737 void Editor::outdent()
739 applyCommand(new IndentOutdentCommand(m_frame->document(), IndentOutdentCommand::Outdent));
742 static void dispatchEditableContentChangedEvents(const EditCommand& command)
744 Element* startRoot = command.startingRootEditableElement();
745 Element* endRoot = command.endingRootEditableElement();
748 startRoot->dispatchEvent(new Event(webkitEditableContentChangedEvent, false, false), ec, true);
749 if (endRoot && endRoot != startRoot)
750 endRoot->dispatchEvent(new Event(webkitEditableContentChangedEvent, false, false), ec, true);
753 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
755 dispatchEditableContentChangedEvents(*cmd);
757 // FIXME: We shouldn't tell setSelection to clear the typing style or removed anchor here.
758 // If we didn't, we wouldn't have to save/restore the removedAnchor, and we wouldn't have to have
759 // the typing style stored in two places (the Frame and commands).
760 RefPtr<Node> anchor = removedAnchor();
762 Selection newSelection(cmd->endingSelection());
763 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
764 // because there is work that it must do in this situation.
765 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
766 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
767 if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
768 m_frame->selectionController()->setSelection(newSelection, false);
770 setRemovedAnchor(anchor);
772 // Now set the typing style from the command. Clear it when done.
773 // This helps make the case work where you completely delete a piece
774 // of styled text and then type a character immediately after.
775 // That new character needs to take on the style of the just-deleted text.
776 // FIXME: Improve typing style.
777 // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
778 if (cmd->typingStyle()) {
779 m_frame->setTypingStyle(cmd->typingStyle());
780 cmd->setTypingStyle(0);
783 // Command will be equal to last edit command only in the case of typing
784 if (m_lastEditCommand.get() == cmd)
785 ASSERT(cmd->isTypingCommand());
787 // Only register a new undo command if the command passed in is
788 // different from the last command
789 m_lastEditCommand = cmd;
791 client()->registerCommandForUndo(m_lastEditCommand);
793 respondToChangedContents(newSelection);
796 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
798 dispatchEditableContentChangedEvents(*cmd);
800 Selection newSelection(cmd->startingSelection());
801 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
802 // because there is work that it must do in this situation.
803 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
804 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
805 if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
806 m_frame->selectionController()->setSelection(newSelection, true);
808 m_lastEditCommand = 0;
810 client()->registerCommandForRedo(cmd);
811 respondToChangedContents(newSelection);
814 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
816 dispatchEditableContentChangedEvents(*cmd);
818 Selection newSelection(cmd->endingSelection());
819 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
820 // because there is work that it must do in this situation.
821 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
822 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
823 if (newSelection == m_frame->selectionController()->selection() || m_frame->shouldChangeSelection(newSelection))
824 m_frame->selectionController()->setSelection(newSelection, true);
826 m_lastEditCommand = 0;
828 client()->registerCommandForUndo(cmd);
829 respondToChangedContents(newSelection);
832 Editor::Editor(Frame* frame)
834 , m_deleteButtonController(new DeleteButtonController(frame))
835 , m_ignoreCompositionSelectionChange(false)
836 , m_shouldStartNewKillRingSequence(false)
846 m_compositionNode = 0;
847 m_customCompositionUnderlines.clear();
850 bool Editor::insertText(const String& text, Event* triggeringEvent)
852 return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
855 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
860 Selection selection = selectionForCommand(triggeringEvent);
861 if (!selection.isContentEditable())
863 RefPtr<Range> range = selection.toRange();
865 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
868 // Get the selection to use for the event that triggered this insertText.
869 // If the event handler changed the selection, we may want to use a different selection
870 // that is contained in the event target.
871 selection = selectionForCommand(triggeringEvent);
872 if (selection.isContentEditable()) {
873 if (Node* selectionStart = selection.start().node()) {
874 RefPtr<Document> document = selectionStart->document();
877 TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
879 // Reveal the current selection
880 if (Frame* editedFrame = document->frame())
881 if (Page* page = editedFrame->page())
882 page->focusController()->focusedOrMainFrame()->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
889 bool Editor::insertLineBreak()
894 if (!shouldInsertText("\n", m_frame->selectionController()->toRange().get(), EditorInsertActionTyped))
897 TypingCommand::insertLineBreak(m_frame->document());
898 revealSelectionAfterEditingOperation();
902 bool Editor::insertParagraphSeparator()
907 if (!canEditRichly())
908 return insertLineBreak();
910 if (!shouldInsertText("\n", m_frame->selectionController()->toRange().get(), EditorInsertActionTyped))
913 TypingCommand::insertParagraphSeparator(m_frame->document());
914 revealSelectionAfterEditingOperation();
921 return; // DHTML did the whole operation
926 RefPtr<Range> selection = selectedRange();
927 if (shouldDeleteRange(selection.get())) {
928 Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
929 didWriteSelectionToPasteboard();
930 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
937 return; // DHTML did the whole operation
943 Document* document = m_frame->document();
944 if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
945 Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
947 Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
949 didWriteSelectionToPasteboard();
956 ASSERT(m_frame->document());
958 return; // DHTML did the whole operation
961 DocLoader* loader = m_frame->document()->docLoader();
962 loader->setAllowStaleResources(true);
963 if (m_frame->selectionController()->isContentRichlyEditable())
964 pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
966 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
967 loader->setAllowStaleResources(false);
972 void Editor::pasteAsPlainText()
976 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
979 void Editor::performDelete()
986 addToKillRing(selectedRange().get(), false);
987 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
989 // clear the "start new kill ring sequence" setting, because it was set to true
990 // when the selection was updated by deleting the range
991 setStartNewKillRingSequence(false);
994 void Editor::copyURL(const KURL& url, const String& title)
996 Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
999 void Editor::copyImage(const HitTestResult& result)
1001 KURL url = result.absoluteLinkURL();
1003 url = result.absoluteImageURL();
1005 Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1008 bool Editor::isContinuousSpellCheckingEnabled()
1010 return client() && client()->isContinuousSpellCheckingEnabled();
1013 void Editor::toggleContinuousSpellChecking()
1016 client()->toggleContinuousSpellChecking();
1019 bool Editor::isGrammarCheckingEnabled()
1021 return client() && client()->isGrammarCheckingEnabled();
1024 void Editor::toggleGrammarChecking()
1027 client()->toggleGrammarChecking();
1030 int Editor::spellCheckerDocumentTag()
1032 return client() ? client()->spellCheckerDocumentTag() : 0;
1035 bool Editor::shouldEndEditing(Range* range)
1037 return client() && client()->shouldEndEditing(range);
1040 bool Editor::shouldBeginEditing(Range* range)
1042 return client() && client()->shouldBeginEditing(range);
1045 void Editor::clearUndoRedoOperations()
1048 client()->clearUndoRedoOperations();
1051 bool Editor::canUndo()
1053 return client() && client()->canUndo();
1062 bool Editor::canRedo()
1064 return client() && client()->canRedo();
1073 void Editor::didBeginEditing()
1076 client()->didBeginEditing();
1079 void Editor::didEndEditing()
1082 client()->didEndEditing();
1085 void Editor::didWriteSelectionToPasteboard()
1088 client()->didWriteSelectionToPasteboard();
1091 void Editor::toggleBold()
1093 command("ToggleBold").execute();
1096 void Editor::toggleUnderline()
1098 command("ToggleUnderline").execute();
1101 void Editor::setBaseWritingDirection(const String& direction)
1103 ExceptionCode ec = 0;
1105 RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration;
1106 style->setProperty(CSS_PROP_DIRECTION, direction, false, ec);
1107 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1110 void Editor::selectComposition()
1112 RefPtr<Range> range = compositionRange();
1116 // The composition can start inside a composed character sequence, so we have to override checks.
1117 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1118 Selection selection;
1119 selection.setWithoutValidation(range->startPosition(), range->endPosition());
1120 m_frame->selectionController()->setSelection(selection, false, false);
1123 void Editor::confirmComposition()
1125 if (!m_compositionNode)
1127 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1130 void Editor::confirmCompositionWithoutDisturbingSelection()
1132 if (!m_compositionNode)
1134 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1137 void Editor::confirmComposition(const String& text)
1139 confirmComposition(text, false);
1142 void Editor::confirmComposition(const String& text, bool preserveSelection)
1144 setIgnoreCompositionSelectionChange(true);
1146 Selection oldSelection = m_frame->selectionController()->selection();
1148 selectComposition();
1150 if (m_frame->selectionController()->isNone()) {
1151 setIgnoreCompositionSelectionChange(false);
1155 // If there is a composition to replace, remove it with a deletion that will be part of the
1156 // same Undo step as the next and previous insertions.
1157 TypingCommand::deleteSelection(m_frame->document(), false);
1159 m_compositionNode = 0;
1160 m_customCompositionUnderlines.clear();
1162 insertText(text, 0);
1164 if (preserveSelection)
1165 m_frame->selectionController()->setSelection(oldSelection, false, false);
1167 setIgnoreCompositionSelectionChange(false);
1170 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1172 setIgnoreCompositionSelectionChange(true);
1174 selectComposition();
1176 if (m_frame->selectionController()->isNone()) {
1177 setIgnoreCompositionSelectionChange(false);
1181 // If there is a composition to replace, remove it with a deletion that will be part of the
1182 // same Undo step as the next and previous insertions.
1183 TypingCommand::deleteSelection(m_frame->document(), false);
1185 m_compositionNode = 0;
1186 m_customCompositionUnderlines.clear();
1188 if (!text.isEmpty()) {
1189 TypingCommand::insertText(m_frame->document(), text, true, true);
1191 Node* baseNode = m_frame->selectionController()->base().node();
1192 unsigned baseOffset = m_frame->selectionController()->base().offset();
1193 Node* extentNode = m_frame->selectionController()->extent().node();
1194 unsigned extentOffset = m_frame->selectionController()->extent().offset();
1196 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1197 m_compositionNode = static_cast<Text*>(baseNode);
1198 m_compositionStart = baseOffset;
1199 m_compositionEnd = extentOffset;
1200 m_customCompositionUnderlines = underlines;
1201 size_t numUnderlines = m_customCompositionUnderlines.size();
1202 for (size_t i = 0; i < numUnderlines; ++i) {
1203 m_customCompositionUnderlines[i].startOffset += baseOffset;
1204 m_customCompositionUnderlines[i].endOffset += baseOffset;
1206 if (baseNode->renderer())
1207 baseNode->renderer()->repaint();
1209 unsigned start = min(baseOffset + selectionStart, extentOffset);
1210 unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1211 RefPtr<Range> selectedRange = new Range(baseNode->document(), baseNode, start, baseNode, end);
1212 m_frame->selectionController()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1216 setIgnoreCompositionSelectionChange(false);
1219 void Editor::ignoreSpelling()
1224 String text = frame()->selectedText();
1225 ASSERT(text.length() != 0);
1226 client()->ignoreWordInSpellDocument(text);
1229 void Editor::learnSpelling()
1234 String text = frame()->selectedText();
1235 ASSERT(text.length() != 0);
1236 client()->learnWord(text);
1239 static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll)
1241 ASSERT_ARG(client, client);
1242 ASSERT_ARG(searchRange, searchRange);
1244 WordAwareIterator it(searchRange);
1245 firstMisspellingOffset = 0;
1247 String firstMisspelling;
1248 int currentChunkOffset = 0;
1250 while (!it.atEnd()) {
1251 const UChar* chars = it.characters();
1252 int len = it.length();
1254 // Skip some work for one-space-char hunks
1255 if (!(len == 1 && chars[0] == ' ')) {
1257 int misspellingLocation = -1;
1258 int misspellingLength = 0;
1259 client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
1261 // 5490627 shows that there was some code path here where the String constructor below crashes.
1262 // We don't know exactly what combination of bad input caused this, so we're making this much
1263 // more robust against bad input on release builds.
1264 ASSERT(misspellingLength >= 0);
1265 ASSERT(misspellingLocation >= -1);
1266 ASSERT(misspellingLength == 0 || misspellingLocation >= 0);
1267 ASSERT(misspellingLocation < len);
1268 ASSERT(misspellingLength <= len);
1269 ASSERT(misspellingLocation + misspellingLength <= len);
1271 if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
1273 // Remember first-encountered misspelling and its offset
1274 if (!firstMisspelling) {
1275 firstMisspellingOffset = currentChunkOffset + misspellingLocation;
1276 firstMisspelling = String(chars + misspellingLocation, misspellingLength);
1279 // Mark this instance if we're marking all instances. Otherwise bail out because we found the first one.
1283 // Compute range of misspelled word
1284 RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
1286 // Store marker for misspelled word
1287 ExceptionCode ec = 0;
1288 misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1293 currentChunkOffset += len;
1297 return firstMisspelling;
1300 #ifndef BUILDING_ON_TIGER
1302 static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
1304 ASSERT_ARG(arbitraryRange, arbitraryRange);
1306 ExceptionCode ec = 0;
1308 // Expand range to paragraph boundaries
1309 RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
1310 setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
1311 setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
1313 // Compute offset from start of expanded range to start of original range
1314 RefPtr<Range> offsetAsRange = new Range(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
1315 offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
1317 // Fill in out parameter with string representing entire paragraph range.
1318 // Someday we might have a caller that doesn't use this, but for now all callers do.
1319 paragraphString = plainText(paragraphRange.get());
1321 return paragraphRange;
1324 static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int badGrammarPhraseLength, Range *searchRange, int startOffset, int endOffset, bool markAll)
1326 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1327 // Optionally add a DocumentMarker for each detail in the range.
1328 int earliestDetailLocationSoFar = -1;
1329 int earliestDetailIndex = -1;
1330 for (unsigned i = 0; i < grammarDetails.size(); i++) {
1331 const GrammarDetail* detail = &grammarDetails[i];
1332 ASSERT(detail->length > 0 && detail->location >= 0);
1334 int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
1336 // Skip this detail if it starts before the original search range
1337 if (detailStartOffsetInParagraph < startOffset)
1340 // Skip this detail if it starts after the original search range
1341 if (detailStartOffsetInParagraph >= endOffset)
1345 RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
1346 ExceptionCode ec = 0;
1347 badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
1351 // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
1352 if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
1353 earliestDetailIndex = i;
1354 earliestDetailLocationSoFar = detail->location;
1358 return earliestDetailIndex;
1361 static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
1363 ASSERT_ARG(client, client);
1364 ASSERT_ARG(searchRange, searchRange);
1366 // Initialize out parameters; these will be updated if we find something to return.
1367 outGrammarDetail.location = -1;
1368 outGrammarDetail.length = 0;
1369 outGrammarDetail.guesses.clear();
1370 outGrammarDetail.userDescription = "";
1371 outGrammarPhraseOffset = 0;
1373 String firstBadGrammarPhrase;
1375 // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
1376 // Determine the character offset from the start of the paragraph to the start of the original search range,
1377 // since we will want to ignore results in this area.
1378 int searchRangeStartOffset;
1379 String paragraphString;
1380 RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
1382 // Determine the character offset from the start of the paragraph to the end of the original search range,
1383 // since we will want to ignore results in this area also.
1384 int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
1386 // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
1387 int startOffset = 0;
1388 while (startOffset < searchRangeEndOffset) {
1389 Vector<GrammarDetail> grammarDetails;
1390 int badGrammarPhraseLocation = -1;
1391 int badGrammarPhraseLength = 0;
1392 client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
1394 if (badGrammarPhraseLength == 0) {
1395 ASSERT(badGrammarPhraseLocation == -1);
1399 ASSERT(badGrammarPhraseLocation >= 0);
1400 badGrammarPhraseLocation += startOffset;
1403 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1404 int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
1405 if (badGrammarIndex >= 0) {
1406 ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
1407 outGrammarDetail = grammarDetails[badGrammarIndex];
1410 // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
1411 // kept going so we could mark all instances).
1412 if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
1413 outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
1414 firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
1416 // Found one. We're done now, unless we're marking each instance.
1421 // These results were all between the start of the paragraph and the start of the search range; look
1422 // beyond this phrase.
1423 startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
1426 return firstBadGrammarPhrase;
1429 #endif /* not BUILDING_ON_TIGER */
1431 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1433 ExceptionCode ec = 0;
1435 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1436 // then we wrap and search from the doc start to (approximately) where we started.
1438 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
1439 // repeated "check spelling" commands work.
1440 Selection selection(frame()->selectionController()->selection());
1441 RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1442 bool startedWithSelection = false;
1443 if (selection.start().node()) {
1444 startedWithSelection = true;
1445 if (startBeforeSelection) {
1446 VisiblePosition start(selection.visibleStart());
1447 // We match AppKit's rule: Start 1 character before the selection.
1448 VisiblePosition oneBeforeStart = start.previous();
1449 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1451 setStart(spellingSearchRange.get(), selection.visibleEnd());
1454 Position position = spellingSearchRange->startPosition();
1455 if (!isEditablePosition(position)) {
1456 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1457 // selection is editable.
1458 // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1459 // when spell checking the whole document before sending the message.
1460 // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1462 position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1463 if (position.isNull())
1466 Position rangeCompliantPosition = rangeCompliantEquivalent(position);
1467 spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.offset(), ec);
1468 startedWithSelection = false; // won't need to wrap
1471 // topNode defines the whole range we want to operate on
1472 Node* topNode = highestEditableRoot(position);
1473 spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
1475 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1476 // at a word boundary. Going back by one char and then forward by a word does the trick.
1477 if (startedWithSelection) {
1478 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1479 if (oneBeforeStart.isNotNull()) {
1480 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1481 } // else we were already at the start of the editable node
1484 if (spellingSearchRange->collapsed(ec))
1485 return; // nothing to search in
1487 // Get the spell checker if it is available
1491 // We go to the end of our first range instead of the start of it, just to be sure
1492 // we don't get foiled by any word boundary problems at the start. It means we might
1493 // do a tiny bit more searching.
1494 Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1495 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1497 int misspellingOffset;
1498 String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
1500 String badGrammarPhrase;
1502 #ifndef BUILDING_ON_TIGER
1503 int grammarPhraseOffset = 0;
1504 GrammarDetail grammarDetail;
1506 // Search for bad grammar that occurs prior to the next misspelled word (if any)
1507 RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1508 if (!misspelledWord.isEmpty()) {
1509 // Stop looking at start of next misspelled word
1510 CharacterIterator chars(grammarSearchRange.get());
1511 chars.advance(misspellingOffset);
1512 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1515 if (isGrammarCheckingEnabled())
1516 badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1519 // 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
1520 // block rather than at a selection).
1521 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1522 spellingSearchRange->setStart(topNode, 0, ec);
1523 // going until the end of the very first chunk we tested is far enough
1524 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1526 misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
1528 #ifndef BUILDING_ON_TIGER
1529 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1530 if (!misspelledWord.isEmpty()) {
1531 // Stop looking at start of next misspelled word
1532 CharacterIterator chars(grammarSearchRange.get());
1533 chars.advance(misspellingOffset);
1534 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1536 if (isGrammarCheckingEnabled())
1537 badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1541 if (!badGrammarPhrase.isEmpty()) {
1542 #ifdef BUILDING_ON_TIGER
1543 ASSERT_NOT_REACHED();
1545 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1546 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1547 // panel, and store a marker so we draw the green squiggle later.
1549 ASSERT(badGrammarPhrase.length() > 0);
1550 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1552 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1553 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1554 frame()->selectionController()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1555 frame()->revealSelection();
1557 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1558 frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1560 } else if (!misspelledWord.isEmpty()) {
1561 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1562 // a marker so we draw the red squiggle later.
1564 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1565 frame()->selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
1566 frame()->revealSelection();
1568 client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1569 frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1573 bool Editor::isSelectionMisspelled()
1575 String selectedString = frame()->selectedText();
1576 int length = selectedString.length();
1583 int misspellingLocation = -1;
1584 int misspellingLength = 0;
1585 client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
1587 // The selection only counts as misspelled if the selected text is exactly one misspelled word
1588 if (misspellingLength != length)
1591 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1592 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1593 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1594 // or a grammar error.
1595 client()->updateSpellingUIWithMisspelledWord(selectedString);
1600 #ifndef BUILDING_ON_TIGER
1601 static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
1607 if (!range || range->collapsed(ec))
1610 // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
1611 // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
1612 // or overlapping the range; the ranges must exactly match.
1613 guessesVector.clear();
1614 int grammarPhraseOffset;
1616 GrammarDetail grammarDetail;
1617 String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);
1619 // No bad grammar in these parts at all.
1620 if (badGrammarPhrase.isEmpty())
1623 // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
1624 if (grammarPhraseOffset > 0)
1627 ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
1629 // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
1630 if (grammarDetail.location + grammarPhraseOffset != 0)
1633 // Bad grammar at start of range, but end of bad grammar is before or after end of range
1634 if (grammarDetail.length != TextIterator::rangeLength(range))
1637 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1638 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1639 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1640 // or a grammar error.
1641 client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1647 bool Editor::isSelectionUngrammatical()
1649 #ifdef BUILDING_ON_TIGER
1652 Vector<String> ignoredGuesses;
1653 return isRangeUngrammatical(client(), frame()->selectionController()->toRange().get(), ignoredGuesses);
1657 Vector<String> Editor::guessesForUngrammaticalSelection()
1659 #ifdef BUILDING_ON_TIGER
1660 return Vector<String>();
1662 Vector<String> guesses;
1663 // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
1664 isRangeUngrammatical(client(), frame()->selectionController()->toRange().get(), guesses);
1669 Vector<String> Editor::guessesForMisspelledSelection()
1671 String selectedString = frame()->selectedText();
1672 ASSERT(selectedString.length() != 0);
1674 Vector<String> guesses;
1676 client()->getGuessesForWord(selectedString, guesses);
1680 void Editor::showSpellingGuessPanel()
1683 LOG_ERROR("No NSSpellChecker");
1687 #ifndef BUILDING_ON_TIGER
1688 // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
1689 // to match rest of OS X.
1690 if (client()->spellingUIIsShowing()) {
1691 client()->showSpellingUI(false);
1696 advanceToNextMisspelling(true);
1697 client()->showSpellingUI(true);
1700 bool Editor::spellingPanelIsShowing()
1704 return client()->spellingUIIsShowing();
1707 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
1709 if (!isContinuousSpellCheckingEnabled())
1712 // Check spelling of one word
1713 markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
1715 if (!isGrammarCheckingEnabled())
1718 // Check grammar of entire sentence
1719 markBadGrammar(Selection(startOfSentence(p), endOfSentence(p)));
1722 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange)
1724 // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
1725 // all we need to do is mark every instance.
1727 findFirstMisspellingInRange(client, searchRange, ignoredOffset, true);
1730 #ifndef BUILDING_ON_TIGER
1731 static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
1733 // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
1734 // do is mark every instance.
1735 GrammarDetail ignoredGrammarDetail;
1737 findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
1741 static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& selection, bool checkSpelling)
1743 // This function is called with a selection already expanded to word boundaries.
1744 // Might be nice to assert that here.
1746 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
1747 // grammar checking can only be on if spell checking is also on.
1748 if (!editor->isContinuousSpellCheckingEnabled())
1751 RefPtr<Range> searchRange(selection.toRange());
1752 if (!searchRange || searchRange->isDetached())
1755 // If we're not in an editable node, bail.
1757 Node *editableNode = searchRange->startContainer(exception);
1758 if (!editableNode->isContentEditable())
1761 // Get the spell checker if it is available
1762 if (!editor->client())
1766 markAllMisspellingsInRange(editor->client(), searchRange.get());
1768 #ifdef BUILDING_ON_TIGER
1769 ASSERT_NOT_REACHED();
1771 if (editor->isGrammarCheckingEnabled())
1772 markAllBadGrammarInRange(editor->client(), searchRange.get());
1777 void Editor::markMisspellings(const Selection& selection)
1779 markMisspellingsOrBadGrammar(this, selection, true);
1782 void Editor::markBadGrammar(const Selection& selection)
1784 #ifndef BUILDING_ON_TIGER
1785 markMisspellingsOrBadGrammar(this, selection, false);
1789 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
1791 Document* document = m_frame->documentAtPoint(windowPoint);
1795 Frame* frame = document->frame();
1797 FrameView* frameView = frame->view();
1800 IntPoint framePoint = frameView->windowToContents(windowPoint);
1801 Selection selection(frame->visiblePositionForPoint(framePoint));
1802 return avoidIntersectionWithNode(selection.toRange().get(), deleteButtonController() ? deleteButtonController()->containerElement() : 0);
1805 void Editor::revealSelectionAfterEditingOperation()
1807 if (m_ignoreCompositionSelectionChange)
1810 m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
1813 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
1815 if (m_ignoreCompositionSelectionChange == ignore)
1818 m_ignoreCompositionSelectionChange = ignore;
1820 revealSelectionAfterEditingOperation();
1823 PassRefPtr<Range> Editor::compositionRange() const
1825 if (!m_compositionNode)
1827 unsigned length = m_compositionNode->length();
1828 unsigned start = min(m_compositionStart, length);
1829 unsigned end = min(max(start, m_compositionEnd), length);
1832 return new Range(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
1835 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
1837 if (!m_compositionNode)
1839 Position start = m_frame->selectionController()->start();
1840 if (start.node() != m_compositionNode)
1842 Position end = m_frame->selectionController()->end();
1843 if (end.node() != m_compositionNode)
1846 if (static_cast<unsigned>(start.offset()) < m_compositionStart)
1848 if (static_cast<unsigned>(end.offset()) > m_compositionEnd)
1851 selectionStart = start.offset() - m_compositionStart;
1852 selectionEnd = start.offset() - m_compositionEnd;
1856 void Editor::transpose()
1861 Selection selection = m_frame->selectionController()->selection();
1862 if (!selection.isCaret())
1865 // Make a selection that goes back one character and forward two characters.
1866 VisiblePosition caret = selection.visibleStart();
1867 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
1868 VisiblePosition previous = next.previous();
1869 if (next == previous)
1871 previous = previous.previous();
1872 if (!inSameParagraph(next, previous))
1874 RefPtr<Range> range = makeRange(previous, next);
1877 Selection newSelection(range.get(), DOWNSTREAM);
1879 // Transpose the two characters.
1880 String text = plainText(range.get());
1881 if (text.length() != 2)
1883 String transposed = text.right(1) + text.left(1);
1885 // Select the two characters.
1886 if (newSelection != m_frame->selectionController()->selection()) {
1887 if (!m_frame->shouldChangeSelection(newSelection))
1889 m_frame->selectionController()->setSelection(newSelection);
1892 // Insert the transposed characters.
1893 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
1895 replaceSelectionWithText(transposed, false, false);
1898 void Editor::addToKillRing(Range* range, bool prepend)
1900 if (m_shouldStartNewKillRingSequence)
1901 startNewKillRingSequence();
1903 String text = plainText(range);
1904 text.replace('\\', m_frame->backslashAsCurrencySymbol());
1906 prependToKillRing(text);
1908 appendToKillRing(text);
1909 m_shouldStartNewKillRingSequence = false;
1914 void Editor::appendToKillRing(const String&)
1918 void Editor::prependToKillRing(const String&)
1922 String Editor::yankFromKillRing()
1927 void Editor::startNewKillRingSequence()
1931 void Editor::setKillRingToYankedState()
1937 } // namespace WebCore