• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CSSProperty.h"
34 #include "CSSPropertyNames.h"
35 #include "CSSValueKeywords.h"
36 #include "ClipboardEvent.h"
37 #include "DeleteButtonController.h"
38 #include "DeleteSelectionCommand.h"
39 #include "DocLoader.h"
40 #include "DocumentFragment.h"
41 #include "EditorClient.h"
42 #include "EventHandler.h"
43 #include "EventNames.h"
44 #include "FocusController.h"
45 #include "Frame.h"
46 #include "FrameTree.h"
47 #include "FrameView.h"
48 #include "HTMLInputElement.h"
49 #include "HTMLTextAreaElement.h"
50 #include "HitTestResult.h"
51 #include "IndentOutdentCommand.h"
52 #include "InsertListCommand.h"
53 #include "KeyboardEvent.h"
54 #include "ModifySelectionListLevel.h"
55 #include "Page.h"
56 #include "Pasteboard.h"
57 #include "RemoveFormatCommand.h"
58 #include "RenderBlock.h"
59 #include "RenderPart.h"
60 #include "ReplaceSelectionCommand.h"
61 #include "Sound.h"
62 #include "Text.h"
63 #include "TextIterator.h"
64 #include "TypingCommand.h"
65 #include "htmlediting.h"
66 #include "markup.h"
67 #include "visible_units.h"
68 #include <wtf/UnusedParam.h>
69 
70 namespace WebCore {
71 
72 using namespace std;
73 using namespace HTMLNames;
74 
75 // When an event handler has moved the selection outside of a text control
76 // we should use the target control's selection for this editing operation.
selectionForCommand(Event * event)77 Selection Editor::selectionForCommand(Event* event)
78 {
79     Selection selection = m_frame->selection()->selection();
80     if (!event)
81         return selection;
82     // If the target is a text control, and the current selection is outside of its shadow tree,
83     // then use the saved selection for that text control.
84     Node* target = event->target()->toNode();
85     Node* selectionStart = selection.start().node();
86     if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
87         if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
88             return static_cast<HTMLInputElement*>(target)->selection();
89         if (target->hasTagName(textareaTag))
90             return static_cast<HTMLTextAreaElement*>(target)->selection();
91     }
92     return selection;
93 }
94 
client() const95 EditorClient* Editor::client() const
96 {
97     if (Page* page = m_frame->page())
98         return page->editorClient();
99     return 0;
100 }
101 
handleKeyboardEvent(KeyboardEvent * event)102 void Editor::handleKeyboardEvent(KeyboardEvent* event)
103 {
104     if (EditorClient* c = client())
105         c->handleKeyboardEvent(event);
106 }
107 
handleInputMethodKeydown(KeyboardEvent * event)108 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
109 {
110     if (EditorClient* c = client())
111         c->handleInputMethodKeydown(event);
112 }
113 
canEdit() const114 bool Editor::canEdit() const
115 {
116     return m_frame->selection()->isContentEditable();
117 }
118 
canEditRichly() const119 bool Editor::canEditRichly() const
120 {
121     return m_frame->selection()->isContentRichlyEditable();
122 }
123 
124 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
125 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
126 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
127 // normally selectable to implement copy/paste (like divs, or a document body).
128 
canDHTMLCut()129 bool Editor::canDHTMLCut()
130 {
131     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
132 }
133 
canDHTMLCopy()134 bool Editor::canDHTMLCopy()
135 {
136     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
137 }
138 
canDHTMLPaste()139 bool Editor::canDHTMLPaste()
140 {
141     return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
142 }
143 
canCut() const144 bool Editor::canCut() const
145 {
146     return canCopy() && canDelete();
147 }
148 
imageElementFromImageDocument(Document * document)149 static HTMLImageElement* imageElementFromImageDocument(Document* document)
150 {
151     if (!document)
152         return 0;
153     if (!document->isImageDocument())
154         return 0;
155 
156     HTMLElement* body = document->body();
157     if (!body)
158         return 0;
159 
160     Node* node = body->firstChild();
161     if (!node)
162         return 0;
163     if (!node->hasTagName(imgTag))
164         return 0;
165     return static_cast<HTMLImageElement*>(node);
166 }
167 
canCopy() const168 bool Editor::canCopy() const
169 {
170     if (imageElementFromImageDocument(m_frame->document()))
171         return true;
172     SelectionController* selection = m_frame->selection();
173     return selection->isRange() && !selection->isInPasswordField();
174 }
175 
canPaste() const176 bool Editor::canPaste() const
177 {
178     return canEdit();
179 }
180 
canDelete() const181 bool Editor::canDelete() const
182 {
183     SelectionController* selection = m_frame->selection();
184     return selection->isRange() && selection->isContentEditable();
185 }
186 
canDeleteRange(Range * range) const187 bool Editor::canDeleteRange(Range* range) const
188 {
189     ExceptionCode ec = 0;
190     Node* startContainer = range->startContainer(ec);
191     Node* endContainer = range->endContainer(ec);
192     if (!startContainer || !endContainer)
193         return false;
194 
195     if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
196         return false;
197 
198     if (range->collapsed(ec)) {
199         VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
200         VisiblePosition previous = start.previous();
201         // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
202         if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
203             return false;
204     }
205     return true;
206 }
207 
smartInsertDeleteEnabled()208 bool Editor::smartInsertDeleteEnabled()
209 {
210     return client() && client()->smartInsertDeleteEnabled();
211 }
212 
canSmartCopyOrDelete()213 bool Editor::canSmartCopyOrDelete()
214 {
215     return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
216 }
217 
isSelectTrailingWhitespaceEnabled()218 bool Editor::isSelectTrailingWhitespaceEnabled()
219 {
220     return client() && client()->isSelectTrailingWhitespaceEnabled();
221 }
222 
deleteWithDirection(SelectionController::EDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)223 bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
224 {
225     if (!canEdit() || !m_frame->document())
226         return false;
227 
228     if (m_frame->selection()->isRange()) {
229         if (isTypingAction) {
230             TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity);
231             revealSelectionAfterEditingOperation();
232         } else {
233             if (killRing)
234                 addToKillRing(selectedRange().get(), false);
235             deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
236             // Implicitly calls revealSelectionAfterEditingOperation().
237         }
238     } else {
239         switch (direction) {
240             case SelectionController::FORWARD:
241             case SelectionController::RIGHT:
242                 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
243                 break;
244             case SelectionController::BACKWARD:
245             case SelectionController::LEFT:
246                 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
247                 break;
248         }
249         revealSelectionAfterEditingOperation();
250     }
251 
252     // FIXME: We should to move this down into deleteKeyPressed.
253     // clear the "start new kill ring sequence" setting, because it was set to true
254     // when the selection was updated by deleting the range
255     if (killRing)
256         setStartNewKillRingSequence(false);
257 
258     return true;
259 }
260 
deleteSelectionWithSmartDelete(bool smartDelete)261 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
262 {
263     if (m_frame->selection()->isNone())
264         return;
265 
266     applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
267 }
268 
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)269 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
270 {
271     String text = pasteboard->plainText(m_frame);
272     if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
273         replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
274 }
275 
pasteWithPasteboard(Pasteboard * pasteboard,bool allowPlainText)276 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
277 {
278     RefPtr<Range> range = selectedRange();
279     bool chosePlainText;
280     RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
281     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
282         replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
283 }
284 
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)285 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
286 {
287     return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
288 }
289 
shouldInsertFragment(PassRefPtr<DocumentFragment> fragment,PassRefPtr<Range> replacingDOMRange,EditorInsertAction givenAction)290 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
291 {
292     if (!client())
293         return false;
294 
295     Node* child = fragment->firstChild();
296     if (child && fragment->lastChild() == child && child->isCharacterDataNode())
297         return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
298 
299     return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
300 }
301 
replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)302 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
303 {
304     if (m_frame->selection()->isNone() || !fragment)
305         return;
306 
307     applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle));
308     revealSelectionAfterEditingOperation();
309 }
310 
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)311 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
312 {
313     replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
314 }
315 
selectedRange()316 PassRefPtr<Range> Editor::selectedRange()
317 {
318     if (!m_frame)
319         return 0;
320     return m_frame->selection()->toRange();
321 }
322 
shouldDeleteRange(Range * range) const323 bool Editor::shouldDeleteRange(Range* range) const
324 {
325     ExceptionCode ec;
326     if (!range || range->collapsed(ec))
327         return false;
328 
329     if (!canDeleteRange(range))
330         return false;
331 
332     return client() && client()->shouldDeleteRange(range);
333 }
334 
tryDHTMLCopy()335 bool Editor::tryDHTMLCopy()
336 {
337     if (m_frame->selection()->isInPasswordField())
338         return false;
339 
340     // Must be done before oncopy adds types and data to the pboard,
341     // also done for security, as it erases data from the last copy/paste.
342     Pasteboard::generalPasteboard()->clear();
343 
344     return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
345 }
346 
tryDHTMLCut()347 bool Editor::tryDHTMLCut()
348 {
349     if (m_frame->selection()->isInPasswordField())
350         return false;
351 
352     // Must be done before oncut adds types and data to the pboard,
353     // also done for security, as it erases data from the last copy/paste.
354     Pasteboard::generalPasteboard()->clear();
355 
356     return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
357 }
358 
tryDHTMLPaste()359 bool Editor::tryDHTMLPaste()
360 {
361     return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
362 }
363 
writeSelectionToPasteboard(Pasteboard * pasteboard)364 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
365 {
366     pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
367 }
368 
shouldInsertText(const String & text,Range * range,EditorInsertAction action) const369 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
370 {
371     return client() && client()->shouldInsertText(text, range, action);
372 }
373 
shouldShowDeleteInterface(HTMLElement * element) const374 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
375 {
376     return client() && client()->shouldShowDeleteInterface(element);
377 }
378 
respondToChangedSelection(const Selection & oldSelection)379 void Editor::respondToChangedSelection(const Selection& oldSelection)
380 {
381     if (client())
382         client()->respondToChangedSelection();
383     m_deleteButtonController->respondToChangedSelection(oldSelection);
384 }
385 
respondToChangedContents(const Selection & endingSelection)386 void Editor::respondToChangedContents(const Selection& endingSelection)
387 {
388     if (AXObjectCache::accessibilityEnabled()) {
389         Node* node = endingSelection.start().node();
390         if (node)
391             m_frame->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
392     }
393 
394     if (client())
395         client()->respondToChangedContents();
396 }
397 
fontForSelection(bool & hasMultipleFonts) const398 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
399 {
400 #if !PLATFORM(QT)
401     hasMultipleFonts = false;
402 
403     if (!m_frame->selection()->isRange()) {
404         Node* nodeToRemove;
405         RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove); // sets nodeToRemove
406 
407         const SimpleFontData* result = 0;
408         if (style)
409             result = style->font().primaryFont();
410 
411         if (nodeToRemove) {
412             ExceptionCode ec;
413             nodeToRemove->remove(ec);
414             ASSERT(ec == 0);
415         }
416 
417         return result;
418     }
419 
420     const SimpleFontData* font = 0;
421 
422     RefPtr<Range> range = m_frame->selection()->toRange();
423     Node* startNode = range->editingStartPosition().node();
424     if (startNode) {
425         Node* pastEnd = range->pastLastNode();
426         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
427         // unreproducible case where this didn't happen, so check for nil also.
428         for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
429             RenderObject *renderer = n->renderer();
430             if (!renderer)
431                 continue;
432             // FIXME: Are there any node types that have renderers, but that we should be skipping?
433             const SimpleFontData* f = renderer->style()->font().primaryFont();
434             if (!font)
435                 font = f;
436             else if (font != f) {
437                 hasMultipleFonts = true;
438                 break;
439             }
440         }
441     }
442 
443     return font;
444 #else
445     return 0;
446 #endif
447 }
448 
textDirectionForSelection(bool & hasNestedOrMultipleEmbeddings) const449 WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const
450 {
451     hasNestedOrMultipleEmbeddings = true;
452 
453     if (m_frame->selection()->isNone())
454         return NaturalWritingDirection;
455 
456     Position pos = m_frame->selection()->selection().start().downstream();
457 
458     Node* node = pos.node();
459     if (!node)
460         return NaturalWritingDirection;
461 
462     Position end;
463     if (m_frame->selection()->isRange()) {
464         end = m_frame->selection()->selection().end().upstream();
465 
466         Node* pastLast = Range::create(m_frame->document(), rangeCompliantEquivalent(pos), rangeCompliantEquivalent(end))->pastLastNode();
467         for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
468             if (!n->isStyledElement())
469                 continue;
470 
471             RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n);
472             RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
473             if (!unicodeBidi)
474                 continue;
475 
476             ASSERT(unicodeBidi->isPrimitiveValue());
477             int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
478             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
479                 return NaturalWritingDirection;
480         }
481     }
482 
483     if (m_frame->selection()->isCaret()) {
484         if (CSSMutableStyleDeclaration *typingStyle = m_frame->typingStyle()) {
485             RefPtr<CSSValue> unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
486             if (unicodeBidi) {
487                 ASSERT(unicodeBidi->isPrimitiveValue());
488                 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
489                 if (unicodeBidiValue == CSSValueEmbed) {
490                     RefPtr<CSSValue> direction = typingStyle->getPropertyCSSValue(CSSPropertyDirection);
491                     ASSERT(!direction || direction->isPrimitiveValue());
492                     if (direction) {
493                         hasNestedOrMultipleEmbeddings = false;
494                         return static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
495                     }
496                 } else if (unicodeBidiValue == CSSValueNormal) {
497                     hasNestedOrMultipleEmbeddings = false;
498                     return NaturalWritingDirection;
499                 }
500             }
501         }
502         node = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
503     }
504 
505     // 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
506     // to decide.
507     Node* block = enclosingBlock(node);
508     WritingDirection foundDirection = NaturalWritingDirection;
509 
510     for (; node != block; node = node->parent()) {
511         if (!node->isStyledElement())
512             continue;
513 
514         RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
515         RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
516         if (!unicodeBidi)
517             continue;
518 
519         ASSERT(unicodeBidi->isPrimitiveValue());
520         int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
521         if (unicodeBidiValue == CSSValueNormal)
522             continue;
523 
524         if (unicodeBidiValue == CSSValueBidiOverride)
525             return NaturalWritingDirection;
526 
527         ASSERT(unicodeBidiValue == CSSValueEmbed);
528         RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
529         if (!direction)
530             continue;
531 
532         ASSERT(direction->isPrimitiveValue());
533         int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
534         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
535             continue;
536 
537         if (foundDirection != NaturalWritingDirection)
538             return NaturalWritingDirection;
539 
540         // In the range case, make sure that the embedding element persists until the end of the range.
541         if (m_frame->selection()->isRange() && !end.node()->isDescendantOf(node))
542             return NaturalWritingDirection;
543 
544         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
545     }
546     hasNestedOrMultipleEmbeddings = false;
547     return foundDirection;
548 }
549 
hasBidiSelection() const550 bool Editor::hasBidiSelection() const
551 {
552     if (m_frame->selection()->isNone())
553         return false;
554 
555     Node* startNode;
556     if (m_frame->selection()->isRange()) {
557         startNode = m_frame->selection()->selection().start().downstream().node();
558         Node* endNode = m_frame->selection()->selection().end().upstream().node();
559         if (enclosingBlock(startNode) != enclosingBlock(endNode))
560             return false;
561     } else
562         startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
563 
564     RenderObject* renderer = startNode->renderer();
565     while (renderer && !renderer->isRenderBlock())
566         renderer = renderer->parent();
567 
568     if (!renderer)
569         return false;
570 
571     RenderStyle* style = renderer->style();
572     if (style->direction() == RTL)
573         return true;
574 
575     return static_cast<RenderBlock*>(renderer)->containsNonZeroBidiLevel();
576 }
577 
selectionUnorderedListState() const578 TriState Editor::selectionUnorderedListState() const
579 {
580     if (m_frame->selection()->isCaret()) {
581         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag))
582             return TrueTriState;
583     } else if (m_frame->selection()->isRange()) {
584         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag);
585         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag);
586         if (startNode && endNode && startNode == endNode)
587             return TrueTriState;
588     }
589 
590     return FalseTriState;
591 }
592 
selectionOrderedListState() const593 TriState Editor::selectionOrderedListState() const
594 {
595     if (m_frame->selection()->isCaret()) {
596         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag))
597             return TrueTriState;
598     } else if (m_frame->selection()->isRange()) {
599         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag);
600         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag);
601         if (startNode && endNode && startNode == endNode)
602             return TrueTriState;
603     }
604 
605     return FalseTriState;
606 }
607 
insertOrderedList()608 PassRefPtr<Node> Editor::insertOrderedList()
609 {
610     if (!canEditRichly())
611         return 0;
612 
613     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
614     revealSelectionAfterEditingOperation();
615     return newList;
616 }
617 
insertUnorderedList()618 PassRefPtr<Node> Editor::insertUnorderedList()
619 {
620     if (!canEditRichly())
621         return 0;
622 
623     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
624     revealSelectionAfterEditingOperation();
625     return newList;
626 }
627 
canIncreaseSelectionListLevel()628 bool Editor::canIncreaseSelectionListLevel()
629 {
630     return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
631 }
632 
canDecreaseSelectionListLevel()633 bool Editor::canDecreaseSelectionListLevel()
634 {
635     return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
636 }
637 
increaseSelectionListLevel()638 PassRefPtr<Node> Editor::increaseSelectionListLevel()
639 {
640     if (!canEditRichly() || m_frame->selection()->isNone())
641         return 0;
642 
643     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
644     revealSelectionAfterEditingOperation();
645     return newList;
646 }
647 
increaseSelectionListLevelOrdered()648 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
649 {
650     if (!canEditRichly() || m_frame->selection()->isNone())
651         return 0;
652 
653     PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
654     revealSelectionAfterEditingOperation();
655     return newList;
656 }
657 
increaseSelectionListLevelUnordered()658 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
659 {
660     if (!canEditRichly() || m_frame->selection()->isNone())
661         return 0;
662 
663     PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
664     revealSelectionAfterEditingOperation();
665     return newList;
666 }
667 
decreaseSelectionListLevel()668 void Editor::decreaseSelectionListLevel()
669 {
670     if (!canEditRichly() || m_frame->selection()->isNone())
671         return;
672 
673     DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
674     revealSelectionAfterEditingOperation();
675 }
676 
removeFormattingAndStyle()677 void Editor::removeFormattingAndStyle()
678 {
679     applyCommand(RemoveFormatCommand::create(m_frame->document()));
680 }
681 
clearLastEditCommand()682 void Editor::clearLastEditCommand()
683 {
684     m_lastEditCommand.clear();
685 }
686 
687 // Returns whether caller should continue with "the default processing", which is the same as
688 // the event handler NOT setting the return value to false
dispatchCPPEvent(const AtomicString & eventType,ClipboardAccessPolicy policy)689 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
690 {
691     Node* target = m_frame->selection()->start().element();
692     if (!target && m_frame->document())
693         target = m_frame->document()->body();
694     if (!target)
695         return true;
696     target = target->shadowAncestorNode();
697 
698     RefPtr<Clipboard> clipboard = newGeneralClipboard(policy);
699 
700     ExceptionCode ec = 0;
701     RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
702     EventTargetNodeCast(target)->dispatchEvent(evt, ec);
703     bool noDefaultProcessing = evt->defaultPrevented();
704 
705     // invalidate clipboard here for security
706     clipboard->setAccessPolicy(ClipboardNumb);
707 
708     return !noDefaultProcessing;
709 }
710 
applyStyle(CSSStyleDeclaration * style,EditAction editingAction)711 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
712 {
713     switch (m_frame->selection()->state()) {
714         case Selection::NONE:
715             // do nothing
716             break;
717         case Selection::CARET:
718             m_frame->computeAndSetTypingStyle(style, editingAction);
719             break;
720         case Selection::RANGE:
721             if (m_frame->document() && style)
722                 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction));
723             break;
724     }
725 }
726 
shouldApplyStyle(CSSStyleDeclaration * style,Range * range)727 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
728 {
729     return client()->shouldApplyStyle(style, range);
730 }
731 
applyParagraphStyle(CSSStyleDeclaration * style,EditAction editingAction)732 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
733 {
734     switch (m_frame->selection()->state()) {
735         case Selection::NONE:
736             // do nothing
737             break;
738         case Selection::CARET:
739         case Selection::RANGE:
740             if (m_frame->document() && style)
741                 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
742             break;
743     }
744 }
745 
applyStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)746 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
747 {
748     if (!style || style->length() == 0 || !canEditRichly())
749         return;
750 
751     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get()))
752         applyStyle(style, editingAction);
753 }
754 
applyParagraphStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)755 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
756 {
757     if (!style || style->length() == 0 || !canEditRichly())
758         return;
759 
760     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get()))
761         applyParagraphStyle(style, editingAction);
762 }
763 
clientIsEditable() const764 bool Editor::clientIsEditable() const
765 {
766     return client() && client()->isEditable();
767 }
768 
selectionStartHasStyle(CSSStyleDeclaration * style) const769 bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const
770 {
771     Node* nodeToRemove;
772     RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
773     if (!selectionStyle)
774         return false;
775 
776     RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
777 
778     bool match = true;
779     CSSMutableStyleDeclaration::const_iterator end = mutableStyle->end();
780     for (CSSMutableStyleDeclaration::const_iterator it = mutableStyle->begin(); it != end; ++it) {
781         int propertyID = (*it).id();
782         if (!equalIgnoringCase(mutableStyle->getPropertyValue(propertyID), selectionStyle->getPropertyValue(propertyID))) {
783             match = false;
784             break;
785         }
786     }
787 
788     if (nodeToRemove) {
789         ExceptionCode ec = 0;
790         nodeToRemove->remove(ec);
791         ASSERT(ec == 0);
792     }
793 
794     return match;
795 }
796 
updateState(CSSMutableStyleDeclaration * desiredStyle,CSSComputedStyleDeclaration * computedStyle,bool & atStart,TriState & state)797 static void updateState(CSSMutableStyleDeclaration* desiredStyle, CSSComputedStyleDeclaration* computedStyle, bool& atStart, TriState& state)
798 {
799     CSSMutableStyleDeclaration::const_iterator end = desiredStyle->end();
800     for (CSSMutableStyleDeclaration::const_iterator it = desiredStyle->begin(); it != end; ++it) {
801         int propertyID = (*it).id();
802         String desiredProperty = desiredStyle->getPropertyValue(propertyID);
803         String computedProperty = computedStyle->getPropertyValue(propertyID);
804         TriState propertyState = equalIgnoringCase(desiredProperty, computedProperty)
805             ? TrueTriState : FalseTriState;
806         if (atStart) {
807             state = propertyState;
808             atStart = false;
809         } else if (state != propertyState) {
810             state = MixedTriState;
811             break;
812         }
813     }
814 }
815 
selectionHasStyle(CSSStyleDeclaration * style) const816 TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const
817 {
818     bool atStart = true;
819     TriState state = FalseTriState;
820 
821     RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
822 
823     if (!m_frame->selection()->isRange()) {
824         Node* nodeToRemove;
825         RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
826         if (!selectionStyle)
827             return FalseTriState;
828         updateState(mutableStyle.get(), selectionStyle.get(), atStart, state);
829         if (nodeToRemove) {
830             ExceptionCode ec = 0;
831             nodeToRemove->remove(ec);
832             ASSERT(ec == 0);
833         }
834     } else {
835         for (Node* node = m_frame->selection()->start().node(); node; node = node->traverseNextNode()) {
836             RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
837             if (nodeStyle)
838                 updateState(mutableStyle.get(), nodeStyle.get(), atStart, state);
839             if (state == MixedTriState)
840                 break;
841             if (node == m_frame->selection()->end().node())
842                 break;
843         }
844     }
845 
846     return state;
847 }
indent()848 void Editor::indent()
849 {
850     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
851 }
852 
outdent()853 void Editor::outdent()
854 {
855     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
856 }
857 
dispatchEditableContentChangedEvents(const EditCommand & command)858 static void dispatchEditableContentChangedEvents(const EditCommand& command)
859 {
860     Element* startRoot = command.startingRootEditableElement();
861     Element* endRoot = command.endingRootEditableElement();
862     ExceptionCode ec;
863     if (startRoot)
864         startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
865     if (endRoot && endRoot != startRoot)
866         endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
867 }
868 
appliedEditing(PassRefPtr<EditCommand> cmd)869 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
870 {
871     dispatchEditableContentChangedEvents(*cmd);
872 
873     Selection newSelection(cmd->endingSelection());
874     // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
875     // because there is work that it must do in this situation.
876     // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
877     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
878     // Don't clear the typing style with this selection change.  We do those things elsewhere if necessary.
879     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
880         m_frame->selection()->setSelection(newSelection, false, false);
881 
882     if (!cmd->preservesTypingStyle())
883         m_frame->setTypingStyle(0);
884 
885     // Command will be equal to last edit command only in the case of typing
886     if (m_lastEditCommand.get() == cmd)
887         ASSERT(cmd->isTypingCommand());
888     else {
889         // Only register a new undo command if the command passed in is
890         // different from the last command
891         m_lastEditCommand = cmd;
892         if (client())
893             client()->registerCommandForUndo(m_lastEditCommand);
894     }
895     respondToChangedContents(newSelection);
896 }
897 
unappliedEditing(PassRefPtr<EditCommand> cmd)898 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
899 {
900     dispatchEditableContentChangedEvents(*cmd);
901 
902     Selection newSelection(cmd->startingSelection());
903     // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
904     // because there is work that it must do in this situation.
905     // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
906     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
907     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
908         m_frame->selection()->setSelection(newSelection, true);
909 
910     m_lastEditCommand = 0;
911     if (client())
912         client()->registerCommandForRedo(cmd);
913     respondToChangedContents(newSelection);
914 }
915 
reappliedEditing(PassRefPtr<EditCommand> cmd)916 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
917 {
918     dispatchEditableContentChangedEvents(*cmd);
919 
920     Selection newSelection(cmd->endingSelection());
921     // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
922     // because there is work that it must do in this situation.
923     // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
924     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
925     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
926         m_frame->selection()->setSelection(newSelection, true);
927 
928     m_lastEditCommand = 0;
929     if (client())
930         client()->registerCommandForUndo(cmd);
931     respondToChangedContents(newSelection);
932 }
933 
Editor(Frame * frame)934 Editor::Editor(Frame* frame)
935     : m_frame(frame)
936     , m_deleteButtonController(new DeleteButtonController(frame))
937     , m_ignoreCompositionSelectionChange(false)
938     , m_shouldStartNewKillRingSequence(false)
939 {
940 }
941 
~Editor()942 Editor::~Editor()
943 {
944 }
945 
clear()946 void Editor::clear()
947 {
948     m_compositionNode = 0;
949     m_customCompositionUnderlines.clear();
950 }
951 
insertText(const String & text,Event * triggeringEvent)952 bool Editor::insertText(const String& text, Event* triggeringEvent)
953 {
954     return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
955 }
956 
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,Event * triggeringEvent)957 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
958 {
959     if (text.isEmpty())
960         return false;
961 
962     Selection selection = selectionForCommand(triggeringEvent);
963     if (!selection.isContentEditable())
964         return false;
965     RefPtr<Range> range = selection.toRange();
966 
967     if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
968         return true;
969 
970     // Get the selection to use for the event that triggered this insertText.
971     // If the event handler changed the selection, we may want to use a different selection
972     // that is contained in the event target.
973     selection = selectionForCommand(triggeringEvent);
974     if (selection.isContentEditable()) {
975         if (Node* selectionStart = selection.start().node()) {
976             RefPtr<Document> document = selectionStart->document();
977 
978             // Insert the text
979             TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
980 
981             // Reveal the current selection
982             if (Frame* editedFrame = document->frame())
983                 if (Page* page = editedFrame->page())
984                     page->focusController()->focusedOrMainFrame()->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
985         }
986     }
987 
988     return true;
989 }
990 
insertLineBreak()991 bool Editor::insertLineBreak()
992 {
993     if (!canEdit())
994         return false;
995 
996     if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped))
997         return true;
998 
999     TypingCommand::insertLineBreak(m_frame->document());
1000     revealSelectionAfterEditingOperation();
1001     return true;
1002 }
1003 
insertParagraphSeparator()1004 bool Editor::insertParagraphSeparator()
1005 {
1006     if (!canEdit())
1007         return false;
1008 
1009     if (!canEditRichly())
1010         return insertLineBreak();
1011 
1012     if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped))
1013         return true;
1014 
1015     TypingCommand::insertParagraphSeparator(m_frame->document());
1016     revealSelectionAfterEditingOperation();
1017     return true;
1018 }
1019 
cut()1020 void Editor::cut()
1021 {
1022     if (tryDHTMLCut())
1023         return; // DHTML did the whole operation
1024     if (!canCut()) {
1025         systemBeep();
1026         return;
1027     }
1028     RefPtr<Range> selection = selectedRange();
1029     if (shouldDeleteRange(selection.get())) {
1030         Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
1031         didWriteSelectionToPasteboard();
1032         deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1033     }
1034 }
1035 
copy()1036 void Editor::copy()
1037 {
1038     if (tryDHTMLCopy())
1039         return; // DHTML did the whole operation
1040     if (!canCopy()) {
1041         systemBeep();
1042         return;
1043     }
1044 
1045     Document* document = m_frame->document();
1046     if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
1047         Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
1048     else
1049         Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
1050 
1051     didWriteSelectionToPasteboard();
1052 }
1053 
1054 #if !PLATFORM(MAC)
1055 
paste()1056 void Editor::paste()
1057 {
1058     ASSERT(m_frame->document());
1059     if (tryDHTMLPaste())
1060         return;     // DHTML did the whole operation
1061     if (!canPaste())
1062         return;
1063     DocLoader* loader = m_frame->document()->docLoader();
1064     loader->setAllowStaleResources(true);
1065     if (m_frame->selection()->isContentRichlyEditable())
1066         pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
1067     else
1068         pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1069     loader->setAllowStaleResources(false);
1070 }
1071 
1072 #endif
1073 
pasteAsPlainText()1074 void Editor::pasteAsPlainText()
1075 {
1076    if (!canPaste())
1077         return;
1078    pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1079 }
1080 
performDelete()1081 void Editor::performDelete()
1082 {
1083     if (!canDelete()) {
1084         systemBeep();
1085         return;
1086     }
1087 
1088     addToKillRing(selectedRange().get(), false);
1089     deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1090 
1091     // clear the "start new kill ring sequence" setting, because it was set to true
1092     // when the selection was updated by deleting the range
1093     setStartNewKillRingSequence(false);
1094 }
1095 
copyURL(const KURL & url,const String & title)1096 void Editor::copyURL(const KURL& url, const String& title)
1097 {
1098     Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1099 }
1100 
copyImage(const HitTestResult & result)1101 void Editor::copyImage(const HitTestResult& result)
1102 {
1103     KURL url = result.absoluteLinkURL();
1104     if (url.isEmpty())
1105         url = result.absoluteImageURL();
1106 
1107     Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1108 }
1109 
isContinuousSpellCheckingEnabled()1110 bool Editor::isContinuousSpellCheckingEnabled()
1111 {
1112     return client() && client()->isContinuousSpellCheckingEnabled();
1113 }
1114 
toggleContinuousSpellChecking()1115 void Editor::toggleContinuousSpellChecking()
1116 {
1117     if (client())
1118         client()->toggleContinuousSpellChecking();
1119 }
1120 
isGrammarCheckingEnabled()1121 bool Editor::isGrammarCheckingEnabled()
1122 {
1123     return client() && client()->isGrammarCheckingEnabled();
1124 }
1125 
toggleGrammarChecking()1126 void Editor::toggleGrammarChecking()
1127 {
1128     if (client())
1129         client()->toggleGrammarChecking();
1130 }
1131 
spellCheckerDocumentTag()1132 int Editor::spellCheckerDocumentTag()
1133 {
1134     return client() ? client()->spellCheckerDocumentTag() : 0;
1135 }
1136 
shouldEndEditing(Range * range)1137 bool Editor::shouldEndEditing(Range* range)
1138 {
1139     return client() && client()->shouldEndEditing(range);
1140 }
1141 
shouldBeginEditing(Range * range)1142 bool Editor::shouldBeginEditing(Range* range)
1143 {
1144     return client() && client()->shouldBeginEditing(range);
1145 }
1146 
clearUndoRedoOperations()1147 void Editor::clearUndoRedoOperations()
1148 {
1149     if (client())
1150         client()->clearUndoRedoOperations();
1151 }
1152 
canUndo()1153 bool Editor::canUndo()
1154 {
1155     return client() && client()->canUndo();
1156 }
1157 
undo()1158 void Editor::undo()
1159 {
1160     if (client())
1161         client()->undo();
1162 }
1163 
canRedo()1164 bool Editor::canRedo()
1165 {
1166     return client() && client()->canRedo();
1167 }
1168 
redo()1169 void Editor::redo()
1170 {
1171     if (client())
1172         client()->redo();
1173 }
1174 
didBeginEditing()1175 void Editor::didBeginEditing()
1176 {
1177     if (client())
1178         client()->didBeginEditing();
1179 }
1180 
didEndEditing()1181 void Editor::didEndEditing()
1182 {
1183     if (client())
1184         client()->didEndEditing();
1185 }
1186 
didWriteSelectionToPasteboard()1187 void Editor::didWriteSelectionToPasteboard()
1188 {
1189     if (client())
1190         client()->didWriteSelectionToPasteboard();
1191 }
1192 
toggleBold()1193 void Editor::toggleBold()
1194 {
1195     command("ToggleBold").execute();
1196 }
1197 
toggleUnderline()1198 void Editor::toggleUnderline()
1199 {
1200     command("ToggleUnderline").execute();
1201 }
1202 
setBaseWritingDirection(WritingDirection direction)1203 void Editor::setBaseWritingDirection(WritingDirection direction)
1204 {
1205     Node* focusedNode = frame()->document()->focusedNode();
1206     if (focusedNode && (focusedNode->hasTagName(textareaTag)
1207                         || focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT
1208                                                                 || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH))) {
1209         if (direction == NaturalWritingDirection)
1210             return;
1211         static_cast<HTMLElement*>(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1212         frame()->document()->updateRendering();
1213         return;
1214     }
1215 
1216     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
1217     style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1218     applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1219 }
1220 
selectComposition()1221 void Editor::selectComposition()
1222 {
1223     RefPtr<Range> range = compositionRange();
1224     if (!range)
1225         return;
1226 
1227     // The composition can start inside a composed character sequence, so we have to override checks.
1228     // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1229     Selection selection;
1230     selection.setWithoutValidation(range->startPosition(), range->endPosition());
1231     m_frame->selection()->setSelection(selection, false, false);
1232 }
1233 
confirmComposition()1234 void Editor::confirmComposition()
1235 {
1236     if (!m_compositionNode)
1237         return;
1238     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1239 }
1240 
confirmCompositionWithoutDisturbingSelection()1241 void Editor::confirmCompositionWithoutDisturbingSelection()
1242 {
1243     if (!m_compositionNode)
1244         return;
1245     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1246 }
1247 
confirmComposition(const String & text)1248 void Editor::confirmComposition(const String& text)
1249 {
1250     confirmComposition(text, false);
1251 }
1252 
confirmComposition(const String & text,bool preserveSelection)1253 void Editor::confirmComposition(const String& text, bool preserveSelection)
1254 {
1255     setIgnoreCompositionSelectionChange(true);
1256 
1257     Selection oldSelection = m_frame->selection()->selection();
1258 
1259     selectComposition();
1260 
1261     if (m_frame->selection()->isNone()) {
1262         setIgnoreCompositionSelectionChange(false);
1263         return;
1264     }
1265 
1266     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1267     // will delete the old composition with an optimized replace operation.
1268     if (text.isEmpty())
1269         TypingCommand::deleteSelection(m_frame->document(), false);
1270 
1271     m_compositionNode = 0;
1272     m_customCompositionUnderlines.clear();
1273 
1274     insertText(text, 0);
1275 
1276     if (preserveSelection)
1277         m_frame->selection()->setSelection(oldSelection, false, false);
1278 
1279     setIgnoreCompositionSelectionChange(false);
1280 }
1281 
setComposition(const String & text,const Vector<CompositionUnderline> & underlines,unsigned selectionStart,unsigned selectionEnd)1282 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1283 {
1284     setIgnoreCompositionSelectionChange(true);
1285 
1286     selectComposition();
1287 
1288     if (m_frame->selection()->isNone()) {
1289         setIgnoreCompositionSelectionChange(false);
1290         return;
1291     }
1292 
1293     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1294     // will delete the old composition with an optimized replace operation.
1295     if (text.isEmpty())
1296         TypingCommand::deleteSelection(m_frame->document(), false);
1297 
1298     m_compositionNode = 0;
1299     m_customCompositionUnderlines.clear();
1300 
1301     if (!text.isEmpty()) {
1302         TypingCommand::insertText(m_frame->document(), text, true, true);
1303 
1304         Node* baseNode = m_frame->selection()->base().node();
1305         unsigned baseOffset = m_frame->selection()->base().offset();
1306         Node* extentNode = m_frame->selection()->extent().node();
1307         unsigned extentOffset = m_frame->selection()->extent().offset();
1308 
1309         if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1310             m_compositionNode = static_cast<Text*>(baseNode);
1311             m_compositionStart = baseOffset;
1312             m_compositionEnd = extentOffset;
1313             m_customCompositionUnderlines = underlines;
1314             size_t numUnderlines = m_customCompositionUnderlines.size();
1315             for (size_t i = 0; i < numUnderlines; ++i) {
1316                 m_customCompositionUnderlines[i].startOffset += baseOffset;
1317                 m_customCompositionUnderlines[i].endOffset += baseOffset;
1318             }
1319             if (baseNode->renderer())
1320                 baseNode->renderer()->repaint();
1321 
1322             unsigned start = min(baseOffset + selectionStart, extentOffset);
1323             unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1324             RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1325             m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1326         }
1327     }
1328 
1329     setIgnoreCompositionSelectionChange(false);
1330 }
1331 
ignoreSpelling()1332 void Editor::ignoreSpelling()
1333 {
1334     if (!client())
1335         return;
1336 
1337     RefPtr<Range> selectedRange = frame()->selection()->toRange();
1338     if (selectedRange)
1339         frame()->document()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1340 
1341     String text = frame()->selectedText();
1342     ASSERT(text.length() != 0);
1343     client()->ignoreWordInSpellDocument(text);
1344 }
1345 
learnSpelling()1346 void Editor::learnSpelling()
1347 {
1348     if (!client())
1349         return;
1350 
1351     // FIXME: We don't call this on the Mac, and it should remove misppelling markers around the
1352     // learned word, see <rdar://problem/5396072>.
1353 
1354     String text = frame()->selectedText();
1355     ASSERT(text.length() != 0);
1356     client()->learnWord(text);
1357 }
1358 
findFirstMisspellingInRange(EditorClient * client,Range * searchRange,int & firstMisspellingOffset,bool markAll)1359 static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll)
1360 {
1361     ASSERT_ARG(client, client);
1362     ASSERT_ARG(searchRange, searchRange);
1363 
1364     WordAwareIterator it(searchRange);
1365     firstMisspellingOffset = 0;
1366 
1367     String firstMisspelling;
1368     int currentChunkOffset = 0;
1369 
1370     while (!it.atEnd()) {
1371         const UChar* chars = it.characters();
1372         int len = it.length();
1373 
1374         // Skip some work for one-space-char hunks
1375         if (!(len == 1 && chars[0] == ' ')) {
1376 
1377             int misspellingLocation = -1;
1378             int misspellingLength = 0;
1379             client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
1380 
1381             // 5490627 shows that there was some code path here where the String constructor below crashes.
1382             // We don't know exactly what combination of bad input caused this, so we're making this much
1383             // more robust against bad input on release builds.
1384             ASSERT(misspellingLength >= 0);
1385             ASSERT(misspellingLocation >= -1);
1386             ASSERT(misspellingLength == 0 || misspellingLocation >= 0);
1387             ASSERT(misspellingLocation < len);
1388             ASSERT(misspellingLength <= len);
1389             ASSERT(misspellingLocation + misspellingLength <= len);
1390 
1391             if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
1392 
1393                 // Remember first-encountered misspelling and its offset
1394                 if (!firstMisspelling) {
1395                     firstMisspellingOffset = currentChunkOffset + misspellingLocation;
1396                     firstMisspelling = String(chars + misspellingLocation, misspellingLength);
1397                 }
1398 
1399                 // Mark this instance if we're marking all instances. Otherwise bail out because we found the first one.
1400                 if (!markAll)
1401                     break;
1402 
1403                 // Compute range of misspelled word
1404                 RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
1405 
1406                 // Store marker for misspelled word
1407                 ExceptionCode ec = 0;
1408                 misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1409                 ASSERT(ec == 0);
1410             }
1411         }
1412 
1413         currentChunkOffset += len;
1414         it.advance();
1415     }
1416 
1417     return firstMisspelling;
1418 }
1419 
1420 #ifndef BUILDING_ON_TIGER
1421 
paragraphAlignedRangeForRange(Range * arbitraryRange,int & offsetIntoParagraphAlignedRange,String & paragraphString)1422 static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
1423 {
1424     ASSERT_ARG(arbitraryRange, arbitraryRange);
1425 
1426     ExceptionCode ec = 0;
1427 
1428     // Expand range to paragraph boundaries
1429     RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
1430     setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
1431     setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
1432 
1433     // Compute offset from start of expanded range to start of original range
1434     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
1435     offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
1436 
1437     // Fill in out parameter with string representing entire paragraph range.
1438     // Someday we might have a caller that doesn't use this, but for now all callers do.
1439     paragraphString = plainText(paragraphRange.get());
1440 
1441     return paragraphRange;
1442 }
1443 
findFirstGrammarDetailInRange(const Vector<GrammarDetail> & grammarDetails,int badGrammarPhraseLocation,int,Range * searchRange,int startOffset,int endOffset,bool markAll)1444 static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int /*badGrammarPhraseLength*/, Range *searchRange, int startOffset, int endOffset, bool markAll)
1445 {
1446     // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1447     // Optionally add a DocumentMarker for each detail in the range.
1448     int earliestDetailLocationSoFar = -1;
1449     int earliestDetailIndex = -1;
1450     for (unsigned i = 0; i < grammarDetails.size(); i++) {
1451         const GrammarDetail* detail = &grammarDetails[i];
1452         ASSERT(detail->length > 0 && detail->location >= 0);
1453 
1454         int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
1455 
1456         // Skip this detail if it starts before the original search range
1457         if (detailStartOffsetInParagraph < startOffset)
1458             continue;
1459 
1460         // Skip this detail if it starts after the original search range
1461         if (detailStartOffsetInParagraph >= endOffset)
1462             continue;
1463 
1464         if (markAll) {
1465             RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
1466             ExceptionCode ec = 0;
1467             badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
1468             ASSERT(ec == 0);
1469         }
1470 
1471         // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
1472         if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
1473             earliestDetailIndex = i;
1474             earliestDetailLocationSoFar = detail->location;
1475         }
1476     }
1477 
1478     return earliestDetailIndex;
1479 }
1480 
findFirstBadGrammarInRange(EditorClient * client,Range * searchRange,GrammarDetail & outGrammarDetail,int & outGrammarPhraseOffset,bool markAll)1481 static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
1482 {
1483     ASSERT_ARG(client, client);
1484     ASSERT_ARG(searchRange, searchRange);
1485 
1486     // Initialize out parameters; these will be updated if we find something to return.
1487     outGrammarDetail.location = -1;
1488     outGrammarDetail.length = 0;
1489     outGrammarDetail.guesses.clear();
1490     outGrammarDetail.userDescription = "";
1491     outGrammarPhraseOffset = 0;
1492 
1493     String firstBadGrammarPhrase;
1494 
1495     // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
1496     // Determine the character offset from the start of the paragraph to the start of the original search range,
1497     // since we will want to ignore results in this area.
1498     int searchRangeStartOffset;
1499     String paragraphString;
1500     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
1501 
1502     // Determine the character offset from the start of the paragraph to the end of the original search range,
1503     // since we will want to ignore results in this area also.
1504     int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
1505 
1506     // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
1507     int startOffset = 0;
1508     while (startOffset < searchRangeEndOffset) {
1509         Vector<GrammarDetail> grammarDetails;
1510         int badGrammarPhraseLocation = -1;
1511         int badGrammarPhraseLength = 0;
1512         client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
1513 
1514         if (badGrammarPhraseLength == 0) {
1515             ASSERT(badGrammarPhraseLocation == -1);
1516             return String();
1517         }
1518 
1519         ASSERT(badGrammarPhraseLocation >= 0);
1520         badGrammarPhraseLocation += startOffset;
1521 
1522 
1523         // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1524         int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
1525         if (badGrammarIndex >= 0) {
1526             ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
1527             outGrammarDetail = grammarDetails[badGrammarIndex];
1528         }
1529 
1530         // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
1531         // kept going so we could mark all instances).
1532         if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
1533             outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
1534             firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
1535 
1536             // Found one. We're done now, unless we're marking each instance.
1537             if (!markAll)
1538                 break;
1539         }
1540 
1541         // These results were all between the start of the paragraph and the start of the search range; look
1542         // beyond this phrase.
1543         startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
1544     }
1545 
1546     return firstBadGrammarPhrase;
1547 }
1548 
1549 #endif /* not BUILDING_ON_TIGER */
1550 
advanceToNextMisspelling(bool startBeforeSelection)1551 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1552 {
1553     ExceptionCode ec = 0;
1554 
1555     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1556     // then we wrap and search from the doc start to (approximately) where we started.
1557 
1558     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
1559     // repeated "check spelling" commands work.
1560     Selection selection(frame()->selection()->selection());
1561     RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1562     bool startedWithSelection = false;
1563     if (selection.start().node()) {
1564         startedWithSelection = true;
1565         if (startBeforeSelection) {
1566             VisiblePosition start(selection.visibleStart());
1567             // We match AppKit's rule: Start 1 character before the selection.
1568             VisiblePosition oneBeforeStart = start.previous();
1569             setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1570         } else
1571             setStart(spellingSearchRange.get(), selection.visibleEnd());
1572     }
1573 
1574     Position position = spellingSearchRange->startPosition();
1575     if (!isEditablePosition(position)) {
1576         // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1577         // selection is editable.
1578         // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1579         // when spell checking the whole document before sending the message.
1580         // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1581 
1582         position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1583         if (position.isNull())
1584             return;
1585 
1586         Position rangeCompliantPosition = rangeCompliantEquivalent(position);
1587         spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.offset(), ec);
1588         startedWithSelection = false;   // won't need to wrap
1589     }
1590 
1591     // topNode defines the whole range we want to operate on
1592     Node* topNode = highestEditableRoot(position);
1593     spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
1594 
1595     // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1596     // at a word boundary. Going back by one char and then forward by a word does the trick.
1597     if (startedWithSelection) {
1598         VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1599         if (oneBeforeStart.isNotNull()) {
1600             setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1601         } // else we were already at the start of the editable node
1602     }
1603 
1604     if (spellingSearchRange->collapsed(ec))
1605         return;       // nothing to search in
1606 
1607     // Get the spell checker if it is available
1608     if (!client())
1609         return;
1610 
1611     // We go to the end of our first range instead of the start of it, just to be sure
1612     // we don't get foiled by any word boundary problems at the start.  It means we might
1613     // do a tiny bit more searching.
1614     Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1615     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1616 
1617     int misspellingOffset;
1618     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
1619 
1620     String badGrammarPhrase;
1621 
1622 #ifndef BUILDING_ON_TIGER
1623     int grammarPhraseOffset = 0;
1624     GrammarDetail grammarDetail;
1625 
1626     // Search for bad grammar that occurs prior to the next misspelled word (if any)
1627     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1628     if (!misspelledWord.isEmpty()) {
1629         // Stop looking at start of next misspelled word
1630         CharacterIterator chars(grammarSearchRange.get());
1631         chars.advance(misspellingOffset);
1632         grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1633     }
1634 
1635     if (isGrammarCheckingEnabled())
1636         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1637 #endif
1638 
1639     // 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
1640     // block rather than at a selection).
1641     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1642         spellingSearchRange->setStart(topNode, 0, ec);
1643         // going until the end of the very first chunk we tested is far enough
1644         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1645 
1646         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
1647 
1648 #ifndef BUILDING_ON_TIGER
1649         grammarSearchRange = spellingSearchRange->cloneRange(ec);
1650         if (!misspelledWord.isEmpty()) {
1651             // Stop looking at start of next misspelled word
1652             CharacterIterator chars(grammarSearchRange.get());
1653             chars.advance(misspellingOffset);
1654             grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1655         }
1656         if (isGrammarCheckingEnabled())
1657             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1658 #endif
1659     }
1660 
1661     if (!badGrammarPhrase.isEmpty()) {
1662 #ifdef BUILDING_ON_TIGER
1663         ASSERT_NOT_REACHED();
1664 #else
1665         // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1666         // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1667         // panel, and store a marker so we draw the green squiggle later.
1668 
1669         ASSERT(badGrammarPhrase.length() > 0);
1670         ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1671 
1672         // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1673         RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1674         frame()->selection()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1675         frame()->revealSelection();
1676 
1677         client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1678         frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1679 #endif
1680     } else if (!misspelledWord.isEmpty()) {
1681         // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1682         // a marker so we draw the red squiggle later.
1683 
1684         RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1685         frame()->selection()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
1686         frame()->revealSelection();
1687 
1688         client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1689         frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1690     }
1691 }
1692 
isSelectionMisspelled()1693 bool Editor::isSelectionMisspelled()
1694 {
1695     String selectedString = frame()->selectedText();
1696     int length = selectedString.length();
1697     if (length == 0)
1698         return false;
1699 
1700     if (!client())
1701         return false;
1702 
1703     int misspellingLocation = -1;
1704     int misspellingLength = 0;
1705     client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
1706 
1707     // The selection only counts as misspelled if the selected text is exactly one misspelled word
1708     if (misspellingLength != length)
1709         return false;
1710 
1711     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1712     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1713     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1714     // or a grammar error.
1715     client()->updateSpellingUIWithMisspelledWord(selectedString);
1716 
1717     return true;
1718 }
1719 
1720 #ifndef BUILDING_ON_TIGER
isRangeUngrammatical(EditorClient * client,Range * range,Vector<String> & guessesVector)1721 static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
1722 {
1723     if (!client)
1724         return false;
1725 
1726     ExceptionCode ec;
1727     if (!range || range->collapsed(ec))
1728         return false;
1729 
1730     // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
1731     // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
1732     // or overlapping the range; the ranges must exactly match.
1733     guessesVector.clear();
1734     int grammarPhraseOffset;
1735 
1736     GrammarDetail grammarDetail;
1737     String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);
1738 
1739     // No bad grammar in these parts at all.
1740     if (badGrammarPhrase.isEmpty())
1741         return false;
1742 
1743     // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
1744     if (grammarPhraseOffset > 0)
1745         return false;
1746 
1747     ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
1748 
1749     // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
1750     if (grammarDetail.location + grammarPhraseOffset != 0)
1751         return false;
1752 
1753     // Bad grammar at start of range, but end of bad grammar is before or after end of range
1754     if (grammarDetail.length != TextIterator::rangeLength(range))
1755         return false;
1756 
1757     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1758     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1759     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1760     // or a grammar error.
1761     client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1762 
1763     return true;
1764 }
1765 #endif
1766 
isSelectionUngrammatical()1767 bool Editor::isSelectionUngrammatical()
1768 {
1769 #ifdef BUILDING_ON_TIGER
1770     return false;
1771 #else
1772     Vector<String> ignoredGuesses;
1773     return isRangeUngrammatical(client(), frame()->selection()->toRange().get(), ignoredGuesses);
1774 #endif
1775 }
1776 
guessesForUngrammaticalSelection()1777 Vector<String> Editor::guessesForUngrammaticalSelection()
1778 {
1779 #ifdef BUILDING_ON_TIGER
1780     return Vector<String>();
1781 #else
1782     Vector<String> guesses;
1783     // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
1784     isRangeUngrammatical(client(), frame()->selection()->toRange().get(), guesses);
1785     return guesses;
1786 #endif
1787 }
1788 
guessesForMisspelledSelection()1789 Vector<String> Editor::guessesForMisspelledSelection()
1790 {
1791     String selectedString = frame()->selectedText();
1792     ASSERT(selectedString.length() != 0);
1793 
1794     Vector<String> guesses;
1795     if (client())
1796         client()->getGuessesForWord(selectedString, guesses);
1797     return guesses;
1798 }
1799 
showSpellingGuessPanel()1800 void Editor::showSpellingGuessPanel()
1801 {
1802     if (!client()) {
1803         LOG_ERROR("No NSSpellChecker");
1804         return;
1805     }
1806 
1807 #ifndef BUILDING_ON_TIGER
1808     // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
1809     // to match rest of OS X.
1810     if (client()->spellingUIIsShowing()) {
1811         client()->showSpellingUI(false);
1812         return;
1813     }
1814 #endif
1815 
1816     advanceToNextMisspelling(true);
1817     client()->showSpellingUI(true);
1818 }
1819 
spellingPanelIsShowing()1820 bool Editor::spellingPanelIsShowing()
1821 {
1822     if (!client())
1823         return false;
1824     return client()->spellingUIIsShowing();
1825 }
1826 
markMisspellingsAfterTypingToPosition(const VisiblePosition & p)1827 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
1828 {
1829     if (!isContinuousSpellCheckingEnabled())
1830         return;
1831 
1832     // Check spelling of one word
1833     markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
1834 
1835     if (!isGrammarCheckingEnabled())
1836         return;
1837 
1838     // Check grammar of entire sentence
1839     markBadGrammar(Selection(startOfSentence(p), endOfSentence(p)));
1840 }
1841 
markAllMisspellingsInRange(EditorClient * client,Range * searchRange)1842 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange)
1843 {
1844     // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
1845     // all we need to do is mark every instance.
1846     int ignoredOffset;
1847     findFirstMisspellingInRange(client, searchRange, ignoredOffset, true);
1848 }
1849 
1850 #ifndef BUILDING_ON_TIGER
markAllBadGrammarInRange(EditorClient * client,Range * searchRange)1851 static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
1852 {
1853     // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
1854     // do is mark every instance.
1855     GrammarDetail ignoredGrammarDetail;
1856     int ignoredOffset;
1857     findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
1858 }
1859 #endif
1860 
markMisspellingsOrBadGrammar(Editor * editor,const Selection & selection,bool checkSpelling)1861 static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& selection, bool checkSpelling)
1862 {
1863     // This function is called with a selection already expanded to word boundaries.
1864     // Might be nice to assert that here.
1865 
1866     // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
1867     // grammar checking can only be on if spell checking is also on.
1868     if (!editor->isContinuousSpellCheckingEnabled())
1869         return;
1870 
1871     RefPtr<Range> searchRange(selection.toRange());
1872     if (!searchRange)
1873         return;
1874 
1875     // If we're not in an editable node, bail.
1876     Node* editableNode = searchRange->startContainer();
1877     if (!editableNode || !editableNode->isContentEditable())
1878         return;
1879 
1880     // Get the spell checker if it is available
1881     if (!editor->client())
1882         return;
1883 
1884     if (checkSpelling)
1885         markAllMisspellingsInRange(editor->client(), searchRange.get());
1886     else {
1887 #ifdef BUILDING_ON_TIGER
1888         ASSERT_NOT_REACHED();
1889 #else
1890         if (editor->isGrammarCheckingEnabled())
1891             markAllBadGrammarInRange(editor->client(), searchRange.get());
1892 #endif
1893     }
1894 }
1895 
markMisspellings(const Selection & selection)1896 void Editor::markMisspellings(const Selection& selection)
1897 {
1898     markMisspellingsOrBadGrammar(this, selection, true);
1899 }
1900 
markBadGrammar(const Selection & selection)1901 void Editor::markBadGrammar(const Selection& selection)
1902 {
1903 #ifndef BUILDING_ON_TIGER
1904     markMisspellingsOrBadGrammar(this, selection, false);
1905 #else
1906     UNUSED_PARAM(selection);
1907 #endif
1908 }
1909 
rangeForPoint(const IntPoint & windowPoint)1910 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
1911 {
1912     Document* document = m_frame->documentAtPoint(windowPoint);
1913     if (!document)
1914         return 0;
1915 
1916     Frame* frame = document->frame();
1917     ASSERT(frame);
1918     FrameView* frameView = frame->view();
1919     if (!frameView)
1920         return 0;
1921     IntPoint framePoint = frameView->windowToContents(windowPoint);
1922     Selection selection(frame->visiblePositionForPoint(framePoint));
1923     return avoidIntersectionWithNode(selection.toRange().get(), deleteButtonController() ? deleteButtonController()->containerElement() : 0);
1924 }
1925 
revealSelectionAfterEditingOperation()1926 void Editor::revealSelectionAfterEditingOperation()
1927 {
1928     if (m_ignoreCompositionSelectionChange)
1929         return;
1930 
1931     m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
1932 }
1933 
setIgnoreCompositionSelectionChange(bool ignore)1934 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
1935 {
1936     if (m_ignoreCompositionSelectionChange == ignore)
1937         return;
1938 
1939     m_ignoreCompositionSelectionChange = ignore;
1940     if (!ignore)
1941         revealSelectionAfterEditingOperation();
1942 }
1943 
compositionRange() const1944 PassRefPtr<Range> Editor::compositionRange() const
1945 {
1946     if (!m_compositionNode)
1947         return 0;
1948     unsigned length = m_compositionNode->length();
1949     unsigned start = min(m_compositionStart, length);
1950     unsigned end = min(max(start, m_compositionEnd), length);
1951     if (start >= end)
1952         return 0;
1953     return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
1954 }
1955 
getCompositionSelection(unsigned & selectionStart,unsigned & selectionEnd) const1956 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
1957 {
1958     if (!m_compositionNode)
1959         return false;
1960     Position start = m_frame->selection()->start();
1961     if (start.node() != m_compositionNode)
1962         return false;
1963     Position end = m_frame->selection()->end();
1964     if (end.node() != m_compositionNode)
1965         return false;
1966 
1967     if (static_cast<unsigned>(start.offset()) < m_compositionStart)
1968         return false;
1969     if (static_cast<unsigned>(end.offset()) > m_compositionEnd)
1970         return false;
1971 
1972     selectionStart = start.offset() - m_compositionStart;
1973     selectionEnd = start.offset() - m_compositionEnd;
1974     return true;
1975 }
1976 
transpose()1977 void Editor::transpose()
1978 {
1979     if (!canEdit())
1980         return;
1981 
1982      Selection selection = m_frame->selection()->selection();
1983      if (!selection.isCaret())
1984          return;
1985 
1986     // Make a selection that goes back one character and forward two characters.
1987     VisiblePosition caret = selection.visibleStart();
1988     VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
1989     VisiblePosition previous = next.previous();
1990     if (next == previous)
1991         return;
1992     previous = previous.previous();
1993     if (!inSameParagraph(next, previous))
1994         return;
1995     RefPtr<Range> range = makeRange(previous, next);
1996     if (!range)
1997         return;
1998     Selection newSelection(range.get(), DOWNSTREAM);
1999 
2000     // Transpose the two characters.
2001     String text = plainText(range.get());
2002     if (text.length() != 2)
2003         return;
2004     String transposed = text.right(1) + text.left(1);
2005 
2006     // Select the two characters.
2007     if (newSelection != m_frame->selection()->selection()) {
2008         if (!m_frame->shouldChangeSelection(newSelection))
2009             return;
2010         m_frame->selection()->setSelection(newSelection);
2011     }
2012 
2013     // Insert the transposed characters.
2014     if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2015         return;
2016     replaceSelectionWithText(transposed, false, false);
2017 }
2018 
addToKillRing(Range * range,bool prepend)2019 void Editor::addToKillRing(Range* range, bool prepend)
2020 {
2021     if (m_shouldStartNewKillRingSequence)
2022         startNewKillRingSequence();
2023 
2024     String text = m_frame->displayStringModifiedByEncoding(plainText(range));
2025     if (prepend)
2026         prependToKillRing(text);
2027     else
2028         appendToKillRing(text);
2029     m_shouldStartNewKillRingSequence = false;
2030 }
2031 
2032 #if !PLATFORM(MAC)
2033 
appendToKillRing(const String &)2034 void Editor::appendToKillRing(const String&)
2035 {
2036 }
2037 
prependToKillRing(const String &)2038 void Editor::prependToKillRing(const String&)
2039 {
2040 }
2041 
yankFromKillRing()2042 String Editor::yankFromKillRing()
2043 {
2044     return String();
2045 }
2046 
startNewKillRingSequence()2047 void Editor::startNewKillRingSequence()
2048 {
2049 }
2050 
setKillRingToYankedState()2051 void Editor::setKillRingToYankedState()
2052 {
2053 }
2054 
2055 #endif
2056 
insideVisibleArea(const IntPoint & point) const2057 bool Editor::insideVisibleArea(const IntPoint& point) const
2058 {
2059     if (m_frame->excludeFromTextSearch())
2060         return false;
2061 
2062     // Right now, we only check the visibility of a point for disconnected frames. For all other
2063     // frames, we assume visibility.
2064     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2065     if (!frame->isDisconnected())
2066         return true;
2067 
2068     RenderPart* renderer = frame->ownerRenderer();
2069     RenderBlock* container = renderer->containingBlock();
2070     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2071         return true;
2072 
2073     IntRect rectInPageCoords = container->getOverflowClipRect(0, 0);
2074     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2075                                     rectInPageCoords.width(), rectInPageCoords.height());
2076 
2077     return rectInFrameCoords.contains(point);
2078 }
2079 
insideVisibleArea(Range * range) const2080 bool Editor::insideVisibleArea(Range* range) const
2081 {
2082     if (!range)
2083         return true;
2084 
2085     if (m_frame->excludeFromTextSearch())
2086         return false;
2087 
2088     // Right now, we only check the visibility of a range for disconnected frames. For all other
2089     // frames, we assume visibility.
2090     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2091     if (!frame->isDisconnected())
2092         return true;
2093 
2094     RenderPart* renderer = frame->ownerRenderer();
2095     RenderBlock* container = renderer->containingBlock();
2096     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2097         return true;
2098 
2099     IntRect rectInPageCoords = container->getOverflowClipRect(0, 0);
2100     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2101                                     rectInPageCoords.width(), rectInPageCoords.height());
2102     IntRect resultRect = range->boundingBox();
2103 
2104     return rectInFrameCoords.contains(resultRect);
2105 }
2106 
firstVisibleRange(const String & target,bool caseFlag)2107 PassRefPtr<Range> Editor::firstVisibleRange(const String& target, bool caseFlag)
2108 {
2109     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2110     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2111     ExceptionCode ec = 0;
2112 
2113     while (!insideVisibleArea(resultRange.get())) {
2114         searchRange->setStartAfter(resultRange->endContainer(), ec);
2115         if (searchRange->startContainer() == searchRange->endContainer())
2116             return Range::create(m_frame->document());
2117         resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2118     }
2119 
2120     return resultRange;
2121 }
2122 
lastVisibleRange(const String & target,bool caseFlag)2123 PassRefPtr<Range> Editor::lastVisibleRange(const String& target, bool caseFlag)
2124 {
2125     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2126     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2127     ExceptionCode ec = 0;
2128 
2129     while (!insideVisibleArea(resultRange.get())) {
2130         searchRange->setEndBefore(resultRange->startContainer(), ec);
2131         if (searchRange->startContainer() == searchRange->endContainer())
2132             return Range::create(m_frame->document());
2133         resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2134     }
2135 
2136     return resultRange;
2137 }
2138 
nextVisibleRange(Range * currentRange,const String & target,bool forward,bool caseFlag,bool wrapFlag)2139 PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, bool forward, bool caseFlag, bool wrapFlag)
2140 {
2141     if (m_frame->excludeFromTextSearch())
2142         return Range::create(m_frame->document());
2143 
2144     RefPtr<Range> resultRange = currentRange;
2145     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2146     ExceptionCode ec = 0;
2147 
2148     for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, forward, caseFlag)) {
2149         if (resultRange->collapsed(ec)) {
2150             if (!resultRange->startContainer()->isInShadowTree())
2151                 break;
2152             searchRange = rangeOfContents(m_frame->document());
2153             if (forward)
2154                 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
2155             else
2156                 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec);
2157             continue;
2158         }
2159 
2160         if (forward)
2161             searchRange->setStartAfter(resultRange->endContainer(), ec);
2162         else
2163             searchRange->setEndBefore(resultRange->startContainer(), ec);
2164 
2165         Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
2166         if (searchRange->collapsed(ec) && shadowTreeRoot) {
2167             if (forward)
2168                 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
2169             else
2170                 searchRange->setStartBefore(shadowTreeRoot, ec);
2171         }
2172 
2173         if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode())
2174             break;
2175     }
2176 
2177     if (insideVisibleArea(resultRange.get()))
2178         return resultRange;
2179 
2180     if (!wrapFlag)
2181         return Range::create(m_frame->document());
2182 
2183     if (forward)
2184         return firstVisibleRange(target, caseFlag);
2185 
2186     return lastVisibleRange(target, caseFlag);
2187 }
2188 
2189 } // namespace WebCore
2190