BlobRegistry no longer needs SessionIDs
[WebKit-https.git] / Source / WebCore / editing / TypingCommand.cpp
1 /*
2  * Copyright (C) 2005-2019 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "TypingCommand.h"
28
29 #include "AXObjectCache.h"
30 #include "BreakBlockquoteCommand.h"
31 #include "DataTransfer.h"
32 #include "DeleteSelectionCommand.h"
33 #include "Document.h"
34 #include "Editing.h"
35 #include "Editor.h"
36 #include "Element.h"
37 #include "Frame.h"
38 #include "HTMLElement.h"
39 #include "HTMLNames.h"
40 #include "InsertLineBreakCommand.h"
41 #include "InsertParagraphSeparatorCommand.h"
42 #include "InsertTextCommand.h"
43 #include "Logging.h"
44 #include "MarkupAccumulator.h"
45 #include "MathMLElement.h"
46 #include "RenderElement.h"
47 #include "StaticRange.h"
48 #include "TextIterator.h"
49 #include "VisibleUnits.h"
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
55 class TypingCommandLineOperation
56 {
57 public:
58     TypingCommandLineOperation(TypingCommand* typingCommand, bool selectInsertedText, const String& text)
59     : m_typingCommand(typingCommand)
60     , m_selectInsertedText(selectInsertedText)
61     , m_text(text)
62     { }
63     
64     void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const
65     {
66         if (isLastLine) {
67             if (!lineOffset || lineLength > 0)
68                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), m_selectInsertedText);
69         } else {
70             if (lineLength > 0)
71                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), false);
72             m_typingCommand->insertParagraphSeparator();
73         }
74     }
75     
76 private:
77     TypingCommand* m_typingCommand;
78     bool m_selectInsertedText;
79     const String& m_text;
80 };
81
82 static inline EditAction editActionForTypingCommand(TypingCommand::ETypingCommand command, TextGranularity granularity, TypingCommand::TextCompositionType compositionType, bool isAutocompletion)
83 {
84     if (compositionType == TypingCommand::TextCompositionPending) {
85         if (command == TypingCommand::InsertText)
86             return EditAction::TypingInsertPendingComposition;
87         if (command == TypingCommand::DeleteSelection)
88             return EditAction::TypingDeletePendingComposition;
89         ASSERT_NOT_REACHED();
90     }
91
92     if (compositionType == TypingCommand::TextCompositionFinal) {
93         if (command == TypingCommand::InsertText)
94             return EditAction::TypingInsertFinalComposition;
95         if (command == TypingCommand::DeleteSelection)
96             return EditAction::TypingDeleteFinalComposition;
97         ASSERT_NOT_REACHED();
98     }
99
100     switch (command) {
101     case TypingCommand::DeleteSelection:
102         return EditAction::TypingDeleteSelection;
103     case TypingCommand::DeleteKey: {
104         if (granularity == WordGranularity)
105             return EditAction::TypingDeleteWordBackward;
106         if (granularity == LineBoundary)
107             return EditAction::TypingDeleteLineBackward;
108         return EditAction::TypingDeleteBackward;
109     }
110     case TypingCommand::ForwardDeleteKey:
111         if (granularity == WordGranularity)
112             return EditAction::TypingDeleteWordForward;
113         if (granularity == LineBoundary)
114             return EditAction::TypingDeleteLineForward;
115         return EditAction::TypingDeleteForward;
116     case TypingCommand::InsertText:
117         return isAutocompletion ? EditAction::InsertReplacement : EditAction::TypingInsertText;
118     case TypingCommand::InsertLineBreak:
119         return EditAction::TypingInsertLineBreak;
120     case TypingCommand::InsertParagraphSeparator:
121     case TypingCommand::InsertParagraphSeparatorInQuotedContent:
122         return EditAction::TypingInsertParagraph;
123     default:
124         return EditAction::Unspecified;
125     }
126 }
127
128 static inline bool editActionIsDeleteByTyping(EditAction action)
129 {
130     switch (action) {
131     case EditAction::TypingDeleteSelection:
132     case EditAction::TypingDeleteBackward:
133     case EditAction::TypingDeleteWordBackward:
134     case EditAction::TypingDeleteLineBackward:
135     case EditAction::TypingDeleteForward:
136     case EditAction::TypingDeleteWordForward:
137     case EditAction::TypingDeleteLineForward:
138         return true;
139     default:
140         return false;
141     }
142 }
143
144 TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
145     : TextInsertionBaseCommand(document, editActionForTypingCommand(commandType, granularity, compositionType, options & IsAutocompletion))
146     , m_commandType(commandType)
147     , m_textToInsert(textToInsert)
148     , m_currentTextToInsert(textToInsert)
149     , m_openForMoreTyping(true)
150     , m_selectInsertedText(options & SelectInsertedText)
151     , m_smartDelete(options & SmartDelete)
152     , m_granularity(granularity)
153     , m_compositionType(compositionType)
154     , m_shouldAddToKillRing(options & AddsToKillRing)
155     , m_isAutocompletion(options & IsAutocompletion)
156     , m_openedByBackwardDelete(false)
157     , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
158     , m_shouldPreventSpellChecking(options & PreventSpellChecking)
159 {
160     m_currentTypingEditAction = editingAction();
161     updatePreservesTypingStyle(m_commandType);
162 }
163
164 void TypingCommand::deleteSelection(Document& document, Options options, TextCompositionType compositionType)
165 {
166     Frame* frame = document.frame();
167     ASSERT(frame);
168
169     if (!frame->selection().isRange())
170         return;
171
172     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
173         lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
174         lastTypingCommand->setCompositionType(compositionType);
175         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
176         lastTypingCommand->deleteSelection(options & SmartDelete);
177         return;
178     }
179
180     TypingCommand::create(document, DeleteSelection, emptyString(), options, compositionType)->apply();
181 }
182
183 void TypingCommand::deleteKeyPressed(Document& document, Options options, TextGranularity granularity)
184 {
185     if (granularity == CharacterGranularity) {
186         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
187             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), document.frame());
188             lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
189             lastTypingCommand->setCompositionType(TextCompositionNone);
190             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
191             lastTypingCommand->deleteKeyPressed(granularity, options & AddsToKillRing);
192             return;
193         }
194     }
195
196     TypingCommand::create(document, DeleteKey, emptyString(), options, granularity)->apply();
197 }
198
199 void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options, TextGranularity granularity)
200 {
201     // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
202     Frame* frame = document.frame();
203     if (granularity == CharacterGranularity) {
204         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
205             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
206             lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
207             lastTypingCommand->setCompositionType(TextCompositionNone);
208             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
209             lastTypingCommand->forwardDeleteKeyPressed(granularity, options & AddsToKillRing);
210             return;
211         }
212     }
213
214     TypingCommand::create(document, ForwardDeleteKey, emptyString(), options, granularity)->apply();
215 }
216
217 void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
218 {
219     ASSERT(frame);
220     VisibleSelection currentSelection = frame->selection().selection();
221     if (currentSelection == typingCommand->endingSelection())
222         return;
223
224     typingCommand->setStartingSelection(currentSelection);
225     typingCommand->setEndingSelection(currentSelection);
226 }
227
228 void TypingCommand::insertText(Document& document, const String& text, Options options, TextCompositionType composition)
229 {
230     Frame* frame = document.frame();
231     ASSERT(frame);
232
233     if (!text.isEmpty())
234         frame->editor().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
235     
236     insertText(document, text, frame->selection().selection(), options, composition);
237 }
238
239 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to FrameSelection's current selection.
240 void TypingCommand::insertText(Document& document, const String& text, const VisibleSelection& selectionForInsertion, Options options, TextCompositionType compositionType)
241 {
242     RefPtr<Frame> frame = document.frame();
243     ASSERT(frame);
244
245     LOG(Editing, "TypingCommand::insertText (text %s)", text.utf8().data());
246
247     VisibleSelection currentSelection = frame->selection().selection();
248
249     String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionPending);
250     
251     // Set the starting and ending selection appropriately if we are using a selection
252     // that is different from the current selection.  In the future, we should change EditCommand
253     // to deal with custom selections in a general way that can be used by all of the commands.
254     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
255         if (lastTypingCommand->endingSelection() != selectionForInsertion) {
256             lastTypingCommand->setStartingSelection(selectionForInsertion);
257             lastTypingCommand->setEndingSelection(selectionForInsertion);
258         }
259
260         lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
261         lastTypingCommand->setCompositionType(compositionType);
262         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
263         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
264         lastTypingCommand->insertTextAndNotifyAccessibility(newText, options & SelectInsertedText);
265         return;
266     }
267
268     auto cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
269     applyTextInsertionCommand(frame.get(), cmd.get(), selectionForInsertion, currentSelection);
270 }
271
272 void TypingCommand::insertLineBreak(Document& document, Options options)
273 {
274     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
275         lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
276         lastTypingCommand->setCompositionType(TextCompositionNone);
277         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
278         lastTypingCommand->insertLineBreakAndNotifyAccessibility();
279         return;
280     }
281
282     TypingCommand::create(document, InsertLineBreak, emptyString(), options)->apply();
283 }
284
285 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
286 {
287     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
288         lastTypingCommand->setIsAutocompletion(false);
289         lastTypingCommand->setCompositionType(TextCompositionNone);
290         lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
291         return;
292     }
293
294     TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)->apply();
295 }
296
297 void TypingCommand::insertParagraphSeparator(Document& document, Options options)
298 {
299     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
300         lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
301         lastTypingCommand->setCompositionType(TextCompositionNone);
302         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
303         lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
304         return;
305     }
306
307     TypingCommand::create(document, InsertParagraphSeparator, emptyString(), options)->apply();
308 }
309
310 RefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame& frame)
311 {
312     RefPtr<CompositeEditCommand> lastEditCommand = frame.editor().lastEditCommand();
313     if (!lastEditCommand || !lastEditCommand->isTypingCommand() || !static_cast<TypingCommand*>(lastEditCommand.get())->isOpenForMoreTyping())
314         return nullptr;
315
316     return static_cast<TypingCommand*>(lastEditCommand.get());
317 }
318
319 bool TypingCommand::shouldDeferWillApplyCommandUntilAddingTypingCommand() const
320 {
321     return !m_isHandlingInitialTypingCommand || editActionIsDeleteByTyping(editingAction());
322 }
323
324 void TypingCommand::closeTyping(Frame* frame)
325 {
326     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame))
327         lastTypingCommand->closeTyping();
328 }
329
330 #if PLATFORM(IOS_FAMILY)
331 void TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(Frame* frame, const VisibleSelection& newSelection)
332 {
333     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
334         lastTypingCommand->setEndingSelection(newSelection);
335         lastTypingCommand->setEndingSelectionOnLastInsertCommand(newSelection);
336     }
337 }
338 #endif
339
340 void TypingCommand::postTextStateChangeNotificationForDeletion(const VisibleSelection& selection)
341 {
342     if (!AXObjectCache::accessibilityEnabled())
343         return;
344     postTextStateChangeNotification(AXTextEditTypeDelete, AccessibilityObject::stringForVisiblePositionRange(selection), selection.start());
345     VisiblePositionIndexRange range;
346     range.startIndex.value = indexForVisiblePosition(selection.start(), range.startIndex.scope);
347     range.endIndex.value = indexForVisiblePosition(selection.end(), range.endIndex.scope);
348     composition()->setRangeDeletedByUnapply(range);
349 }
350
351 bool TypingCommand::willApplyCommand()
352 {
353     if (shouldDeferWillApplyCommandUntilAddingTypingCommand()) {
354         // The TypingCommand will handle the willApplyCommand logic separately in TypingCommand::willAddTypingToOpenCommand.
355         return true;
356     }
357
358     return CompositeEditCommand::willApplyCommand();
359 }
360
361 void TypingCommand::doApply()
362 {
363     if (endingSelection().isNoneOrOrphaned())
364         return;
365
366     if (m_commandType == DeleteKey)
367         if (m_commands.isEmpty())
368             m_openedByBackwardDelete = true;
369
370     switch (m_commandType) {
371     case DeleteSelection:
372         deleteSelection(m_smartDelete);
373         return;
374     case DeleteKey:
375         deleteKeyPressed(m_granularity, m_shouldAddToKillRing);
376         return;
377     case ForwardDeleteKey:
378         forwardDeleteKeyPressed(m_granularity, m_shouldAddToKillRing);
379         return;
380     case InsertLineBreak:
381         insertLineBreakAndNotifyAccessibility();
382         return;
383     case InsertParagraphSeparator:
384         insertParagraphSeparatorAndNotifyAccessibility();
385         return;
386     case InsertParagraphSeparatorInQuotedContent:
387         insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
388         return;
389     case InsertText:
390         insertTextAndNotifyAccessibility(m_textToInsert, m_selectInsertedText);
391         return;
392     }
393
394     ASSERT_NOT_REACHED();
395 }
396
397 String TypingCommand::inputEventTypeName() const
398 {
399     return inputTypeNameForEditingAction(m_currentTypingEditAction);
400 }
401
402 bool TypingCommand::isBeforeInputEventCancelable() const
403 {
404     return m_currentTypingEditAction != EditAction::TypingInsertPendingComposition && m_currentTypingEditAction != EditAction::TypingDeletePendingComposition;
405 }
406
407 String TypingCommand::inputEventData() const
408 {
409     switch (m_currentTypingEditAction) {
410     case EditAction::TypingInsertText:
411     case EditAction::TypingInsertPendingComposition:
412     case EditAction::TypingInsertFinalComposition:
413         return m_currentTextToInsert;
414     case EditAction::InsertReplacement:
415         return isEditingTextAreaOrTextInput() ? m_currentTextToInsert : String();
416     default:
417         return CompositeEditCommand::inputEventData();
418     }
419 }
420
421 RefPtr<DataTransfer> TypingCommand::inputEventDataTransfer() const
422 {
423     if (m_currentTypingEditAction != EditAction::InsertReplacement || isEditingTextAreaOrTextInput())
424         return nullptr;
425
426     StringBuilder htmlText;
427     MarkupAccumulator::appendCharactersReplacingEntities(htmlText, m_currentTextToInsert, 0, m_currentTextToInsert.length(), EntityMaskInHTMLPCDATA);
428     return DataTransfer::createForInputEvent(m_currentTextToInsert, htmlText.toString());
429 }
430
431 void TypingCommand::didApplyCommand()
432 {
433     // TypingCommands handle applied editing separately (see TypingCommand::typingAddedToOpenCommand).
434     m_isHandlingInitialTypingCommand = false;
435 }
436
437 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
438 {
439     Frame& frame = this->frame();
440
441 #if PLATFORM(MAC)
442     if (!frame.editor().isContinuousSpellCheckingEnabled()
443         && !frame.editor().isAutomaticQuoteSubstitutionEnabled()
444         && !frame.editor().isAutomaticLinkDetectionEnabled()
445         && !frame.editor().isAutomaticDashSubstitutionEnabled()
446         && !frame.editor().isAutomaticTextReplacementEnabled())
447             return;
448     if (frame.editor().isHandlingAcceptedCandidate())
449         return;
450 #else
451     if (!frame.editor().isContinuousSpellCheckingEnabled())
452         return;
453 #endif
454     // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
455     // Since the word containing the current selection is never marked, this does a check to
456     // see if typing made a new word that is not in the current selection. Basically, you
457     // get this by being at the end of a word and typing a space.    
458     VisiblePosition start(endingSelection().start(), endingSelection().affinity());
459     VisiblePosition previous = start.previous();
460     if (previous.isNotNull()) {
461 #if !PLATFORM(IOS_FAMILY)
462         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
463         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
464         if (p1 != p2) {
465             RefPtr<Range> range = makeRange(p1, p2);
466             String strippedPreviousWord;
467             if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
468                 strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
469             frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
470         } else if (commandType == TypingCommand::InsertText)
471             frame.editor().startAlternativeTextUITimer();
472 #else
473         UNUSED_PARAM(commandType);
474         // If this bug gets fixed, this PLATFORM(IOS_FAMILY) code could be removed:
475         // <rdar://problem/7259611> Word boundary code on iPhone gives different results than desktop
476         EWordSide startWordSide = LeftWordIfOnBoundary;
477         UChar32 c = previous.characterAfter();
478         // FIXME: VisiblePosition::characterAfter() and characterBefore() do not emit newlines the same
479         // way as TextIterator, so we do an isEndOfParagraph check here.
480         if (isSpaceOrNewline(c) || c == noBreakSpace || isEndOfParagraph(previous)) {
481             startWordSide = RightWordIfOnBoundary;
482         }
483         VisiblePosition p1 = startOfWord(previous, startWordSide);
484         VisiblePosition p2 = startOfWord(start, startWordSide);
485         if (p1 != p2)
486             frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), false);
487 #endif // !PLATFORM(IOS_FAMILY)
488     }
489 }
490
491 bool TypingCommand::willAddTypingToOpenCommand(ETypingCommand commandType, TextGranularity granularity, const String& text, RefPtr<Range>&& range)
492 {
493     m_currentTextToInsert = text;
494     m_currentTypingEditAction = editActionForTypingCommand(commandType, granularity, m_compositionType, m_isAutocompletion);
495
496     if (!shouldDeferWillApplyCommandUntilAddingTypingCommand())
497         return true;
498
499     if (!range || isEditingTextAreaOrTextInput())
500         return frame().editor().willApplyEditing(*this, CompositeEditCommand::targetRangesForBindings());
501
502     return frame().editor().willApplyEditing(*this, { 1, StaticRange::createFromRange(*range) });
503 }
504
505 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
506 {
507     Frame& frame = this->frame();
508
509     updatePreservesTypingStyle(commandTypeForAddedTyping);
510
511 #if PLATFORM(COCOA)
512     frame.editor().appliedEditing(*this);
513     // Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
514     if (!m_shouldPreventSpellChecking)
515         markMisspellingsAfterTyping(commandTypeForAddedTyping);
516 #else
517     // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
518     markMisspellingsAfterTyping(commandTypeForAddedTyping);
519     frame.editor().appliedEditing(*this);
520 #endif
521 }
522
523 void TypingCommand::insertText(const String &text, bool selectInsertedText)
524 {
525     // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
526     // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
527     // an existing selection; at the moment they can either put the caret after what's inserted or
528     // select what's inserted, but there's no way to "extend selection" to include both an old selection
529     // that ends just before where we want to insert text and the newly inserted text.
530     TypingCommandLineOperation operation(this, selectInsertedText, text);
531     forEachLineInString(text, operation);
532 }
533
534 void TypingCommand::insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText)
535 {
536     LOG(Editing, "TypingCommand %p insertTextAndNotifyAccessibility (text %s, selectInsertedText %d)", this, text.utf8().data(), selectInsertedText);
537
538     AccessibilityReplacedText replacedText(frame().selection().selection());
539     insertText(text, selectInsertedText);
540     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, text, frame().selection().selection());
541     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
542 }
543
544 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
545 {
546     if (!willAddTypingToOpenCommand(InsertText, CharacterGranularity, text))
547         return;
548
549     auto command = InsertTextCommand::create(document(), text, selectInsertedText,
550         m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces, EditAction::TypingInsertText);
551
552     applyCommandToComposite(WTFMove(command), endingSelection());
553
554     Frame& frame = this->frame();
555     Ref<Frame> protector(frame);
556     typingAddedToOpenCommand(InsertText);
557 }
558
559 void TypingCommand::insertLineBreak()
560 {
561     if (!canAppendNewLineFeedToSelection(endingSelection()))
562         return;
563
564     if (!willAddTypingToOpenCommand(InsertLineBreak, LineGranularity))
565         return;
566
567     applyCommandToComposite(InsertLineBreakCommand::create(document()));
568
569     Frame& frame = this->frame();
570     Ref<Frame> protector(frame);
571     typingAddedToOpenCommand(InsertLineBreak);
572 }
573
574 void TypingCommand::insertLineBreakAndNotifyAccessibility()
575 {
576     AccessibilityReplacedText replacedText(frame().selection().selection());
577     insertLineBreak();
578     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
579     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
580 }
581
582 void TypingCommand::insertParagraphSeparator()
583 {
584     if (!canAppendNewLineFeedToSelection(endingSelection()))
585         return;
586
587     if (!willAddTypingToOpenCommand(InsertParagraphSeparator, ParagraphGranularity))
588         return;
589
590     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditAction::TypingInsertParagraph));
591
592     Frame& frame = this->frame();
593     Ref<Frame> protector(frame);
594     typingAddedToOpenCommand(InsertParagraphSeparator);
595 }
596
597 void TypingCommand::insertParagraphSeparatorAndNotifyAccessibility()
598 {
599     AccessibilityReplacedText replacedText(frame().selection().selection());
600     insertParagraphSeparator();
601     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
602     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
603 }
604
605 void TypingCommand::insertParagraphSeparatorInQuotedContent()
606 {
607     if (!willAddTypingToOpenCommand(InsertParagraphSeparatorInQuotedContent, ParagraphGranularity))
608         return;
609
610     // If the selection starts inside a table, just insert the paragraph separator normally
611     // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
612     if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
613         insertParagraphSeparator();
614         return;
615     }
616         
617     applyCommandToComposite(BreakBlockquoteCommand::create(document()));
618
619     Frame& frame = this->frame();
620     Ref<Frame> protector(frame);
621     typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
622 }
623
624 void TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility()
625 {
626     AccessibilityReplacedText replacedText(frame().selection().selection());
627     insertParagraphSeparatorInQuotedContent();
628     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
629     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
630 }
631
632 bool TypingCommand::makeEditableRootEmpty()
633 {
634     Element* root = endingSelection().rootEditableElement();
635     if (!root || !root->firstChild())
636         return false;
637
638     if (root->firstChild() == root->lastChild() && root->firstElementChild() && root->firstElementChild()->hasTagName(brTag)) {
639         // If there is a single child and it could be a placeholder, leave it alone.
640         if (root->renderer() && root->renderer()->isRenderBlockFlow())
641             return false;
642     }
643
644     while (Node* child = root->firstChild())
645         removeNode(*child);
646
647     addBlockPlaceholderIfNeeded(root);
648     setEndingSelection(VisibleSelection(firstPositionInNode(root), DOWNSTREAM, endingSelection().isDirectional()));
649
650     return true;
651 }
652
653 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
654 {
655     Frame& frame = this->frame();
656     Ref<Frame> protector(frame);
657
658     frame.editor().updateMarkersForWordsAffectedByEditing(false);
659
660     VisibleSelection selectionToDelete;
661     VisibleSelection selectionAfterUndo;
662     bool expandForSpecialElements = !endingSelection().isCaret();
663
664     switch (endingSelection().selectionType()) {
665     case VisibleSelection::RangeSelection:
666         selectionToDelete = endingSelection();
667         selectionAfterUndo = selectionToDelete;
668         break;
669     case VisibleSelection::CaretSelection: {
670         // After breaking out of an empty mail blockquote, we still want continue with the deletion
671         // so actual content will get deleted, and not just the quote style.
672         if (breakOutOfEmptyMailBlockquotedParagraph())
673             typingAddedToOpenCommand(DeleteKey);
674
675         m_smartDelete = false;
676
677         FrameSelection selection;
678         selection.setSelection(endingSelection());
679         selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
680         if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
681             selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
682
683         const VisiblePosition& visibleStart = endingSelection().visibleStart();
684         const VisiblePosition& previousPosition = visibleStart.previous(CannotCrossEditingBoundary);
685         Node* enclosingTableCell = enclosingNodeOfType(visibleStart.deepEquivalent(), &isTableCell);
686         const Node* enclosingTableCellForPreviousPosition = enclosingNodeOfType(previousPosition.deepEquivalent(), &isTableCell);
687         if (previousPosition.isNull() || enclosingTableCell != enclosingTableCellForPreviousPosition) {
688             // When the caret is at the start of the editable area in an empty list item, break out of the list item.
689             if (auto deleteListSelection = shouldBreakOutOfEmptyListItem()) {
690                 if (willAddTypingToOpenCommand(DeleteKey, granularity, { }, Range::create(document(), deleteListSelection.value().start(), deleteListSelection.value().end()))) {
691                     breakOutOfEmptyListItem();
692                     typingAddedToOpenCommand(DeleteKey);
693                 }
694                 return;
695             }
696         }
697         if (previousPosition.isNull()) {
698             // When there are no visible positions in the editing root, delete its entire contents.
699             // FIXME: Dispatch a `beforeinput` event here and bail if preventDefault() was invoked.
700             if (visibleStart.next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
701                 typingAddedToOpenCommand(DeleteKey);
702                 return;
703             }
704         }
705
706         // If we have a caret selection at the beginning of a cell, we have nothing to do.
707         if (enclosingTableCell && visibleStart == firstPositionInNode(enclosingTableCell))
708             return;
709
710         // If the caret is at the start of a paragraph after a table, move content into the last table cell.
711         if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(CannotCrossEditingBoundary))) {
712             // Unless the caret is just before a table.  We don't want to move a table into the last table cell.
713             if (isLastPositionBeforeTable(visibleStart))
714                 return;
715             // Extend the selection backward into the last cell, then deletion will handle the move.
716             selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
717         // If the caret is just after a table, select the table and don't delete anything.
718         } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
719             setEndingSelection(VisibleSelection(positionBeforeNode(table), endingSelection().start(), DOWNSTREAM, endingSelection().isDirectional()));
720             typingAddedToOpenCommand(DeleteKey);
721             return;
722         }
723
724         selectionToDelete = selection.selection();
725
726         if (granularity == CharacterGranularity && selectionToDelete.end().containerNode() == selectionToDelete.start().containerNode()
727             && selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode() > 1) {
728             // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
729             selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
730         }
731
732         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
733             selectionAfterUndo = selectionToDelete;
734         else
735             // It's a little tricky to compute what the starting selection would have been in the original document.
736             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
737             // the current state of the document and we'll get the wrong result.
738             selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
739         break;
740     }
741     case VisibleSelection::NoSelection:
742         ASSERT_NOT_REACHED();
743         break;
744     }
745     
746     ASSERT(!selectionToDelete.isNone());
747     if (selectionToDelete.isNone()) {
748 #if PLATFORM(IOS_FAMILY)
749         // Workaround for this bug:
750         // <rdar://problem/4653755> UIKit text widgets should use WebKit editing API to manipulate text
751         setEndingSelection(frame.selection().selection());
752         closeTyping(&frame);
753 #endif
754         return;
755     }
756     
757     if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
758         return;
759     
760     if (!willAddTypingToOpenCommand(DeleteKey, granularity, { }, selectionToDelete.firstRange()))
761         return;
762
763     if (shouldAddToKillRing)
764         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::PrependText);
765
766     // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
767     postTextStateChangeNotificationForDeletion(selectionToDelete);
768
769     // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
770     // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
771     // more text than you insert.  In that case all of the text that was around originally should be selected.
772     if (m_openedByBackwardDelete)
773         setStartingSelection(selectionAfterUndo);
774     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete, /* mergeBlocksAfterDelete*/ true, /* replace*/ false, expandForSpecialElements, /*sanitizeMarkup*/ true);
775     setSmartDelete(false);
776     typingAddedToOpenCommand(DeleteKey);
777 }
778
779 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
780 {
781     Frame& frame = this->frame();
782     Ref<Frame> protector(frame);
783
784     frame.editor().updateMarkersForWordsAffectedByEditing(false);
785
786     VisibleSelection selectionToDelete;
787     VisibleSelection selectionAfterUndo;
788     bool expandForSpecialElements = !endingSelection().isCaret();
789
790     switch (endingSelection().selectionType()) {
791     case VisibleSelection::RangeSelection:
792         selectionToDelete = endingSelection();
793         selectionAfterUndo = selectionToDelete;
794         break;
795     case VisibleSelection::CaretSelection: {
796         m_smartDelete = false;
797
798         // Handle delete at beginning-of-block case.
799         // Do nothing in the case that the caret is at the start of a
800         // root editable element or at the start of a document.
801         FrameSelection selection;
802         selection.setSelection(endingSelection());
803         selection.modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
804         if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
805             selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
806
807         Position downstreamEnd = endingSelection().end().downstream();
808         VisiblePosition visibleEnd = endingSelection().visibleEnd();
809         Node* enclosingTableCell = enclosingNodeOfType(visibleEnd.deepEquivalent(), &isTableCell);
810         if (enclosingTableCell && visibleEnd == lastPositionInNode(enclosingTableCell))
811             return;
812         if (visibleEnd == endOfParagraph(visibleEnd))
813             downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
814         // When deleting tables: Select the table first, then perform the deletion
815         if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
816             && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(*downstreamEnd.containerNode())) {
817             setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
818             typingAddedToOpenCommand(ForwardDeleteKey);
819             return;
820         }
821
822         // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
823         if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
824             selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
825
826         selectionToDelete = selection.selection();
827         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
828             selectionAfterUndo = selectionToDelete;
829         else {
830             // It's a little tricky to compute what the starting selection would have been in the original document.
831             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
832             // the current state of the document and we'll get the wrong result.
833             Position extent = startingSelection().end();
834             if (extent.containerNode() != selectionToDelete.end().containerNode())
835                 extent = selectionToDelete.extent();
836             else {
837                 int extraCharacters;
838                 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
839                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
840                 else
841                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
842                 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
843             }
844             selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
845         }
846         break;
847     }
848     case VisibleSelection::NoSelection:
849         ASSERT_NOT_REACHED();
850         break;
851     }
852     
853     ASSERT(!selectionToDelete.isNone());
854     if (selectionToDelete.isNone()) {
855 #if PLATFORM(IOS_FAMILY)
856         // Workaround for this bug:
857         // <rdar://problem/4653755> UIKit text widgets should use WebKit editing API to manipulate text
858         setEndingSelection(frame.selection().selection());
859         closeTyping(&frame);
860 #endif
861         return;
862     }
863     
864     if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
865         return;
866
867     if (!willAddTypingToOpenCommand(ForwardDeleteKey, granularity, { }, selectionToDelete.firstRange()))
868         return;
869
870     // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
871     postTextStateChangeNotificationForDeletion(selectionToDelete);
872
873     if (shouldAddToKillRing)
874         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::AppendText);
875     // make undo select what was deleted
876     setStartingSelection(selectionAfterUndo);
877     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete, /* mergeBlocksAfterDelete*/ true, /* replace*/ false, expandForSpecialElements, /*sanitizeMarkup*/ true);
878     setSmartDelete(false);
879     typingAddedToOpenCommand(ForwardDeleteKey);
880 }
881
882 void TypingCommand::deleteSelection(bool smartDelete)
883 {
884     if (!willAddTypingToOpenCommand(DeleteSelection, CharacterGranularity))
885         return;
886
887     CompositeEditCommand::deleteSelection(smartDelete);
888     typingAddedToOpenCommand(DeleteSelection);
889 }
890
891 #if PLATFORM(IOS_FAMILY)
892 class FriendlyEditCommand : public EditCommand {
893 public:
894     void setEndingSelection(const VisibleSelection& selection)
895     {
896         EditCommand::setEndingSelection(selection);
897     }
898 };
899
900 void TypingCommand::setEndingSelectionOnLastInsertCommand(const VisibleSelection& selection)
901 {
902     if (!m_commands.isEmpty()) {
903         EditCommand* lastCommand = m_commands.last().get();
904         if (lastCommand->isInsertTextCommand())
905             static_cast<FriendlyEditCommand*>(lastCommand)->setEndingSelection(selection);
906     }
907 }
908 #endif
909
910 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
911 {
912     switch (commandType) {
913     case DeleteSelection:
914     case DeleteKey:
915     case ForwardDeleteKey:
916     case InsertParagraphSeparator:
917     case InsertLineBreak:
918         m_preservesTypingStyle = true;
919         return;
920     case InsertParagraphSeparatorInQuotedContent:
921     case InsertText:
922         m_preservesTypingStyle = false;
923         return;
924     }
925     ASSERT_NOT_REACHED();
926     m_preservesTypingStyle = false;
927 }
928
929 bool TypingCommand::isTypingCommand() const
930 {
931     return true;
932 }
933
934 } // namespace WebCore