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 "CharacterNames.h"
33 #include "CompositionEvent.h"
34 #include "CreateLinkCommand.h"
35 #include "CSSComputedStyleDeclaration.h"
36 #include "CSSMutableStyleDeclaration.h"
37 #include "CSSProperty.h"
38 #include "CSSPropertyNames.h"
39 #include "CSSValueKeywords.h"
40 #include "ClipboardEvent.h"
41 #include "DeleteButtonController.h"
42 #include "DeleteSelectionCommand.h"
43 #include "DocLoader.h"
44 #include "DocumentFragment.h"
45 #include "EditorClient.h"
46 #include "EventHandler.h"
47 #include "EventNames.h"
48 #include "FocusController.h"
49 #include "Frame.h"
50 #include "FrameTree.h"
51 #include "FrameView.h"
52 #include "HTMLInputElement.h"
53 #include "HTMLTextAreaElement.h"
54 #include "HitTestResult.h"
55 #include "IndentOutdentCommand.h"
56 #include "InsertListCommand.h"
57 #include "KeyboardEvent.h"
58 #include "ModifySelectionListLevel.h"
59 #include "Page.h"
60 #include "Pasteboard.h"
61 #include "RemoveFormatCommand.h"
62 #include "RenderBlock.h"
63 #include "RenderPart.h"
64 #include "ReplaceSelectionCommand.h"
65 #include "Sound.h"
66 #include "Text.h"
67 #include "TextIterator.h"
68 #include "TypingCommand.h"
69 #include "htmlediting.h"
70 #include "markup.h"
71 #include "visible_units.h"
72 #include <wtf/UnusedParam.h>
73
74 namespace WebCore {
75
76 using namespace std;
77 using namespace HTMLNames;
78
79 // When an event handler has moved the selection outside of a text control
80 // we should use the target control's selection for this editing operation.
selectionForCommand(Event * event)81 VisibleSelection Editor::selectionForCommand(Event* event)
82 {
83 VisibleSelection selection = m_frame->selection()->selection();
84 if (!event)
85 return selection;
86 // If the target is a text control, and the current selection is outside of its shadow tree,
87 // then use the saved selection for that text control.
88 Node* target = event->target()->toNode();
89 Node* selectionStart = selection.start().node();
90 if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
91 if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
92 return static_cast<HTMLInputElement*>(target)->selection();
93 if (target->hasTagName(textareaTag))
94 return static_cast<HTMLTextAreaElement*>(target)->selection();
95 }
96 return selection;
97 }
98
client() const99 EditorClient* Editor::client() const
100 {
101 if (Page* page = m_frame->page())
102 return page->editorClient();
103 return 0;
104 }
105
handleKeyboardEvent(KeyboardEvent * event)106 void Editor::handleKeyboardEvent(KeyboardEvent* event)
107 {
108 if (EditorClient* c = client())
109 c->handleKeyboardEvent(event);
110 }
111
handleInputMethodKeydown(KeyboardEvent * event)112 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
113 {
114 if (EditorClient* c = client())
115 c->handleInputMethodKeydown(event);
116 }
117
canEdit() const118 bool Editor::canEdit() const
119 {
120 return m_frame->selection()->isContentEditable();
121 }
122
canEditRichly() const123 bool Editor::canEditRichly() const
124 {
125 return m_frame->selection()->isContentRichlyEditable();
126 }
127
128 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
129 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
130 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
131 // normally selectable to implement copy/paste (like divs, or a document body).
132
canDHTMLCut()133 bool Editor::canDHTMLCut()
134 {
135 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
136 }
137
canDHTMLCopy()138 bool Editor::canDHTMLCopy()
139 {
140 return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
141 }
142
canDHTMLPaste()143 bool Editor::canDHTMLPaste()
144 {
145 return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
146 }
147
canCut() const148 bool Editor::canCut() const
149 {
150 return canCopy() && canDelete();
151 }
152
imageElementFromImageDocument(Document * document)153 static HTMLImageElement* imageElementFromImageDocument(Document* document)
154 {
155 if (!document)
156 return 0;
157 if (!document->isImageDocument())
158 return 0;
159
160 HTMLElement* body = document->body();
161 if (!body)
162 return 0;
163
164 Node* node = body->firstChild();
165 if (!node)
166 return 0;
167 if (!node->hasTagName(imgTag))
168 return 0;
169 return static_cast<HTMLImageElement*>(node);
170 }
171
canCopy() const172 bool Editor::canCopy() const
173 {
174 if (imageElementFromImageDocument(m_frame->document()))
175 return true;
176 SelectionController* selection = m_frame->selection();
177 return selection->isRange() && !selection->isInPasswordField();
178 }
179
canPaste() const180 bool Editor::canPaste() const
181 {
182 return canEdit();
183 }
184
canDelete() const185 bool Editor::canDelete() const
186 {
187 SelectionController* selection = m_frame->selection();
188 return selection->isRange() && selection->isContentEditable();
189 }
190
canDeleteRange(Range * range) const191 bool Editor::canDeleteRange(Range* range) const
192 {
193 ExceptionCode ec = 0;
194 Node* startContainer = range->startContainer(ec);
195 Node* endContainer = range->endContainer(ec);
196 if (!startContainer || !endContainer)
197 return false;
198
199 if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
200 return false;
201
202 if (range->collapsed(ec)) {
203 VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
204 VisiblePosition previous = start.previous();
205 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
206 if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
207 return false;
208 }
209 return true;
210 }
211
smartInsertDeleteEnabled()212 bool Editor::smartInsertDeleteEnabled()
213 {
214 return client() && client()->smartInsertDeleteEnabled();
215 }
216
canSmartCopyOrDelete()217 bool Editor::canSmartCopyOrDelete()
218 {
219 return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
220 }
221
isSelectTrailingWhitespaceEnabled()222 bool Editor::isSelectTrailingWhitespaceEnabled()
223 {
224 return client() && client()->isSelectTrailingWhitespaceEnabled();
225 }
226
deleteWithDirection(SelectionController::EDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)227 bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
228 {
229 if (!canEdit())
230 return false;
231
232 if (m_frame->selection()->isRange()) {
233 if (isTypingAction) {
234 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity);
235 revealSelectionAfterEditingOperation();
236 } else {
237 if (killRing)
238 addToKillRing(selectedRange().get(), false);
239 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
240 // Implicitly calls revealSelectionAfterEditingOperation().
241 }
242 } else {
243 switch (direction) {
244 case SelectionController::FORWARD:
245 case SelectionController::RIGHT:
246 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
247 break;
248 case SelectionController::BACKWARD:
249 case SelectionController::LEFT:
250 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
251 break;
252 }
253 revealSelectionAfterEditingOperation();
254 }
255
256 // FIXME: We should to move this down into deleteKeyPressed.
257 // clear the "start new kill ring sequence" setting, because it was set to true
258 // when the selection was updated by deleting the range
259 if (killRing)
260 setStartNewKillRingSequence(false);
261
262 return true;
263 }
264
deleteSelectionWithSmartDelete(bool smartDelete)265 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
266 {
267 if (m_frame->selection()->isNone())
268 return;
269
270 applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
271 }
272
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)273 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
274 {
275 String text = pasteboard->plainText(m_frame);
276 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
277 replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
278 }
279
pasteWithPasteboard(Pasteboard * pasteboard,bool allowPlainText)280 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
281 {
282 RefPtr<Range> range = selectedRange();
283 bool chosePlainText;
284 RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
285 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
286 replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
287 }
288
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)289 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
290 {
291 return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
292 }
293
shouldInsertFragment(PassRefPtr<DocumentFragment> fragment,PassRefPtr<Range> replacingDOMRange,EditorInsertAction givenAction)294 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
295 {
296 if (!client())
297 return false;
298
299 Node* child = fragment->firstChild();
300 if (child && fragment->lastChild() == child && child->isCharacterDataNode())
301 return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
302
303 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
304 }
305
replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)306 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
307 {
308 if (m_frame->selection()->isNone() || !fragment)
309 return;
310
311 applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle));
312 revealSelectionAfterEditingOperation();
313 }
314
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)315 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
316 {
317 replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
318 }
319
selectedRange()320 PassRefPtr<Range> Editor::selectedRange()
321 {
322 if (!m_frame)
323 return 0;
324 return m_frame->selection()->toNormalizedRange();
325 }
326
shouldDeleteRange(Range * range) const327 bool Editor::shouldDeleteRange(Range* range) const
328 {
329 ExceptionCode ec;
330 if (!range || range->collapsed(ec))
331 return false;
332
333 if (!canDeleteRange(range))
334 return false;
335
336 return client() && client()->shouldDeleteRange(range);
337 }
338
tryDHTMLCopy()339 bool Editor::tryDHTMLCopy()
340 {
341 if (m_frame->selection()->isInPasswordField())
342 return false;
343
344 if (canCopy())
345 // Must be done before oncopy adds types and data to the pboard,
346 // also done for security, as it erases data from the last copy/paste.
347 Pasteboard::generalPasteboard()->clear();
348
349 return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
350 }
351
tryDHTMLCut()352 bool Editor::tryDHTMLCut()
353 {
354 if (m_frame->selection()->isInPasswordField())
355 return false;
356
357 if (canCut())
358 // Must be done before oncut adds types and data to the pboard,
359 // also done for security, as it erases data from the last copy/paste.
360 Pasteboard::generalPasteboard()->clear();
361
362 return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
363 }
364
tryDHTMLPaste()365 bool Editor::tryDHTMLPaste()
366 {
367 return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
368 }
369
writeSelectionToPasteboard(Pasteboard * pasteboard)370 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
371 {
372 pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
373 }
374
shouldInsertText(const String & text,Range * range,EditorInsertAction action) const375 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
376 {
377 return client() && client()->shouldInsertText(text, range, action);
378 }
379
shouldShowDeleteInterface(HTMLElement * element) const380 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
381 {
382 return client() && client()->shouldShowDeleteInterface(element);
383 }
384
respondToChangedSelection(const VisibleSelection & oldSelection)385 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
386 {
387 if (client())
388 client()->respondToChangedSelection();
389 m_deleteButtonController->respondToChangedSelection(oldSelection);
390 }
391
respondToChangedContents(const VisibleSelection & endingSelection)392 void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
393 {
394 if (AXObjectCache::accessibilityEnabled()) {
395 Node* node = endingSelection.start().node();
396 if (node)
397 m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false);
398 }
399
400 if (client())
401 client()->respondToChangedContents();
402 }
403
fontForSelection(bool & hasMultipleFonts) const404 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
405 {
406 #if !PLATFORM(QT)
407 hasMultipleFonts = false;
408
409 if (!m_frame->selection()->isRange()) {
410 Node* nodeToRemove;
411 RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove); // sets nodeToRemove
412
413 const SimpleFontData* result = 0;
414 if (style)
415 result = style->font().primaryFont();
416
417 if (nodeToRemove) {
418 ExceptionCode ec;
419 nodeToRemove->remove(ec);
420 ASSERT(ec == 0);
421 }
422
423 return result;
424 }
425
426 const SimpleFontData* font = 0;
427
428 RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
429 Node* startNode = range->editingStartPosition().node();
430 if (startNode) {
431 Node* pastEnd = range->pastLastNode();
432 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
433 // unreproducible case where this didn't happen, so check for nil also.
434 for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
435 RenderObject *renderer = n->renderer();
436 if (!renderer)
437 continue;
438 // FIXME: Are there any node types that have renderers, but that we should be skipping?
439 const SimpleFontData* f = renderer->style()->font().primaryFont();
440 if (!font)
441 font = f;
442 else if (font != f) {
443 hasMultipleFonts = true;
444 break;
445 }
446 }
447 }
448
449 return font;
450 #else
451 return 0;
452 #endif
453 }
454
textDirectionForSelection(bool & hasNestedOrMultipleEmbeddings) const455 WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const
456 {
457 hasNestedOrMultipleEmbeddings = true;
458
459 if (m_frame->selection()->isNone())
460 return NaturalWritingDirection;
461
462 Position pos = m_frame->selection()->selection().start().downstream();
463
464 Node* node = pos.node();
465 if (!node)
466 return NaturalWritingDirection;
467
468 Position end;
469 if (m_frame->selection()->isRange()) {
470 end = m_frame->selection()->selection().end().upstream();
471
472 Node* pastLast = Range::create(m_frame->document(), rangeCompliantEquivalent(pos), rangeCompliantEquivalent(end))->pastLastNode();
473 for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
474 if (!n->isStyledElement())
475 continue;
476
477 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n);
478 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
479 if (!unicodeBidi)
480 continue;
481
482 ASSERT(unicodeBidi->isPrimitiveValue());
483 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
484 if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
485 return NaturalWritingDirection;
486 }
487 }
488
489 if (m_frame->selection()->isCaret()) {
490 if (CSSMutableStyleDeclaration *typingStyle = m_frame->typingStyle()) {
491 RefPtr<CSSValue> unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
492 if (unicodeBidi) {
493 ASSERT(unicodeBidi->isPrimitiveValue());
494 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
495 if (unicodeBidiValue == CSSValueEmbed) {
496 RefPtr<CSSValue> direction = typingStyle->getPropertyCSSValue(CSSPropertyDirection);
497 ASSERT(!direction || direction->isPrimitiveValue());
498 if (direction) {
499 hasNestedOrMultipleEmbeddings = false;
500 return static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
501 }
502 } else if (unicodeBidiValue == CSSValueNormal) {
503 hasNestedOrMultipleEmbeddings = false;
504 return NaturalWritingDirection;
505 }
506 }
507 }
508 node = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
509 }
510
511 // 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
512 // to decide.
513 Node* block = enclosingBlock(node);
514 WritingDirection foundDirection = NaturalWritingDirection;
515
516 for (; node != block; node = node->parent()) {
517 if (!node->isStyledElement())
518 continue;
519
520 RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
521 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
522 if (!unicodeBidi)
523 continue;
524
525 ASSERT(unicodeBidi->isPrimitiveValue());
526 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
527 if (unicodeBidiValue == CSSValueNormal)
528 continue;
529
530 if (unicodeBidiValue == CSSValueBidiOverride)
531 return NaturalWritingDirection;
532
533 ASSERT(unicodeBidiValue == CSSValueEmbed);
534 RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
535 if (!direction)
536 continue;
537
538 ASSERT(direction->isPrimitiveValue());
539 int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
540 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
541 continue;
542
543 if (foundDirection != NaturalWritingDirection)
544 return NaturalWritingDirection;
545
546 // In the range case, make sure that the embedding element persists until the end of the range.
547 if (m_frame->selection()->isRange() && !end.node()->isDescendantOf(node))
548 return NaturalWritingDirection;
549
550 foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
551 }
552 hasNestedOrMultipleEmbeddings = false;
553 return foundDirection;
554 }
555
hasBidiSelection() const556 bool Editor::hasBidiSelection() const
557 {
558 if (m_frame->selection()->isNone())
559 return false;
560
561 Node* startNode;
562 if (m_frame->selection()->isRange()) {
563 startNode = m_frame->selection()->selection().start().downstream().node();
564 Node* endNode = m_frame->selection()->selection().end().upstream().node();
565 if (enclosingBlock(startNode) != enclosingBlock(endNode))
566 return false;
567 } else
568 startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
569
570 RenderObject* renderer = startNode->renderer();
571 while (renderer && !renderer->isRenderBlock())
572 renderer = renderer->parent();
573
574 if (!renderer)
575 return false;
576
577 RenderStyle* style = renderer->style();
578 if (style->direction() == RTL)
579 return true;
580
581 return toRenderBlock(renderer)->containsNonZeroBidiLevel();
582 }
583
selectionUnorderedListState() const584 TriState Editor::selectionUnorderedListState() const
585 {
586 if (m_frame->selection()->isCaret()) {
587 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag))
588 return TrueTriState;
589 } else if (m_frame->selection()->isRange()) {
590 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag);
591 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag);
592 if (startNode && endNode && startNode == endNode)
593 return TrueTriState;
594 }
595
596 return FalseTriState;
597 }
598
selectionOrderedListState() const599 TriState Editor::selectionOrderedListState() const
600 {
601 if (m_frame->selection()->isCaret()) {
602 if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag))
603 return TrueTriState;
604 } else if (m_frame->selection()->isRange()) {
605 Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag);
606 Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag);
607 if (startNode && endNode && startNode == endNode)
608 return TrueTriState;
609 }
610
611 return FalseTriState;
612 }
613
insertOrderedList()614 PassRefPtr<Node> Editor::insertOrderedList()
615 {
616 if (!canEditRichly())
617 return 0;
618
619 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
620 revealSelectionAfterEditingOperation();
621 return newList;
622 }
623
insertUnorderedList()624 PassRefPtr<Node> Editor::insertUnorderedList()
625 {
626 if (!canEditRichly())
627 return 0;
628
629 RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
630 revealSelectionAfterEditingOperation();
631 return newList;
632 }
633
canIncreaseSelectionListLevel()634 bool Editor::canIncreaseSelectionListLevel()
635 {
636 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
637 }
638
canDecreaseSelectionListLevel()639 bool Editor::canDecreaseSelectionListLevel()
640 {
641 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
642 }
643
increaseSelectionListLevel()644 PassRefPtr<Node> Editor::increaseSelectionListLevel()
645 {
646 if (!canEditRichly() || m_frame->selection()->isNone())
647 return 0;
648
649 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
650 revealSelectionAfterEditingOperation();
651 return newList;
652 }
653
increaseSelectionListLevelOrdered()654 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
655 {
656 if (!canEditRichly() || m_frame->selection()->isNone())
657 return 0;
658
659 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
660 revealSelectionAfterEditingOperation();
661 return newList.release();
662 }
663
increaseSelectionListLevelUnordered()664 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
665 {
666 if (!canEditRichly() || m_frame->selection()->isNone())
667 return 0;
668
669 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
670 revealSelectionAfterEditingOperation();
671 return newList.release();
672 }
673
decreaseSelectionListLevel()674 void Editor::decreaseSelectionListLevel()
675 {
676 if (!canEditRichly() || m_frame->selection()->isNone())
677 return;
678
679 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
680 revealSelectionAfterEditingOperation();
681 }
682
removeFormattingAndStyle()683 void Editor::removeFormattingAndStyle()
684 {
685 applyCommand(RemoveFormatCommand::create(m_frame->document()));
686 }
687
clearLastEditCommand()688 void Editor::clearLastEditCommand()
689 {
690 m_lastEditCommand.clear();
691 }
692
693 // Returns whether caller should continue with "the default processing", which is the same as
694 // the event handler NOT setting the return value to false
dispatchCPPEvent(const AtomicString & eventType,ClipboardAccessPolicy policy)695 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
696 {
697 Node* target = m_frame->selection()->start().element();
698 if (!target)
699 target = m_frame->document()->body();
700 if (!target)
701 return true;
702 target = target->shadowAncestorNode();
703
704 RefPtr<Clipboard> clipboard = newGeneralClipboard(policy);
705
706 ExceptionCode ec = 0;
707 RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
708 target->dispatchEvent(evt, ec);
709 bool noDefaultProcessing = evt->defaultPrevented();
710
711 // invalidate clipboard here for security
712 clipboard->setAccessPolicy(ClipboardNumb);
713
714 return !noDefaultProcessing;
715 }
716
applyStyle(CSSStyleDeclaration * style,EditAction editingAction)717 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
718 {
719 switch (m_frame->selection()->selectionType()) {
720 case VisibleSelection::NoSelection:
721 // do nothing
722 break;
723 case VisibleSelection::CaretSelection:
724 m_frame->computeAndSetTypingStyle(style, editingAction);
725 break;
726 case VisibleSelection::RangeSelection:
727 if (style)
728 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction));
729 break;
730 }
731 }
732
shouldApplyStyle(CSSStyleDeclaration * style,Range * range)733 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
734 {
735 return client()->shouldApplyStyle(style, range);
736 }
737
applyParagraphStyle(CSSStyleDeclaration * style,EditAction editingAction)738 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
739 {
740 switch (m_frame->selection()->selectionType()) {
741 case VisibleSelection::NoSelection:
742 // do nothing
743 break;
744 case VisibleSelection::CaretSelection:
745 case VisibleSelection::RangeSelection:
746 if (style)
747 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
748 break;
749 }
750 }
751
applyStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)752 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
753 {
754 if (!style || style->length() == 0 || !canEditRichly())
755 return;
756
757 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
758 applyStyle(style, editingAction);
759 }
760
applyParagraphStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)761 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
762 {
763 if (!style || style->length() == 0 || !canEditRichly())
764 return;
765
766 if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
767 applyParagraphStyle(style, editingAction);
768 }
769
clientIsEditable() const770 bool Editor::clientIsEditable() const
771 {
772 return client() && client()->isEditable();
773 }
774
775 // CSS properties that only has a visual difference when applied to text.
776 static const int textOnlyProperties[] = {
777 CSSPropertyTextDecoration,
778 CSSPropertyWebkitTextDecorationsInEffect,
779 CSSPropertyFontStyle,
780 CSSPropertyFontWeight,
781 CSSPropertyColor,
782 };
783
triStateOfStyleInComputedStyle(CSSStyleDeclaration * desiredStyle,CSSComputedStyleDeclaration * computedStyle,bool ignoreTextOnlyProperties=false)784 static TriState triStateOfStyleInComputedStyle(CSSStyleDeclaration* desiredStyle, CSSComputedStyleDeclaration* computedStyle, bool ignoreTextOnlyProperties = false)
785 {
786 RefPtr<CSSMutableStyleDeclaration> diff = getPropertiesNotInComputedStyle(desiredStyle, computedStyle);
787
788 if (ignoreTextOnlyProperties)
789 diff->removePropertiesInSet(textOnlyProperties, sizeof(textOnlyProperties)/sizeof(textOnlyProperties[0]));
790
791 if (!diff->length())
792 return TrueTriState;
793 else if (diff->length() == desiredStyle->length())
794 return FalseTriState;
795 return MixedTriState;
796 }
797
selectionStartHasStyle(CSSStyleDeclaration * style) const798 bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const
799 {
800 Node* nodeToRemove;
801 RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
802 if (!selectionStyle)
803 return false;
804 TriState state = triStateOfStyleInComputedStyle(style, selectionStyle.get());
805 if (nodeToRemove) {
806 ExceptionCode ec = 0;
807 nodeToRemove->remove(ec);
808 ASSERT(ec == 0);
809 }
810 return state == TrueTriState;
811 }
812
selectionHasStyle(CSSStyleDeclaration * style) const813 TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const
814 {
815 TriState state = FalseTriState;
816
817 if (!m_frame->selection()->isRange()) {
818 Node* nodeToRemove;
819 RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
820 if (!selectionStyle)
821 return FalseTriState;
822 state = triStateOfStyleInComputedStyle(style, selectionStyle.get());
823 if (nodeToRemove) {
824 ExceptionCode ec = 0;
825 nodeToRemove->remove(ec);
826 ASSERT(ec == 0);
827 }
828 } else {
829 for (Node* node = m_frame->selection()->start().node(); node; node = node->traverseNextNode()) {
830 RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
831 if (nodeStyle) {
832 TriState nodeState = triStateOfStyleInComputedStyle(style, nodeStyle.get(), !node->isTextNode());
833 if (node == m_frame->selection()->start().node())
834 state = nodeState;
835 else if (state != nodeState && node->isTextNode()) {
836 state = MixedTriState;
837 break;
838 }
839 }
840 if (node == m_frame->selection()->end().node())
841 break;
842 }
843 }
844
845 return state;
846 }
indent()847 void Editor::indent()
848 {
849 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
850 }
851
outdent()852 void Editor::outdent()
853 {
854 applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
855 }
856
dispatchEditableContentChangedEvents(const EditCommand & command)857 static void dispatchEditableContentChangedEvents(const EditCommand& command)
858 {
859 Element* startRoot = command.startingRootEditableElement();
860 Element* endRoot = command.endingRootEditableElement();
861 ExceptionCode ec;
862 if (startRoot)
863 startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
864 if (endRoot && endRoot != startRoot)
865 endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
866 }
867
appliedEditing(PassRefPtr<EditCommand> cmd)868 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
869 {
870 dispatchEditableContentChangedEvents(*cmd);
871
872 VisibleSelection newSelection(cmd->endingSelection());
873 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
874 changeSelectionAfterCommand(newSelection, false, false, cmd.get());
875
876 if (!cmd->preservesTypingStyle())
877 m_frame->setTypingStyle(0);
878
879 // Command will be equal to last edit command only in the case of typing
880 if (m_lastEditCommand.get() == cmd)
881 ASSERT(cmd->isTypingCommand());
882 else {
883 // Only register a new undo command if the command passed in is
884 // different from the last command
885 m_lastEditCommand = cmd;
886 if (client())
887 client()->registerCommandForUndo(m_lastEditCommand);
888 }
889 respondToChangedContents(newSelection);
890 }
891
unappliedEditing(PassRefPtr<EditCommand> cmd)892 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
893 {
894 dispatchEditableContentChangedEvents(*cmd);
895
896 VisibleSelection newSelection(cmd->startingSelection());
897 changeSelectionAfterCommand(newSelection, true, true, cmd.get());
898
899 m_lastEditCommand = 0;
900 if (client())
901 client()->registerCommandForRedo(cmd);
902 respondToChangedContents(newSelection);
903 }
904
reappliedEditing(PassRefPtr<EditCommand> cmd)905 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
906 {
907 dispatchEditableContentChangedEvents(*cmd);
908
909 VisibleSelection newSelection(cmd->endingSelection());
910 changeSelectionAfterCommand(newSelection, true, true, cmd.get());
911
912 m_lastEditCommand = 0;
913 if (client())
914 client()->registerCommandForUndo(cmd);
915 respondToChangedContents(newSelection);
916 }
917
Editor(Frame * frame)918 Editor::Editor(Frame* frame)
919 : m_frame(frame)
920 , m_deleteButtonController(new DeleteButtonController(frame))
921 , m_ignoreCompositionSelectionChange(false)
922 , m_shouldStartNewKillRingSequence(false)
923 // This is off by default, since most editors want this behavior (this matches IE but not FF).
924 , m_shouldStyleWithCSS(false)
925 {
926 }
927
~Editor()928 Editor::~Editor()
929 {
930 }
931
clear()932 void Editor::clear()
933 {
934 m_compositionNode = 0;
935 m_customCompositionUnderlines.clear();
936 m_shouldStyleWithCSS = false;
937 }
938
insertText(const String & text,Event * triggeringEvent)939 bool Editor::insertText(const String& text, Event* triggeringEvent)
940 {
941 return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
942 }
943
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,Event * triggeringEvent)944 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
945 {
946 if (text.isEmpty())
947 return false;
948
949 VisibleSelection selection = selectionForCommand(triggeringEvent);
950 if (!selection.isContentEditable())
951 return false;
952 RefPtr<Range> range = selection.toNormalizedRange();
953
954 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
955 return true;
956
957 // Get the selection to use for the event that triggered this insertText.
958 // If the event handler changed the selection, we may want to use a different selection
959 // that is contained in the event target.
960 selection = selectionForCommand(triggeringEvent);
961 if (selection.isContentEditable()) {
962 if (Node* selectionStart = selection.start().node()) {
963 RefPtr<Document> document = selectionStart->document();
964
965 // Insert the text
966 TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
967
968 // Reveal the current selection
969 if (Frame* editedFrame = document->frame())
970 if (Page* page = editedFrame->page())
971 page->focusController()->focusedOrMainFrame()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
972 }
973 }
974
975 return true;
976 }
977
insertLineBreak()978 bool Editor::insertLineBreak()
979 {
980 if (!canEdit())
981 return false;
982
983 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
984 return true;
985
986 TypingCommand::insertLineBreak(m_frame->document());
987 revealSelectionAfterEditingOperation();
988 return true;
989 }
990
insertParagraphSeparator()991 bool Editor::insertParagraphSeparator()
992 {
993 if (!canEdit())
994 return false;
995
996 if (!canEditRichly())
997 return insertLineBreak();
998
999 if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
1000 return true;
1001
1002 TypingCommand::insertParagraphSeparator(m_frame->document());
1003 revealSelectionAfterEditingOperation();
1004 return true;
1005 }
1006
cut()1007 void Editor::cut()
1008 {
1009 if (tryDHTMLCut())
1010 return; // DHTML did the whole operation
1011 if (!canCut()) {
1012 systemBeep();
1013 return;
1014 }
1015 RefPtr<Range> selection = selectedRange();
1016 if (shouldDeleteRange(selection.get())) {
1017 if (isNodeInTextFormControl(m_frame->selection()->start().node()))
1018 Pasteboard::generalPasteboard()->writePlainText(m_frame->selectedText());
1019 else
1020 Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
1021 didWriteSelectionToPasteboard();
1022 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1023 }
1024 }
1025
copy()1026 void Editor::copy()
1027 {
1028 if (tryDHTMLCopy())
1029 return; // DHTML did the whole operation
1030 if (!canCopy()) {
1031 systemBeep();
1032 return;
1033 }
1034
1035 if (isNodeInTextFormControl(m_frame->selection()->start().node()))
1036 Pasteboard::generalPasteboard()->writePlainText(m_frame->selectedText());
1037 else {
1038 Document* document = m_frame->document();
1039 if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
1040 Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
1041 else
1042 Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
1043 }
1044
1045 didWriteSelectionToPasteboard();
1046 }
1047
1048 #if !PLATFORM(MAC)
1049
paste()1050 void Editor::paste()
1051 {
1052 ASSERT(m_frame->document());
1053 if (tryDHTMLPaste())
1054 return; // DHTML did the whole operation
1055 if (!canPaste())
1056 return;
1057 DocLoader* loader = m_frame->document()->docLoader();
1058 loader->setAllowStaleResources(true);
1059 if (m_frame->selection()->isContentRichlyEditable())
1060 pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
1061 else
1062 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1063 loader->setAllowStaleResources(false);
1064 }
1065
1066 #endif
1067
pasteAsPlainText()1068 void Editor::pasteAsPlainText()
1069 {
1070 if (tryDHTMLPaste())
1071 return;
1072 if (!canPaste())
1073 return;
1074 pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1075 }
1076
performDelete()1077 void Editor::performDelete()
1078 {
1079 if (!canDelete()) {
1080 systemBeep();
1081 return;
1082 }
1083
1084 addToKillRing(selectedRange().get(), false);
1085 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1086
1087 // clear the "start new kill ring sequence" setting, because it was set to true
1088 // when the selection was updated by deleting the range
1089 setStartNewKillRingSequence(false);
1090 }
1091
copyURL(const KURL & url,const String & title)1092 void Editor::copyURL(const KURL& url, const String& title)
1093 {
1094 Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1095 }
1096
copyImage(const HitTestResult & result)1097 void Editor::copyImage(const HitTestResult& result)
1098 {
1099 KURL url = result.absoluteLinkURL();
1100 if (url.isEmpty())
1101 url = result.absoluteImageURL();
1102
1103 Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1104 }
1105
isContinuousSpellCheckingEnabled()1106 bool Editor::isContinuousSpellCheckingEnabled()
1107 {
1108 return client() && client()->isContinuousSpellCheckingEnabled();
1109 }
1110
toggleContinuousSpellChecking()1111 void Editor::toggleContinuousSpellChecking()
1112 {
1113 if (client())
1114 client()->toggleContinuousSpellChecking();
1115 }
1116
isGrammarCheckingEnabled()1117 bool Editor::isGrammarCheckingEnabled()
1118 {
1119 return client() && client()->isGrammarCheckingEnabled();
1120 }
1121
toggleGrammarChecking()1122 void Editor::toggleGrammarChecking()
1123 {
1124 if (client())
1125 client()->toggleGrammarChecking();
1126 }
1127
spellCheckerDocumentTag()1128 int Editor::spellCheckerDocumentTag()
1129 {
1130 return client() ? client()->spellCheckerDocumentTag() : 0;
1131 }
1132
1133 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1134
uppercaseWord()1135 void Editor::uppercaseWord()
1136 {
1137 if (client())
1138 client()->uppercaseWord();
1139 }
1140
lowercaseWord()1141 void Editor::lowercaseWord()
1142 {
1143 if (client())
1144 client()->lowercaseWord();
1145 }
1146
capitalizeWord()1147 void Editor::capitalizeWord()
1148 {
1149 if (client())
1150 client()->capitalizeWord();
1151 }
1152
showSubstitutionsPanel()1153 void Editor::showSubstitutionsPanel()
1154 {
1155 if (!client()) {
1156 LOG_ERROR("No NSSpellChecker");
1157 return;
1158 }
1159
1160 if (client()->substitutionsPanelIsShowing()) {
1161 client()->showSubstitutionsPanel(false);
1162 return;
1163 }
1164 client()->showSubstitutionsPanel(true);
1165 }
1166
substitutionsPanelIsShowing()1167 bool Editor::substitutionsPanelIsShowing()
1168 {
1169 if (!client())
1170 return false;
1171 return client()->substitutionsPanelIsShowing();
1172 }
1173
toggleSmartInsertDelete()1174 void Editor::toggleSmartInsertDelete()
1175 {
1176 if (client())
1177 client()->toggleSmartInsertDelete();
1178 }
1179
isAutomaticQuoteSubstitutionEnabled()1180 bool Editor::isAutomaticQuoteSubstitutionEnabled()
1181 {
1182 return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1183 }
1184
toggleAutomaticQuoteSubstitution()1185 void Editor::toggleAutomaticQuoteSubstitution()
1186 {
1187 if (client())
1188 client()->toggleAutomaticQuoteSubstitution();
1189 }
1190
isAutomaticLinkDetectionEnabled()1191 bool Editor::isAutomaticLinkDetectionEnabled()
1192 {
1193 return client() && client()->isAutomaticLinkDetectionEnabled();
1194 }
1195
toggleAutomaticLinkDetection()1196 void Editor::toggleAutomaticLinkDetection()
1197 {
1198 if (client())
1199 client()->toggleAutomaticLinkDetection();
1200 }
1201
isAutomaticDashSubstitutionEnabled()1202 bool Editor::isAutomaticDashSubstitutionEnabled()
1203 {
1204 return client() && client()->isAutomaticDashSubstitutionEnabled();
1205 }
1206
toggleAutomaticDashSubstitution()1207 void Editor::toggleAutomaticDashSubstitution()
1208 {
1209 if (client())
1210 client()->toggleAutomaticDashSubstitution();
1211 }
1212
isAutomaticTextReplacementEnabled()1213 bool Editor::isAutomaticTextReplacementEnabled()
1214 {
1215 return client() && client()->isAutomaticTextReplacementEnabled();
1216 }
1217
toggleAutomaticTextReplacement()1218 void Editor::toggleAutomaticTextReplacement()
1219 {
1220 if (client())
1221 client()->toggleAutomaticTextReplacement();
1222 }
1223
isAutomaticSpellingCorrectionEnabled()1224 bool Editor::isAutomaticSpellingCorrectionEnabled()
1225 {
1226 return client() && client()->isAutomaticSpellingCorrectionEnabled();
1227 }
1228
toggleAutomaticSpellingCorrection()1229 void Editor::toggleAutomaticSpellingCorrection()
1230 {
1231 if (client())
1232 client()->toggleAutomaticSpellingCorrection();
1233 }
1234
1235 #endif
1236
shouldEndEditing(Range * range)1237 bool Editor::shouldEndEditing(Range* range)
1238 {
1239 return client() && client()->shouldEndEditing(range);
1240 }
1241
shouldBeginEditing(Range * range)1242 bool Editor::shouldBeginEditing(Range* range)
1243 {
1244 return client() && client()->shouldBeginEditing(range);
1245 }
1246
clearUndoRedoOperations()1247 void Editor::clearUndoRedoOperations()
1248 {
1249 if (client())
1250 client()->clearUndoRedoOperations();
1251 }
1252
canUndo()1253 bool Editor::canUndo()
1254 {
1255 return client() && client()->canUndo();
1256 }
1257
undo()1258 void Editor::undo()
1259 {
1260 if (client())
1261 client()->undo();
1262 }
1263
canRedo()1264 bool Editor::canRedo()
1265 {
1266 return client() && client()->canRedo();
1267 }
1268
redo()1269 void Editor::redo()
1270 {
1271 if (client())
1272 client()->redo();
1273 }
1274
didBeginEditing()1275 void Editor::didBeginEditing()
1276 {
1277 if (client())
1278 client()->didBeginEditing();
1279 }
1280
didEndEditing()1281 void Editor::didEndEditing()
1282 {
1283 if (client())
1284 client()->didEndEditing();
1285 }
1286
didWriteSelectionToPasteboard()1287 void Editor::didWriteSelectionToPasteboard()
1288 {
1289 if (client())
1290 client()->didWriteSelectionToPasteboard();
1291 }
1292
toggleBold()1293 void Editor::toggleBold()
1294 {
1295 command("ToggleBold").execute();
1296 }
1297
toggleUnderline()1298 void Editor::toggleUnderline()
1299 {
1300 command("ToggleUnderline").execute();
1301 }
1302
setBaseWritingDirection(WritingDirection direction)1303 void Editor::setBaseWritingDirection(WritingDirection direction)
1304 {
1305 Node* focusedNode = frame()->document()->focusedNode();
1306 if (focusedNode && (focusedNode->hasTagName(textareaTag)
1307 || (focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT
1308 || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH)))) {
1309 if (direction == NaturalWritingDirection)
1310 return;
1311 static_cast<HTMLElement*>(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1312 frame()->document()->updateStyleIfNeeded();
1313 return;
1314 }
1315
1316 RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
1317 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1318 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1319 }
1320
selectComposition()1321 void Editor::selectComposition()
1322 {
1323 RefPtr<Range> range = compositionRange();
1324 if (!range)
1325 return;
1326
1327 // The composition can start inside a composed character sequence, so we have to override checks.
1328 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1329 VisibleSelection selection;
1330 selection.setWithoutValidation(range->startPosition(), range->endPosition());
1331 m_frame->selection()->setSelection(selection, false, false);
1332 }
1333
confirmComposition()1334 void Editor::confirmComposition()
1335 {
1336 if (!m_compositionNode)
1337 return;
1338 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1339 }
1340
confirmCompositionWithoutDisturbingSelection()1341 void Editor::confirmCompositionWithoutDisturbingSelection()
1342 {
1343 if (!m_compositionNode)
1344 return;
1345 confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1346 }
1347
confirmComposition(const String & text)1348 void Editor::confirmComposition(const String& text)
1349 {
1350 confirmComposition(text, false);
1351 }
1352
confirmComposition(const String & text,bool preserveSelection)1353 void Editor::confirmComposition(const String& text, bool preserveSelection)
1354 {
1355 setIgnoreCompositionSelectionChange(true);
1356
1357 VisibleSelection oldSelection = m_frame->selection()->selection();
1358
1359 selectComposition();
1360
1361 if (m_frame->selection()->isNone()) {
1362 setIgnoreCompositionSelectionChange(false);
1363 return;
1364 }
1365
1366 // Dispatch a compositionend event to the focused node.
1367 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
1368 // the DOM Event specification.
1369 Node* target = m_frame->document()->focusedNode();
1370 if (target) {
1371 RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1372 ExceptionCode ec = 0;
1373 target->dispatchEvent(event, ec);
1374 }
1375
1376 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1377 // will delete the old composition with an optimized replace operation.
1378 if (text.isEmpty())
1379 TypingCommand::deleteSelection(m_frame->document(), false);
1380
1381 m_compositionNode = 0;
1382 m_customCompositionUnderlines.clear();
1383
1384 insertText(text, 0);
1385
1386 if (preserveSelection) {
1387 m_frame->selection()->setSelection(oldSelection, false, false);
1388 // An open typing command that disagrees about current selection would cause issues with typing later on.
1389 TypingCommand::closeTyping(m_lastEditCommand.get());
1390 }
1391
1392 setIgnoreCompositionSelectionChange(false);
1393 }
1394
setComposition(const String & text,const Vector<CompositionUnderline> & underlines,unsigned selectionStart,unsigned selectionEnd)1395 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1396 {
1397 setIgnoreCompositionSelectionChange(true);
1398
1399 selectComposition();
1400
1401 if (m_frame->selection()->isNone()) {
1402 setIgnoreCompositionSelectionChange(false);
1403 return;
1404 }
1405
1406 Node* target = m_frame->document()->focusedNode();
1407 if (target) {
1408 // Dispatch an appropriate composition event to the focused node.
1409 // We check the composition status and choose an appropriate composition event since this
1410 // function is used for three purposes:
1411 // 1. Starting a new composition.
1412 // Send a compositionstart event when this function creates a new composition node, i.e.
1413 // m_compositionNode == 0 && !text.isEmpty().
1414 // 2. Updating the existing composition node.
1415 // Send a compositionupdate event when this function updates the existing composition
1416 // node, i.e. m_compositionNode != 0 && !text.isEmpty().
1417 // 3. Canceling the ongoing composition.
1418 // Send a compositionend event when function deletes the existing composition node, i.e.
1419 // m_compositionNode != 0 && test.isEmpty().
1420 RefPtr<CompositionEvent> event;
1421 if (!m_compositionNode) {
1422 // We should send a compositionstart event only when the given text is not empty because this
1423 // function doesn't create a composition node when the text is empty.
1424 if (!text.isEmpty())
1425 event = CompositionEvent::create(eventNames().compositionstartEvent, m_frame->domWindow(), text);
1426 } else {
1427 if (!text.isEmpty())
1428 event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text);
1429 else
1430 event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1431 }
1432 ExceptionCode ec = 0;
1433 if (event.get())
1434 target->dispatchEvent(event, ec);
1435 }
1436
1437 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1438 // will delete the old composition with an optimized replace operation.
1439 if (text.isEmpty())
1440 TypingCommand::deleteSelection(m_frame->document(), false);
1441
1442 m_compositionNode = 0;
1443 m_customCompositionUnderlines.clear();
1444
1445 if (!text.isEmpty()) {
1446 TypingCommand::insertText(m_frame->document(), text, true, true);
1447
1448 Node* baseNode = m_frame->selection()->base().node();
1449 unsigned baseOffset = m_frame->selection()->base().deprecatedEditingOffset();
1450 Node* extentNode = m_frame->selection()->extent().node();
1451 unsigned extentOffset = m_frame->selection()->extent().deprecatedEditingOffset();
1452
1453 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1454 m_compositionNode = static_cast<Text*>(baseNode);
1455 m_compositionStart = baseOffset;
1456 m_compositionEnd = extentOffset;
1457 m_customCompositionUnderlines = underlines;
1458 size_t numUnderlines = m_customCompositionUnderlines.size();
1459 for (size_t i = 0; i < numUnderlines; ++i) {
1460 m_customCompositionUnderlines[i].startOffset += baseOffset;
1461 m_customCompositionUnderlines[i].endOffset += baseOffset;
1462 }
1463 if (baseNode->renderer())
1464 baseNode->renderer()->repaint();
1465
1466 unsigned start = min(baseOffset + selectionStart, extentOffset);
1467 unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1468 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1469 m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1470 }
1471 }
1472
1473 setIgnoreCompositionSelectionChange(false);
1474 }
1475
ignoreSpelling()1476 void Editor::ignoreSpelling()
1477 {
1478 if (!client())
1479 return;
1480
1481 RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1482 if (selectedRange)
1483 frame()->document()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1484
1485 String text = frame()->selectedText();
1486 ASSERT(text.length() != 0);
1487 client()->ignoreWordInSpellDocument(text);
1488 }
1489
learnSpelling()1490 void Editor::learnSpelling()
1491 {
1492 if (!client())
1493 return;
1494
1495 // FIXME: We don't call this on the Mac, and it should remove misspelling markers around the
1496 // learned word, see <rdar://problem/5396072>.
1497
1498 String text = frame()->selectedText();
1499 ASSERT(text.length() != 0);
1500 client()->learnWord(text);
1501 }
1502
findFirstMisspellingInRange(EditorClient * client,Range * searchRange,int & firstMisspellingOffset,bool markAll,RefPtr<Range> & firstMisspellingRange)1503 static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll, RefPtr<Range>& firstMisspellingRange)
1504 {
1505 ASSERT_ARG(client, client);
1506 ASSERT_ARG(searchRange, searchRange);
1507
1508 WordAwareIterator it(searchRange);
1509 firstMisspellingOffset = 0;
1510
1511 String firstMisspelling;
1512 int currentChunkOffset = 0;
1513
1514 while (!it.atEnd()) {
1515 const UChar* chars = it.characters();
1516 int len = it.length();
1517
1518 // Skip some work for one-space-char hunks
1519 if (!(len == 1 && chars[0] == ' ')) {
1520
1521 int misspellingLocation = -1;
1522 int misspellingLength = 0;
1523 client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
1524
1525 // 5490627 shows that there was some code path here where the String constructor below crashes.
1526 // We don't know exactly what combination of bad input caused this, so we're making this much
1527 // more robust against bad input on release builds.
1528 ASSERT(misspellingLength >= 0);
1529 ASSERT(misspellingLocation >= -1);
1530 ASSERT(misspellingLength == 0 || misspellingLocation >= 0);
1531 ASSERT(misspellingLocation < len);
1532 ASSERT(misspellingLength <= len);
1533 ASSERT(misspellingLocation + misspellingLength <= len);
1534
1535 if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
1536
1537 // Compute range of misspelled word
1538 RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
1539
1540 // Remember first-encountered misspelling and its offset.
1541 if (!firstMisspelling) {
1542 firstMisspellingOffset = currentChunkOffset + misspellingLocation;
1543 firstMisspelling = String(chars + misspellingLocation, misspellingLength);
1544 firstMisspellingRange = misspellingRange;
1545 }
1546
1547 // Store marker for misspelled word.
1548 ExceptionCode ec = 0;
1549 misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1550 ASSERT(ec == 0);
1551
1552 // Bail out if we're marking only the first misspelling, and not all instances.
1553 if (!markAll)
1554 break;
1555 }
1556 }
1557
1558 currentChunkOffset += len;
1559 it.advance();
1560 }
1561
1562 return firstMisspelling;
1563 }
1564
1565 #ifndef BUILDING_ON_TIGER
1566
paragraphAlignedRangeForRange(Range * arbitraryRange,int & offsetIntoParagraphAlignedRange,String & paragraphString)1567 static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
1568 {
1569 ASSERT_ARG(arbitraryRange, arbitraryRange);
1570
1571 ExceptionCode ec = 0;
1572
1573 // Expand range to paragraph boundaries
1574 RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
1575 setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
1576 setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
1577
1578 // Compute offset from start of expanded range to start of original range
1579 RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
1580 offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
1581
1582 // Fill in out parameter with string representing entire paragraph range.
1583 // Someday we might have a caller that doesn't use this, but for now all callers do.
1584 paragraphString = plainText(paragraphRange.get());
1585
1586 return paragraphRange;
1587 }
1588
findFirstGrammarDetailInRange(const Vector<GrammarDetail> & grammarDetails,int badGrammarPhraseLocation,int,Range * searchRange,int startOffset,int endOffset,bool markAll)1589 static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int /*badGrammarPhraseLength*/, Range *searchRange, int startOffset, int endOffset, bool markAll)
1590 {
1591 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1592 // Optionally add a DocumentMarker for each detail in the range.
1593 int earliestDetailLocationSoFar = -1;
1594 int earliestDetailIndex = -1;
1595 for (unsigned i = 0; i < grammarDetails.size(); i++) {
1596 const GrammarDetail* detail = &grammarDetails[i];
1597 ASSERT(detail->length > 0 && detail->location >= 0);
1598
1599 int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
1600
1601 // Skip this detail if it starts before the original search range
1602 if (detailStartOffsetInParagraph < startOffset)
1603 continue;
1604
1605 // Skip this detail if it starts after the original search range
1606 if (detailStartOffsetInParagraph >= endOffset)
1607 continue;
1608
1609 if (markAll) {
1610 RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
1611 ExceptionCode ec = 0;
1612 badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
1613 ASSERT(ec == 0);
1614 }
1615
1616 // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
1617 if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
1618 earliestDetailIndex = i;
1619 earliestDetailLocationSoFar = detail->location;
1620 }
1621 }
1622
1623 return earliestDetailIndex;
1624 }
1625
findFirstBadGrammarInRange(EditorClient * client,Range * searchRange,GrammarDetail & outGrammarDetail,int & outGrammarPhraseOffset,bool markAll)1626 static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
1627 {
1628 ASSERT_ARG(client, client);
1629 ASSERT_ARG(searchRange, searchRange);
1630
1631 // Initialize out parameters; these will be updated if we find something to return.
1632 outGrammarDetail.location = -1;
1633 outGrammarDetail.length = 0;
1634 outGrammarDetail.guesses.clear();
1635 outGrammarDetail.userDescription = "";
1636 outGrammarPhraseOffset = 0;
1637
1638 String firstBadGrammarPhrase;
1639
1640 // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
1641 // Determine the character offset from the start of the paragraph to the start of the original search range,
1642 // since we will want to ignore results in this area.
1643 int searchRangeStartOffset;
1644 String paragraphString;
1645 RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
1646
1647 // Determine the character offset from the start of the paragraph to the end of the original search range,
1648 // since we will want to ignore results in this area also.
1649 int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
1650
1651 // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
1652 int startOffset = 0;
1653 while (startOffset < searchRangeEndOffset) {
1654 Vector<GrammarDetail> grammarDetails;
1655 int badGrammarPhraseLocation = -1;
1656 int badGrammarPhraseLength = 0;
1657 client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
1658
1659 if (badGrammarPhraseLength == 0) {
1660 ASSERT(badGrammarPhraseLocation == -1);
1661 return String();
1662 }
1663
1664 ASSERT(badGrammarPhraseLocation >= 0);
1665 badGrammarPhraseLocation += startOffset;
1666
1667
1668 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1669 int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
1670 if (badGrammarIndex >= 0) {
1671 ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
1672 outGrammarDetail = grammarDetails[badGrammarIndex];
1673 }
1674
1675 // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
1676 // kept going so we could mark all instances).
1677 if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
1678 outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
1679 firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
1680
1681 // Found one. We're done now, unless we're marking each instance.
1682 if (!markAll)
1683 break;
1684 }
1685
1686 // These results were all between the start of the paragraph and the start of the search range; look
1687 // beyond this phrase.
1688 startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
1689 }
1690
1691 return firstBadGrammarPhrase;
1692 }
1693
1694 #endif /* not BUILDING_ON_TIGER */
1695
1696 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1697
findFirstMisspellingOrBadGrammarInRange(EditorClient * client,Range * searchRange,bool checkGrammar,bool & outIsSpelling,int & outFirstFoundOffset,GrammarDetail & outGrammarDetail)1698 static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Range* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
1699 {
1700 ASSERT_ARG(client, client);
1701 ASSERT_ARG(searchRange, searchRange);
1702
1703 String firstFoundItem;
1704 String misspelledWord;
1705 String badGrammarPhrase;
1706 ExceptionCode ec = 0;
1707
1708 // Initialize out parameters; these will be updated if we find something to return.
1709 outIsSpelling = true;
1710 outFirstFoundOffset = 0;
1711 outGrammarDetail.location = -1;
1712 outGrammarDetail.length = 0;
1713 outGrammarDetail.guesses.clear();
1714 outGrammarDetail.userDescription = "";
1715
1716 // Expand the search range to encompass entire paragraphs, since text checking needs that much context.
1717 // Determine the character offset from the start of the paragraph to the start of the original search range,
1718 // since we will want to ignore results in this area.
1719 RefPtr<Range> paragraphRange = searchRange->cloneRange(ec);
1720 setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition()));
1721 int totalRangeLength = TextIterator::rangeLength(paragraphRange.get());
1722 setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition()));
1723
1724 RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->startPosition());
1725 int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
1726 int totalLengthProcessed = 0;
1727
1728 bool firstIteration = true;
1729 bool lastIteration = false;
1730 while (totalLengthProcessed < totalRangeLength) {
1731 // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
1732 int currentLength = TextIterator::rangeLength(paragraphRange.get());
1733 int currentStartOffset = firstIteration ? searchRangeStartOffset : 0;
1734 int currentEndOffset = currentLength;
1735 if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPosition())) {
1736 // Determine the character offset from the end of the original search range to the end of the paragraph,
1737 // since we will want to ignore results in this area.
1738 RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->endPosition());
1739 currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get());
1740 lastIteration = true;
1741 }
1742 if (currentStartOffset < currentEndOffset) {
1743 String paragraphString = plainText(paragraphRange.get());
1744 if (paragraphString.length() > 0) {
1745 bool foundGrammar = false;
1746 int spellingLocation = 0;
1747 int grammarPhraseLocation = 0;
1748 int grammarDetailLocation = 0;
1749 unsigned grammarDetailIndex = 0;
1750
1751 Vector<TextCheckingResult> results;
1752 uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
1753 client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
1754
1755 for (unsigned i = 0; i < results.size(); i++) {
1756 const TextCheckingResult* result = &results[i];
1757 if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
1758 ASSERT(result->length > 0 && result->location >= 0);
1759 spellingLocation = result->location;
1760 misspelledWord = paragraphString.substring(result->location, result->length);
1761 ASSERT(misspelledWord.length() != 0);
1762 break;
1763 } else if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
1764 ASSERT(result->length > 0 && result->location >= 0);
1765 // We can't stop after the first grammar result, since there might still be a spelling result after
1766 // it begins but before the first detail in it, but we can stop if we find a second grammar result.
1767 if (foundGrammar) break;
1768 for (unsigned j = 0; j < result->details.size(); j++) {
1769 const GrammarDetail* detail = &result->details[j];
1770 ASSERT(detail->length > 0 && detail->location >= 0);
1771 if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
1772 grammarDetailIndex = j;
1773 grammarDetailLocation = result->location + detail->location;
1774 foundGrammar = true;
1775 }
1776 }
1777 if (foundGrammar) {
1778 grammarPhraseLocation = result->location;
1779 outGrammarDetail = result->details[grammarDetailIndex];
1780 badGrammarPhrase = paragraphString.substring(result->location, result->length);
1781 ASSERT(badGrammarPhrase.length() != 0);
1782 }
1783 }
1784 }
1785
1786 if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
1787 int spellingOffset = spellingLocation - currentStartOffset;
1788 if (!firstIteration) {
1789 RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
1790 spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
1791 }
1792 outIsSpelling = true;
1793 outFirstFoundOffset = spellingOffset;
1794 firstFoundItem = misspelledWord;
1795 break;
1796 } else if (checkGrammar && !badGrammarPhrase.isEmpty()) {
1797 int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
1798 if (!firstIteration) {
1799 RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
1800 grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
1801 }
1802 outIsSpelling = false;
1803 outFirstFoundOffset = grammarPhraseOffset;
1804 firstFoundItem = badGrammarPhrase;
1805 break;
1806 }
1807 }
1808 }
1809 if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
1810 break;
1811 VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition());
1812 setStart(paragraphRange.get(), newParagraphStart);
1813 setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart));
1814 firstIteration = false;
1815 totalLengthProcessed += currentLength;
1816 }
1817 return firstFoundItem;
1818 }
1819
1820 #endif
1821
advanceToNextMisspelling(bool startBeforeSelection)1822 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1823 {
1824 ExceptionCode ec = 0;
1825
1826 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1827 // then we wrap and search from the doc start to (approximately) where we started.
1828
1829 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
1830 // repeated "check spelling" commands work.
1831 VisibleSelection selection(frame()->selection()->selection());
1832 RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1833 bool startedWithSelection = false;
1834 if (selection.start().node()) {
1835 startedWithSelection = true;
1836 if (startBeforeSelection) {
1837 VisiblePosition start(selection.visibleStart());
1838 // We match AppKit's rule: Start 1 character before the selection.
1839 VisiblePosition oneBeforeStart = start.previous();
1840 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1841 } else
1842 setStart(spellingSearchRange.get(), selection.visibleEnd());
1843 }
1844
1845 Position position = spellingSearchRange->startPosition();
1846 if (!isEditablePosition(position)) {
1847 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1848 // selection is editable.
1849 // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1850 // when spell checking the whole document before sending the message.
1851 // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1852
1853 position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1854 if (position.isNull())
1855 return;
1856
1857 Position rangeCompliantPosition = rangeCompliantEquivalent(position);
1858 spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.deprecatedEditingOffset(), ec);
1859 startedWithSelection = false; // won't need to wrap
1860 }
1861
1862 // topNode defines the whole range we want to operate on
1863 Node* topNode = highestEditableRoot(position);
1864 // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
1865 spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec);
1866
1867 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1868 // at a word boundary. Going back by one char and then forward by a word does the trick.
1869 if (startedWithSelection) {
1870 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1871 if (oneBeforeStart.isNotNull()) {
1872 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1873 } // else we were already at the start of the editable node
1874 }
1875
1876 if (spellingSearchRange->collapsed(ec))
1877 return; // nothing to search in
1878
1879 // Get the spell checker if it is available
1880 if (!client())
1881 return;
1882
1883 // We go to the end of our first range instead of the start of it, just to be sure
1884 // we don't get foiled by any word boundary problems at the start. It means we might
1885 // do a tiny bit more searching.
1886 Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1887 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1888
1889 int misspellingOffset = 0;
1890 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1891 RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1892 String misspelledWord;
1893 String badGrammarPhrase;
1894 int grammarPhraseOffset = 0;
1895 bool isSpelling = true;
1896 int foundOffset = 0;
1897 GrammarDetail grammarDetail;
1898 String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1899 if (isSpelling) {
1900 misspelledWord = foundItem;
1901 misspellingOffset = foundOffset;
1902 } else {
1903 badGrammarPhrase = foundItem;
1904 grammarPhraseOffset = foundOffset;
1905 }
1906 #else
1907 RefPtr<Range> firstMisspellingRange;
1908 String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
1909 String badGrammarPhrase;
1910
1911 #ifndef BUILDING_ON_TIGER
1912 int grammarPhraseOffset = 0;
1913 GrammarDetail grammarDetail;
1914
1915 // Search for bad grammar that occurs prior to the next misspelled word (if any)
1916 RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1917 if (!misspelledWord.isEmpty()) {
1918 // Stop looking at start of next misspelled word
1919 CharacterIterator chars(grammarSearchRange.get());
1920 chars.advance(misspellingOffset);
1921 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1922 }
1923
1924 if (isGrammarCheckingEnabled())
1925 badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1926 #endif
1927 #endif
1928
1929 // 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
1930 // block rather than at a selection).
1931 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1932 spellingSearchRange->setStart(topNode, 0, ec);
1933 // going until the end of the very first chunk we tested is far enough
1934 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1935
1936 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1937 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1938 foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1939 if (isSpelling) {
1940 misspelledWord = foundItem;
1941 misspellingOffset = foundOffset;
1942 } else {
1943 badGrammarPhrase = foundItem;
1944 grammarPhraseOffset = foundOffset;
1945 }
1946 #else
1947 misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
1948
1949 #ifndef BUILDING_ON_TIGER
1950 grammarSearchRange = spellingSearchRange->cloneRange(ec);
1951 if (!misspelledWord.isEmpty()) {
1952 // Stop looking at start of next misspelled word
1953 CharacterIterator chars(grammarSearchRange.get());
1954 chars.advance(misspellingOffset);
1955 grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1956 }
1957 if (isGrammarCheckingEnabled())
1958 badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1959 #endif
1960 #endif
1961 }
1962
1963 if (!badGrammarPhrase.isEmpty()) {
1964 #ifdef BUILDING_ON_TIGER
1965 ASSERT_NOT_REACHED();
1966 #else
1967 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1968 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1969 // panel, and store a marker so we draw the green squiggle later.
1970
1971 ASSERT(badGrammarPhrase.length() > 0);
1972 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1973
1974 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1975 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1976 frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1977 frame()->revealSelection();
1978
1979 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1980 frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1981 #endif
1982 } else if (!misspelledWord.isEmpty()) {
1983 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1984 // a marker so we draw the red squiggle later.
1985
1986 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1987 frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
1988 frame()->revealSelection();
1989
1990 client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1991 frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1992 }
1993 }
1994
isSelectionMisspelled()1995 bool Editor::isSelectionMisspelled()
1996 {
1997 String selectedString = frame()->selectedText();
1998 int length = selectedString.length();
1999 if (length == 0)
2000 return false;
2001
2002 if (!client())
2003 return false;
2004
2005 int misspellingLocation = -1;
2006 int misspellingLength = 0;
2007 client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
2008
2009 // The selection only counts as misspelled if the selected text is exactly one misspelled word
2010 if (misspellingLength != length)
2011 return false;
2012
2013 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
2014 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
2015 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
2016 // or a grammar error.
2017 client()->updateSpellingUIWithMisspelledWord(selectedString);
2018
2019 return true;
2020 }
2021
2022 #ifndef BUILDING_ON_TIGER
isRangeUngrammatical(EditorClient * client,Range * range,Vector<String> & guessesVector)2023 static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
2024 {
2025 if (!client)
2026 return false;
2027
2028 ExceptionCode ec;
2029 if (!range || range->collapsed(ec))
2030 return false;
2031
2032 // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
2033 // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
2034 // or overlapping the range; the ranges must exactly match.
2035 guessesVector.clear();
2036 int grammarPhraseOffset;
2037
2038 GrammarDetail grammarDetail;
2039 String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);
2040
2041 // No bad grammar in these parts at all.
2042 if (badGrammarPhrase.isEmpty())
2043 return false;
2044
2045 // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
2046 if (grammarPhraseOffset > 0)
2047 return false;
2048
2049 ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
2050
2051 // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
2052 if (grammarDetail.location + grammarPhraseOffset != 0)
2053 return false;
2054
2055 // Bad grammar at start of range, but end of bad grammar is before or after end of range
2056 if (grammarDetail.length != TextIterator::rangeLength(range))
2057 return false;
2058
2059 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
2060 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
2061 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
2062 // or a grammar error.
2063 client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
2064
2065 return true;
2066 }
2067 #endif
2068
isSelectionUngrammatical()2069 bool Editor::isSelectionUngrammatical()
2070 {
2071 #ifdef BUILDING_ON_TIGER
2072 return false;
2073 #else
2074 Vector<String> ignoredGuesses;
2075 return isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), ignoredGuesses);
2076 #endif
2077 }
2078
guessesForUngrammaticalSelection()2079 Vector<String> Editor::guessesForUngrammaticalSelection()
2080 {
2081 #ifdef BUILDING_ON_TIGER
2082 return Vector<String>();
2083 #else
2084 Vector<String> guesses;
2085 // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
2086 isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), guesses);
2087 return guesses;
2088 #endif
2089 }
2090
guessesForMisspelledSelection()2091 Vector<String> Editor::guessesForMisspelledSelection()
2092 {
2093 String selectedString = frame()->selectedText();
2094 ASSERT(selectedString.length() != 0);
2095
2096 Vector<String> guesses;
2097 if (client())
2098 client()->getGuessesForWord(selectedString, guesses);
2099 return guesses;
2100 }
2101
2102 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2103
guessesForMisspelledOrUngrammaticalRange(EditorClient * client,Range * range,bool checkGrammar,bool & misspelled,bool & ungrammatical)2104 static Vector<String> guessesForMisspelledOrUngrammaticalRange(EditorClient* client, Range *range, bool checkGrammar, bool& misspelled, bool& ungrammatical)
2105 {
2106 Vector<String> guesses;
2107 ExceptionCode ec;
2108 misspelled = false;
2109 ungrammatical = false;
2110
2111 if (!client || !range || range->collapsed(ec))
2112 return guesses;
2113
2114 // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2115 int rangeStartOffset;
2116 String paragraphString;
2117 RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(range, rangeStartOffset, paragraphString);
2118 int rangeLength = TextIterator::rangeLength(range);
2119 if (rangeLength == 0 || paragraphString.length() == 0)
2120 return guesses;
2121
2122 Vector<TextCheckingResult> results;
2123 uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
2124 client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
2125
2126 for (unsigned i = 0; i < results.size(); i++) {
2127 const TextCheckingResult* result = &results[i];
2128 if (result->type == TextCheckingTypeSpelling && result->location == rangeStartOffset && result->length == rangeLength) {
2129 String misspelledWord = paragraphString.substring(rangeStartOffset, rangeLength);
2130 ASSERT(misspelledWord.length() != 0);
2131 client->getGuessesForWord(misspelledWord, guesses);
2132 client->updateSpellingUIWithMisspelledWord(misspelledWord);
2133 misspelled = true;
2134 return guesses;
2135 }
2136 }
2137
2138 if (!checkGrammar)
2139 return guesses;
2140
2141 for (unsigned i = 0; i < results.size(); i++) {
2142 const TextCheckingResult* result = &results[i];
2143 if (result->type == TextCheckingTypeGrammar && result->location <= rangeStartOffset && result->location + result->length >= rangeStartOffset + rangeLength) {
2144 for (unsigned j = 0; j < result->details.size(); j++) {
2145 const GrammarDetail* detail = &result->details[j];
2146 ASSERT(detail->length > 0 && detail->location >= 0);
2147 if (result->location + detail->location == rangeStartOffset && detail->length == rangeLength) {
2148 String badGrammarPhrase = paragraphString.substring(result->location, result->length);
2149 ASSERT(badGrammarPhrase.length() != 0);
2150 for (unsigned k = 0; k < detail->guesses.size(); k++)
2151 guesses.append(detail->guesses[k]);
2152 client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail);
2153 ungrammatical = true;
2154 return guesses;
2155 }
2156 }
2157 }
2158 }
2159 return guesses;
2160 }
2161
2162 #endif
2163
guessesForMisspelledOrUngrammaticalSelection(bool & misspelled,bool & ungrammatical)2164 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
2165 {
2166 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2167 return guessesForMisspelledOrUngrammaticalRange(client(), frame()->selection()->toNormalizedRange().get(), isGrammarCheckingEnabled(), misspelled, ungrammatical);
2168 #else
2169 misspelled = isSelectionMisspelled();
2170 if (misspelled) {
2171 ungrammatical = false;
2172 return guessesForMisspelledSelection();
2173 }
2174 if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) {
2175 ungrammatical = true;
2176 return guessesForUngrammaticalSelection();
2177 }
2178 ungrammatical = false;
2179 return Vector<String>();
2180 #endif
2181 }
2182
showSpellingGuessPanel()2183 void Editor::showSpellingGuessPanel()
2184 {
2185 if (!client()) {
2186 LOG_ERROR("No NSSpellChecker");
2187 return;
2188 }
2189
2190 #ifndef BUILDING_ON_TIGER
2191 // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
2192 // to match rest of OS X.
2193 if (client()->spellingUIIsShowing()) {
2194 client()->showSpellingUI(false);
2195 return;
2196 }
2197 #endif
2198
2199 advanceToNextMisspelling(true);
2200 client()->showSpellingUI(true);
2201 }
2202
spellingPanelIsShowing()2203 bool Editor::spellingPanelIsShowing()
2204 {
2205 if (!client())
2206 return false;
2207 return client()->spellingUIIsShowing();
2208 }
2209
markMisspellingsAfterTypingToPosition(const VisiblePosition & p)2210 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
2211 {
2212 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2213 bool markSpelling = isContinuousSpellCheckingEnabled();
2214 bool markGrammar = markSpelling && isGrammarCheckingEnabled();
2215 bool performTextCheckingReplacements = isAutomaticQuoteSubstitutionEnabled()
2216 || isAutomaticLinkDetectionEnabled()
2217 || isAutomaticDashSubstitutionEnabled()
2218 || isAutomaticTextReplacementEnabled()
2219 || (markSpelling && isAutomaticSpellingCorrectionEnabled());
2220 if (!markSpelling && !performTextCheckingReplacements)
2221 return;
2222
2223 VisibleSelection adjacentWords = VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary));
2224 if (markGrammar) {
2225 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(p), endOfSentence(p));
2226 markAllMisspellingsAndBadGrammarInRanges(true, adjacentWords.toNormalizedRange().get(), true, selectedSentence.toNormalizedRange().get(), performTextCheckingReplacements);
2227 } else {
2228 markAllMisspellingsAndBadGrammarInRanges(markSpelling, adjacentWords.toNormalizedRange().get(), false, adjacentWords.toNormalizedRange().get(), performTextCheckingReplacements);
2229 }
2230 #else
2231 if (!isContinuousSpellCheckingEnabled())
2232 return;
2233
2234 // Check spelling of one word
2235 RefPtr<Range> misspellingRange;
2236 markMisspellings(VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)), misspellingRange);
2237
2238 // Autocorrect the misspelled word.
2239 if (misspellingRange == 0)
2240 return;
2241
2242 // Get the misspelled word.
2243 const String misspelledWord = plainText(misspellingRange.get());
2244 String autocorrectedString = client()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
2245
2246 // If autocorrected word is non empty, replace the misspelled word by this word.
2247 if (!autocorrectedString.isEmpty()) {
2248 VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
2249 if (newSelection != frame()->selection()->selection()) {
2250 if (!frame()->shouldChangeSelection(newSelection))
2251 return;
2252 frame()->selection()->setSelection(newSelection);
2253 }
2254
2255 if (!frame()->editor()->shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
2256 return;
2257 frame()->editor()->replaceSelectionWithText(autocorrectedString, false, false);
2258
2259 // Reset the charet one character further.
2260 frame()->selection()->moveTo(frame()->selection()->end());
2261 frame()->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2262 }
2263
2264 if (!isGrammarCheckingEnabled())
2265 return;
2266
2267 // Check grammar of entire sentence
2268 markBadGrammar(VisibleSelection(startOfSentence(p), endOfSentence(p)));
2269 #endif
2270 }
2271
markAllMisspellingsInRange(EditorClient * client,Range * searchRange,RefPtr<Range> & firstMisspellingRange)2272 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange, RefPtr<Range>& firstMisspellingRange)
2273 {
2274 // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
2275 // all we need to do is mark every instance.
2276 int ignoredOffset;
2277 findFirstMisspellingInRange(client, searchRange, ignoredOffset, true, firstMisspellingRange);
2278 }
2279
2280 #ifndef BUILDING_ON_TIGER
markAllBadGrammarInRange(EditorClient * client,Range * searchRange)2281 static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
2282 {
2283 // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
2284 // do is mark every instance.
2285 GrammarDetail ignoredGrammarDetail;
2286 int ignoredOffset;
2287 findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
2288 }
2289 #endif
2290
markMisspellingsOrBadGrammar(Editor * editor,const VisibleSelection & selection,bool checkSpelling,RefPtr<Range> & firstMisspellingRange)2291 static void markMisspellingsOrBadGrammar(Editor* editor, const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
2292 {
2293 // This function is called with a selection already expanded to word boundaries.
2294 // Might be nice to assert that here.
2295
2296 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
2297 // grammar checking can only be on if spell checking is also on.
2298 if (!editor->isContinuousSpellCheckingEnabled())
2299 return;
2300
2301 RefPtr<Range> searchRange(selection.toNormalizedRange());
2302 if (!searchRange)
2303 return;
2304
2305 // If we're not in an editable node, bail.
2306 Node* editableNode = searchRange->startContainer();
2307 if (!editableNode || !editableNode->isContentEditable())
2308 return;
2309
2310 if (!editor->spellCheckingEnabledInFocusedNode())
2311 return;
2312
2313 // Get the spell checker if it is available
2314 if (!editor->client())
2315 return;
2316
2317 if (checkSpelling)
2318 markAllMisspellingsInRange(editor->client(), searchRange.get(), firstMisspellingRange);
2319 else {
2320 #ifdef BUILDING_ON_TIGER
2321 ASSERT_NOT_REACHED();
2322 #else
2323 if (editor->isGrammarCheckingEnabled())
2324 markAllBadGrammarInRange(editor->client(), searchRange.get());
2325 #endif
2326 }
2327 }
2328
spellCheckingEnabledInFocusedNode() const2329 bool Editor::spellCheckingEnabledInFocusedNode() const
2330 {
2331 // Ascend the DOM tree to find a "spellcheck" attribute.
2332 // When we find a "spellcheck" attribute, retrieve its value and return false if its value is "false".
2333 const Node* node = frame()->document()->focusedNode();
2334 while (node) {
2335 if (node->isElementNode()) {
2336 const WebCore::AtomicString& value = static_cast<const Element*>(node)->getAttribute(spellcheckAttr);
2337 if (equalIgnoringCase(value, "true"))
2338 return true;
2339 if (equalIgnoringCase(value, "false"))
2340 return false;
2341 }
2342 node = node->parent();
2343 }
2344 return true;
2345 }
2346
markMisspellings(const VisibleSelection & selection,RefPtr<Range> & firstMisspellingRange)2347 void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2348 {
2349 markMisspellingsOrBadGrammar(this, selection, true, firstMisspellingRange);
2350 }
2351
markBadGrammar(const VisibleSelection & selection)2352 void Editor::markBadGrammar(const VisibleSelection& selection)
2353 {
2354 #ifndef BUILDING_ON_TIGER
2355 RefPtr<Range> firstMisspellingRange;
2356 markMisspellingsOrBadGrammar(this, selection, false, firstMisspellingRange);
2357 #else
2358 UNUSED_PARAM(selection);
2359 #endif
2360 }
2361
2362 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2363
isAmbiguousBoundaryCharacter(UChar character)2364 static inline bool isAmbiguousBoundaryCharacter(UChar character)
2365 {
2366 // These are characters that can behave as word boundaries, but can appear within words.
2367 // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
2368 // FIXME: this is required until 6853027 is fixed and text checking can do this for us.
2369 return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
2370 }
2371
markAllMisspellingsAndBadGrammarInRanges(bool markSpelling,Range * spellingRange,bool markGrammar,Range * grammarRange,bool performTextCheckingReplacements)2372 void Editor::markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range* spellingRange, bool markGrammar, Range* grammarRange, bool performTextCheckingReplacements)
2373 {
2374 // This function is called with selections already expanded to word boundaries.
2375 ExceptionCode ec = 0;
2376 if (!client() || !spellingRange || (markGrammar && !grammarRange))
2377 return;
2378
2379 // If we're not in an editable node, bail.
2380 Node* editableNode = spellingRange->startContainer();
2381 if (!editableNode || !editableNode->isContentEditable())
2382 return;
2383
2384 if (!spellCheckingEnabledInFocusedNode())
2385 return;
2386
2387 // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2388 int spellingRangeStartOffset = 0;
2389 int spellingRangeEndOffset = 0;
2390 int grammarRangeStartOffset = 0;
2391 int grammarRangeEndOffset = 0;
2392 int offsetDueToReplacement = 0;
2393 int paragraphLength = 0;
2394 int selectionOffset = 0;
2395 int ambiguousBoundaryOffset = -1;
2396 bool selectionChanged = false;
2397 bool restoreSelectionAfterChange = false;
2398 bool adjustSelectionForParagraphBoundaries = false;
2399 String paragraphString;
2400 RefPtr<Range> paragraphRange;
2401
2402 if (markGrammar) {
2403 // The spelling range should be contained in the paragraph-aligned extension of the grammar range.
2404 paragraphRange = paragraphAlignedRangeForRange(grammarRange, grammarRangeStartOffset, paragraphString);
2405 RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
2406 spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
2407 grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
2408 } else {
2409 paragraphRange = paragraphAlignedRangeForRange(spellingRange, spellingRangeStartOffset, paragraphString);
2410 }
2411 spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
2412 paragraphLength = paragraphString.length();
2413 if (paragraphLength <= 0 || (spellingRangeStartOffset >= spellingRangeEndOffset && (!markGrammar || grammarRangeStartOffset >= grammarRangeEndOffset)))
2414 return;
2415
2416 if (performTextCheckingReplacements) {
2417 if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) {
2418 // Attempt to save the caret position so we can restore it later if needed
2419 RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), paragraphRange->startPosition());
2420 Position caretPosition = m_frame->selection()->end();
2421 offsetAsRange->setEnd(caretPosition.containerNode(), caretPosition.computeOffsetInContainerNode(), ec);
2422 if (!ec) {
2423 selectionOffset = TextIterator::rangeLength(offsetAsRange.get());
2424 restoreSelectionAfterChange = true;
2425 if (selectionOffset > 0 && (selectionOffset > paragraphLength || paragraphString[selectionOffset - 1] == newlineCharacter))
2426 adjustSelectionForParagraphBoundaries = true;
2427 if (selectionOffset > 0 && selectionOffset <= paragraphLength && isAmbiguousBoundaryCharacter(paragraphString[selectionOffset - 1]))
2428 ambiguousBoundaryOffset = selectionOffset - 1;
2429 }
2430 }
2431 }
2432
2433 Vector<TextCheckingResult> results;
2434 uint64_t checkingTypes = 0;
2435 if (markSpelling)
2436 checkingTypes |= TextCheckingTypeSpelling;
2437 if (markGrammar)
2438 checkingTypes |= TextCheckingTypeGrammar;
2439 if (performTextCheckingReplacements) {
2440 if (isAutomaticLinkDetectionEnabled())
2441 checkingTypes |= TextCheckingTypeLink;
2442 if (isAutomaticQuoteSubstitutionEnabled())
2443 checkingTypes |= TextCheckingTypeQuote;
2444 if (isAutomaticDashSubstitutionEnabled())
2445 checkingTypes |= TextCheckingTypeDash;
2446 if (isAutomaticTextReplacementEnabled())
2447 checkingTypes |= TextCheckingTypeReplacement;
2448 if (markSpelling && isAutomaticSpellingCorrectionEnabled())
2449 checkingTypes |= TextCheckingTypeCorrection;
2450 }
2451 client()->checkTextOfParagraph(paragraphString.characters(), paragraphLength, checkingTypes, results);
2452
2453 for (unsigned i = 0; i < results.size(); i++) {
2454 const TextCheckingResult* result = &results[i];
2455 int resultLocation = result->location + offsetDueToReplacement;
2456 int resultLength = result->length;
2457 if (markSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingRangeStartOffset && resultLocation + resultLength <= spellingRangeEndOffset) {
2458 ASSERT(resultLength > 0 && resultLocation >= 0);
2459 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingRange, resultLocation - spellingRangeStartOffset, resultLength);
2460 misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2461 } else if (markGrammar && result->type == TextCheckingTypeGrammar && resultLocation < grammarRangeEndOffset && resultLocation + resultLength > grammarRangeStartOffset) {
2462 ASSERT(resultLength > 0 && resultLocation >= 0);
2463 for (unsigned j = 0; j < result->details.size(); j++) {
2464 const GrammarDetail* detail = &result->details[j];
2465 ASSERT(detail->length > 0 && detail->location >= 0);
2466 if (resultLocation + detail->location >= grammarRangeStartOffset && resultLocation + detail->location + detail->length <= grammarRangeEndOffset) {
2467 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarRange, resultLocation + detail->location - grammarRangeStartOffset, detail->length);
2468 grammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
2469 }
2470 }
2471 } else if (performTextCheckingReplacements && resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingRangeStartOffset &&
2472 (result->type == TextCheckingTypeLink
2473 || result->type == TextCheckingTypeQuote
2474 || result->type == TextCheckingTypeDash
2475 || result->type == TextCheckingTypeReplacement
2476 || result->type == TextCheckingTypeCorrection)) {
2477 // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
2478 ASSERT(resultLength > 0 && resultLocation >= 0);
2479 int replacementLength = result->replacement.length();
2480 bool doReplacement = (replacementLength > 0);
2481 RefPtr<Range> rangeToReplace = TextIterator::subrange(paragraphRange.get(), resultLocation, resultLength);
2482 VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM);
2483
2484 // avoid correcting text after an ambiguous boundary character has been typed
2485 // FIXME: this is required until 6853027 is fixed and text checking can do this for us
2486 if (ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset)
2487 doReplacement = false;
2488
2489 // adding links should be done only immediately after they are typed
2490 if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1)
2491 doReplacement = false;
2492
2493 // Don't correct spelling in an already-corrected word.
2494 if (doReplacement && result->type == TextCheckingTypeCorrection) {
2495 Node* node = rangeToReplace->startContainer();
2496 int startOffset = rangeToReplace->startOffset();
2497 int endOffset = startOffset + replacementLength;
2498 Vector<DocumentMarker> markers = node->document()->markersForNode(node);
2499 size_t markerCount = markers.size();
2500 for (size_t i = 0; i < markerCount; ++i) {
2501 const DocumentMarker& marker = markers[i];
2502 if (marker.type == DocumentMarker::Replacement && static_cast<int>(marker.startOffset) < endOffset && static_cast<int>(marker.endOffset) > startOffset) {
2503 doReplacement = false;
2504 break;
2505 }
2506 if (static_cast<int>(marker.startOffset) >= endOffset)
2507 break;
2508 }
2509 }
2510 if (doReplacement && selectionToReplace != m_frame->selection()->selection()) {
2511 if (m_frame->shouldChangeSelection(selectionToReplace)) {
2512 m_frame->selection()->setSelection(selectionToReplace);
2513 selectionChanged = true;
2514 } else {
2515 doReplacement = false;
2516 }
2517 }
2518 if (doReplacement) {
2519 if (result->type == TextCheckingTypeLink) {
2520 restoreSelectionAfterChange = false;
2521 if (canEditRichly())
2522 applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
2523 } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
2524 String replacedString;
2525 if (result->type == TextCheckingTypeCorrection)
2526 replacedString = plainText(rangeToReplace.get());
2527 replaceSelectionWithText(result->replacement, false, false);
2528 spellingRangeEndOffset += replacementLength - resultLength;
2529 offsetDueToReplacement += replacementLength - resultLength;
2530 if (resultLocation < selectionOffset)
2531 selectionOffset += replacementLength - resultLength;
2532 if (result->type == TextCheckingTypeCorrection) {
2533 // Add a marker so that corrections can easily be undone and won't be re-corrected.
2534 RefPtr<Range> replacedRange = TextIterator::subrange(paragraphRange.get(), resultLocation, replacementLength);
2535 replacedRange->startContainer()->document()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString);
2536 }
2537 }
2538 }
2539 }
2540 }
2541
2542 if (selectionChanged) {
2543 // Restore the caret position if we have made any replacements
2544 setEnd(paragraphRange.get(), endOfParagraph(startOfNextParagraph(paragraphRange->startPosition())));
2545 int newLength = TextIterator::rangeLength(paragraphRange.get());
2546 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= newLength) {
2547 RefPtr<Range> selectionRange = TextIterator::subrange(paragraphRange.get(), 0, selectionOffset);
2548 m_frame->selection()->moveTo(selectionRange->endPosition(), DOWNSTREAM);
2549 if (adjustSelectionForParagraphBoundaries)
2550 m_frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2551 } else {
2552 // If this fails for any reason, the fallback is to go one position beyond the last replacement
2553 m_frame->selection()->moveTo(m_frame->selection()->end());
2554 m_frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2555 }
2556 }
2557 }
2558
changeBackToReplacedString(const String & replacedString)2559 void Editor::changeBackToReplacedString(const String& replacedString)
2560 {
2561 if (replacedString.isEmpty())
2562 return;
2563
2564 RefPtr<Range> selection = selectedRange();
2565 if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
2566 return;
2567
2568 String paragraphString;
2569 int selectionOffset;
2570 RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(selection.get(), selectionOffset, paragraphString);
2571 replaceSelectionWithText(replacedString, false, false);
2572 RefPtr<Range> changedRange = TextIterator::subrange(paragraphRange.get(), selectionOffset, replacedString.length());
2573 changedRange->startContainer()->document()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
2574 }
2575
2576 #endif
2577
markMisspellingsAndBadGrammar(const VisibleSelection & spellingSelection,bool markGrammar,const VisibleSelection & grammarSelection)2578 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
2579 {
2580 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2581 if (!isContinuousSpellCheckingEnabled())
2582 return;
2583 markAllMisspellingsAndBadGrammarInRanges(true, spellingSelection.toNormalizedRange().get(), markGrammar && isGrammarCheckingEnabled(), grammarSelection.toNormalizedRange().get(), false);
2584 #else
2585 RefPtr<Range> firstMisspellingRange;
2586 markMisspellings(spellingSelection, firstMisspellingRange);
2587 if (markGrammar)
2588 markBadGrammar(grammarSelection);
2589 #endif
2590 }
2591
rangeForPoint(const IntPoint & windowPoint)2592 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
2593 {
2594 Document* document = m_frame->documentAtPoint(windowPoint);
2595 if (!document)
2596 return 0;
2597
2598 Frame* frame = document->frame();
2599 ASSERT(frame);
2600 FrameView* frameView = frame->view();
2601 if (!frameView)
2602 return 0;
2603 IntPoint framePoint = frameView->windowToContents(windowPoint);
2604 VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
2605 return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
2606 }
2607
revealSelectionAfterEditingOperation()2608 void Editor::revealSelectionAfterEditingOperation()
2609 {
2610 if (m_ignoreCompositionSelectionChange)
2611 return;
2612
2613 m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2614 }
2615
setIgnoreCompositionSelectionChange(bool ignore)2616 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
2617 {
2618 if (m_ignoreCompositionSelectionChange == ignore)
2619 return;
2620
2621 m_ignoreCompositionSelectionChange = ignore;
2622 if (!ignore)
2623 revealSelectionAfterEditingOperation();
2624 }
2625
compositionRange() const2626 PassRefPtr<Range> Editor::compositionRange() const
2627 {
2628 if (!m_compositionNode)
2629 return 0;
2630 unsigned length = m_compositionNode->length();
2631 unsigned start = min(m_compositionStart, length);
2632 unsigned end = min(max(start, m_compositionEnd), length);
2633 if (start >= end)
2634 return 0;
2635 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
2636 }
2637
getCompositionSelection(unsigned & selectionStart,unsigned & selectionEnd) const2638 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
2639 {
2640 if (!m_compositionNode)
2641 return false;
2642 Position start = m_frame->selection()->start();
2643 if (start.node() != m_compositionNode)
2644 return false;
2645 Position end = m_frame->selection()->end();
2646 if (end.node() != m_compositionNode)
2647 return false;
2648
2649 if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart)
2650 return false;
2651 if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd)
2652 return false;
2653
2654 selectionStart = start.deprecatedEditingOffset() - m_compositionStart;
2655 selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd;
2656 return true;
2657 }
2658
transpose()2659 void Editor::transpose()
2660 {
2661 if (!canEdit())
2662 return;
2663
2664 VisibleSelection selection = m_frame->selection()->selection();
2665 if (!selection.isCaret())
2666 return;
2667
2668 // Make a selection that goes back one character and forward two characters.
2669 VisiblePosition caret = selection.visibleStart();
2670 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
2671 VisiblePosition previous = next.previous();
2672 if (next == previous)
2673 return;
2674 previous = previous.previous();
2675 if (!inSameParagraph(next, previous))
2676 return;
2677 RefPtr<Range> range = makeRange(previous, next);
2678 if (!range)
2679 return;
2680 VisibleSelection newSelection(range.get(), DOWNSTREAM);
2681
2682 // Transpose the two characters.
2683 String text = plainText(range.get());
2684 if (text.length() != 2)
2685 return;
2686 String transposed = text.right(1) + text.left(1);
2687
2688 // Select the two characters.
2689 if (newSelection != m_frame->selection()->selection()) {
2690 if (!m_frame->shouldChangeSelection(newSelection))
2691 return;
2692 m_frame->selection()->setSelection(newSelection);
2693 }
2694
2695 // Insert the transposed characters.
2696 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2697 return;
2698 replaceSelectionWithText(transposed, false, false);
2699 }
2700
addToKillRing(Range * range,bool prepend)2701 void Editor::addToKillRing(Range* range, bool prepend)
2702 {
2703 if (m_shouldStartNewKillRingSequence)
2704 startNewKillRingSequence();
2705
2706 String text = m_frame->displayStringModifiedByEncoding(plainText(range));
2707 if (prepend)
2708 prependToKillRing(text);
2709 else
2710 appendToKillRing(text);
2711 m_shouldStartNewKillRingSequence = false;
2712 }
2713
2714 #if !PLATFORM(MAC)
2715
appendToKillRing(const String &)2716 void Editor::appendToKillRing(const String&)
2717 {
2718 }
2719
prependToKillRing(const String &)2720 void Editor::prependToKillRing(const String&)
2721 {
2722 }
2723
yankFromKillRing()2724 String Editor::yankFromKillRing()
2725 {
2726 return String();
2727 }
2728
startNewKillRingSequence()2729 void Editor::startNewKillRingSequence()
2730 {
2731 }
2732
setKillRingToYankedState()2733 void Editor::setKillRingToYankedState()
2734 {
2735 }
2736
2737 #endif
2738
insideVisibleArea(const IntPoint & point) const2739 bool Editor::insideVisibleArea(const IntPoint& point) const
2740 {
2741 if (m_frame->excludeFromTextSearch())
2742 return false;
2743
2744 // Right now, we only check the visibility of a point for disconnected frames. For all other
2745 // frames, we assume visibility.
2746 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2747 if (!frame->isDisconnected())
2748 return true;
2749
2750 RenderPart* renderer = frame->ownerRenderer();
2751 if (!renderer)
2752 return false;
2753
2754 RenderBlock* container = renderer->containingBlock();
2755 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2756 return true;
2757
2758 IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2759 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2760 rectInPageCoords.width(), rectInPageCoords.height());
2761
2762 return rectInFrameCoords.contains(point);
2763 }
2764
insideVisibleArea(Range * range) const2765 bool Editor::insideVisibleArea(Range* range) const
2766 {
2767 if (!range)
2768 return true;
2769
2770 if (m_frame->excludeFromTextSearch())
2771 return false;
2772
2773 // Right now, we only check the visibility of a range for disconnected frames. For all other
2774 // frames, we assume visibility.
2775 Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2776 if (!frame->isDisconnected())
2777 return true;
2778
2779 RenderPart* renderer = frame->ownerRenderer();
2780 if (!renderer)
2781 return false;
2782
2783 RenderBlock* container = renderer->containingBlock();
2784 if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2785 return true;
2786
2787 IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2788 IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2789 rectInPageCoords.width(), rectInPageCoords.height());
2790 IntRect resultRect = range->boundingBox();
2791
2792 return rectInFrameCoords.contains(resultRect);
2793 }
2794
firstVisibleRange(const String & target,bool caseFlag)2795 PassRefPtr<Range> Editor::firstVisibleRange(const String& target, bool caseFlag)
2796 {
2797 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2798 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2799 ExceptionCode ec = 0;
2800
2801 while (!insideVisibleArea(resultRange.get())) {
2802 searchRange->setStartAfter(resultRange->endContainer(), ec);
2803 if (searchRange->startContainer() == searchRange->endContainer())
2804 return Range::create(m_frame->document());
2805 resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2806 }
2807
2808 return resultRange;
2809 }
2810
lastVisibleRange(const String & target,bool caseFlag)2811 PassRefPtr<Range> Editor::lastVisibleRange(const String& target, bool caseFlag)
2812 {
2813 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2814 RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2815 ExceptionCode ec = 0;
2816
2817 while (!insideVisibleArea(resultRange.get())) {
2818 searchRange->setEndBefore(resultRange->startContainer(), ec);
2819 if (searchRange->startContainer() == searchRange->endContainer())
2820 return Range::create(m_frame->document());
2821 resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2822 }
2823
2824 return resultRange;
2825 }
2826
nextVisibleRange(Range * currentRange,const String & target,bool forward,bool caseFlag,bool wrapFlag)2827 PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, bool forward, bool caseFlag, bool wrapFlag)
2828 {
2829 if (m_frame->excludeFromTextSearch())
2830 return Range::create(m_frame->document());
2831
2832 RefPtr<Range> resultRange = currentRange;
2833 RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2834 ExceptionCode ec = 0;
2835
2836 for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, forward, caseFlag)) {
2837 if (resultRange->collapsed(ec)) {
2838 if (!resultRange->startContainer()->isInShadowTree())
2839 break;
2840 searchRange = rangeOfContents(m_frame->document());
2841 if (forward)
2842 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
2843 else
2844 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec);
2845 continue;
2846 }
2847
2848 if (forward)
2849 searchRange->setStartAfter(resultRange->endContainer(), ec);
2850 else
2851 searchRange->setEndBefore(resultRange->startContainer(), ec);
2852
2853 Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
2854 if (searchRange->collapsed(ec) && shadowTreeRoot) {
2855 if (forward)
2856 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
2857 else
2858 searchRange->setStartBefore(shadowTreeRoot, ec);
2859 }
2860
2861 if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode())
2862 break;
2863 }
2864
2865 if (insideVisibleArea(resultRange.get()))
2866 return resultRange;
2867
2868 if (!wrapFlag)
2869 return Range::create(m_frame->document());
2870
2871 if (forward)
2872 return firstVisibleRange(target, caseFlag);
2873
2874 return lastVisibleRange(target, caseFlag);
2875 }
2876
changeSelectionAfterCommand(const VisibleSelection & newSelection,bool closeTyping,bool clearTypingStyle,EditCommand * cmd)2877 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle, EditCommand* cmd)
2878 {
2879 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
2880 // because there is work that it must do in this situation.
2881 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
2882 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
2883 bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection();
2884 if (selectionDidNotChangeDOMPosition || m_frame->shouldChangeSelection(newSelection))
2885 m_frame->selection()->setSelection(newSelection, closeTyping, clearTypingStyle);
2886
2887 // Some kinds of deletes and line break insertions change the selection's position within the document without
2888 // changing its position within the DOM. For example when you press return in the following (the caret is marked by ^):
2889 // <div contentEditable="true"><div>^Hello</div></div>
2890 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
2891 // change the caret's DOM position (["hello", 0]). In these situations the above SelectionController::setSelection call
2892 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
2893 // starts a new kill ring sequence, but we want to do these things (matches AppKit).
2894 if (selectionDidNotChangeDOMPosition && cmd->isTypingCommand())
2895 client()->respondToChangedSelection();
2896 }
2897
2898 } // namespace WebCore
2899