Not possible to remove the 'li' element inside the table cell
[WebKit-https.git] / Source / WebCore / editing / TypingCommand.cpp
1 /*
2  * Copyright (C) 2005-2008, 2016 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 EditActionTypingInsertPendingComposition;
87         if (command == TypingCommand::DeleteSelection)
88             return EditActionTypingDeletePendingComposition;
89         ASSERT_NOT_REACHED();
90     }
91
92     if (compositionType == TypingCommand::TextCompositionFinal) {
93         if (command == TypingCommand::InsertText)
94             return EditActionTypingInsertFinalComposition;
95         if (command == TypingCommand::DeleteSelection)
96             return EditActionTypingDeleteFinalComposition;
97         ASSERT_NOT_REACHED();
98     }
99
100     switch (command) {
101     case TypingCommand::DeleteSelection:
102         return EditActionTypingDeleteSelection;
103     case TypingCommand::DeleteKey: {
104         if (granularity == WordGranularity)
105             return EditActionTypingDeleteWordBackward;
106         if (granularity == LineBoundary)
107             return EditActionTypingDeleteLineBackward;
108         return EditActionTypingDeleteBackward;
109     }
110     case TypingCommand::ForwardDeleteKey:
111         if (granularity == WordGranularity)
112             return EditActionTypingDeleteWordForward;
113         if (granularity == LineBoundary)
114             return EditActionTypingDeleteLineForward;
115         return EditActionTypingDeleteForward;
116     case TypingCommand::InsertText:
117         return isAutocompletion ? EditActionInsertReplacement : EditActionTypingInsertText;
118     case TypingCommand::InsertLineBreak:
119         return EditActionTypingInsertLineBreak;
120     case TypingCommand::InsertParagraphSeparator:
121     case TypingCommand::InsertParagraphSeparatorInQuotedContent:
122         return EditActionTypingInsertParagraph;
123     default:
124         return EditActionUnspecified;
125     }
126 }
127
128 static inline bool editActionIsDeleteByTyping(EditAction action)
129 {
130     switch (action) {
131     case EditActionTypingDeleteSelection:
132     case EditActionTypingDeleteBackward:
133     case EditActionTypingDeleteWordBackward:
134     case EditActionTypingDeleteLineBackward:
135     case EditActionTypingDeleteForward:
136     case EditActionTypingDeleteWordForward:
137     case EditActionTypingDeleteLineForward:
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     RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
269     applyTextInsertionCommand(frame.get(), *cmd, 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)
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 != EditActionTypingInsertPendingComposition && m_currentTypingEditAction != EditActionTypingDeletePendingComposition;
405 }
406
407 String TypingCommand::inputEventData() const
408 {
409     switch (m_currentTypingEditAction) {
410     case EditActionTypingInsertText:
411     case EditActionTypingInsertPendingComposition:
412     case EditActionTypingInsertFinalComposition:
413         return m_currentTextToInsert;
414     case EditActionInsertReplacement:
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 != EditActionInsertReplacement || 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)
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) 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 == 0xA0 || 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)
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     RefPtr<StaticRange> staticRange = StaticRange::createFromRange(*range);
503     return frame().editor().willApplyEditing(*this, { 1, staticRange });
504 }
505
506 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
507 {
508     Frame& frame = this->frame();
509
510     updatePreservesTypingStyle(commandTypeForAddedTyping);
511
512 #if PLATFORM(COCOA)
513     frame.editor().appliedEditing(*this);
514     // Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
515     if (!m_shouldPreventSpellChecking)
516         markMisspellingsAfterTyping(commandTypeForAddedTyping);
517 #else
518     // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
519     markMisspellingsAfterTyping(commandTypeForAddedTyping);
520     frame.editor().appliedEditing(*this);
521 #endif
522 }
523
524 void TypingCommand::insertText(const String &text, bool selectInsertedText)
525 {
526     // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
527     // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
528     // an existing selection; at the moment they can either put the caret after what's inserted or
529     // select what's inserted, but there's no way to "extend selection" to include both an old selection
530     // that ends just before where we want to insert text and the newly inserted text.
531     TypingCommandLineOperation operation(this, selectInsertedText, text);
532     forEachLineInString(text, operation);
533 }
534
535 void TypingCommand::insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText)
536 {
537     LOG(Editing, "TypingCommand %p insertTextAndNotifyAccessibility (text %s, selectInsertedText %d)", this, text.utf8().data(), selectInsertedText);
538
539     AccessibilityReplacedText replacedText(frame().selection().selection());
540     insertText(text, selectInsertedText);
541     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, text, frame().selection().selection());
542     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
543 }
544
545 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
546 {
547     if (!willAddTypingToOpenCommand(InsertText, CharacterGranularity, text))
548         return;
549
550     auto command = InsertTextCommand::create(document(), text, selectInsertedText,
551         m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces, EditActionTypingInsertText);
552
553     applyCommandToComposite(WTFMove(command), endingSelection());
554
555     typingAddedToOpenCommand(InsertText);
556 }
557
558 void TypingCommand::insertLineBreak()
559 {
560     if (!canAppendNewLineFeedToSelection(endingSelection()))
561         return;
562
563     if (!willAddTypingToOpenCommand(InsertLineBreak, LineGranularity))
564         return;
565
566     applyCommandToComposite(InsertLineBreakCommand::create(document()));
567     typingAddedToOpenCommand(InsertLineBreak);
568 }
569
570 void TypingCommand::insertLineBreakAndNotifyAccessibility()
571 {
572     AccessibilityReplacedText replacedText(frame().selection().selection());
573     insertLineBreak();
574     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
575     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
576 }
577
578 void TypingCommand::insertParagraphSeparator()
579 {
580     if (!canAppendNewLineFeedToSelection(endingSelection()))
581         return;
582
583     if (!willAddTypingToOpenCommand(InsertParagraphSeparator, ParagraphGranularity))
584         return;
585
586     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionTypingInsertParagraph));
587     typingAddedToOpenCommand(InsertParagraphSeparator);
588 }
589
590 void TypingCommand::insertParagraphSeparatorAndNotifyAccessibility()
591 {
592     AccessibilityReplacedText replacedText(frame().selection().selection());
593     insertParagraphSeparator();
594     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
595     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
596 }
597
598 void TypingCommand::insertParagraphSeparatorInQuotedContent()
599 {
600     if (!willAddTypingToOpenCommand(InsertParagraphSeparatorInQuotedContent, ParagraphGranularity))
601         return;
602
603     // If the selection starts inside a table, just insert the paragraph separator normally
604     // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
605     if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
606         insertParagraphSeparator();
607         return;
608     }
609         
610     applyCommandToComposite(BreakBlockquoteCommand::create(document()));
611     typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
612 }
613
614 void TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility()
615 {
616     AccessibilityReplacedText replacedText(frame().selection().selection());
617     insertParagraphSeparatorInQuotedContent();
618     replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
619     composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
620 }
621
622 bool TypingCommand::makeEditableRootEmpty()
623 {
624     Element* root = endingSelection().rootEditableElement();
625     if (!root || !root->firstChild())
626         return false;
627
628     if (root->firstChild() == root->lastChild() && root->firstElementChild() && root->firstElementChild()->hasTagName(brTag)) {
629         // If there is a single child and it could be a placeholder, leave it alone.
630         if (root->renderer() && root->renderer()->isRenderBlockFlow())
631             return false;
632     }
633
634     while (Node* child = root->firstChild())
635         removeNode(*child);
636
637     addBlockPlaceholderIfNeeded(root);
638     setEndingSelection(VisibleSelection(firstPositionInNode(root), DOWNSTREAM, endingSelection().isDirectional()));
639
640     return true;
641 }
642
643 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
644 {
645     Frame& frame = this->frame();
646     Ref<Frame> protector(frame);
647
648     frame.editor().updateMarkersForWordsAffectedByEditing(false);
649
650     VisibleSelection selectionToDelete;
651     VisibleSelection selectionAfterUndo;
652
653     switch (endingSelection().selectionType()) {
654     case VisibleSelection::RangeSelection:
655         selectionToDelete = endingSelection();
656         selectionAfterUndo = selectionToDelete;
657         break;
658     case VisibleSelection::CaretSelection: {
659         // After breaking out of an empty mail blockquote, we still want continue with the deletion
660         // so actual content will get deleted, and not just the quote style.
661         if (breakOutOfEmptyMailBlockquotedParagraph())
662             typingAddedToOpenCommand(DeleteKey);
663
664         m_smartDelete = false;
665
666         FrameSelection selection;
667         selection.setSelection(endingSelection());
668         selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
669         if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
670             selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
671
672         const VisiblePosition& visibleStart = endingSelection().visibleStart();
673         const VisiblePosition& previousPosition = visibleStart.previous(CannotCrossEditingBoundary);
674         Node* enclosingTableCell = enclosingNodeOfType(visibleStart.deepEquivalent(), &isTableCell);
675         const Node* enclosingTableCellForPreviousPosition = enclosingNodeOfType(previousPosition.deepEquivalent(), &isTableCell);
676         if (previousPosition.isNull() || enclosingTableCell != enclosingTableCellForPreviousPosition) {
677             // When the caret is at the start of the editable area in an empty list item, break out of the list item.
678             if (auto deleteListSelection = shouldBreakOutOfEmptyListItem()) {
679                 if (willAddTypingToOpenCommand(DeleteKey, granularity, { }, Range::create(document(), deleteListSelection.value().start(), deleteListSelection.value().end()))) {
680                     breakOutOfEmptyListItem();
681                     typingAddedToOpenCommand(DeleteKey);
682                 }
683                 return;
684             }
685         }
686         if (previousPosition.isNull()) {
687             // When there are no visible positions in the editing root, delete its entire contents.
688             // FIXME: Dispatch a `beforeinput` event here and bail if preventDefault() was invoked.
689             if (visibleStart.next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
690                 typingAddedToOpenCommand(DeleteKey);
691                 return;
692             }
693         }
694
695         // If we have a caret selection at the beginning of a cell, we have nothing to do.
696         if (enclosingTableCell && visibleStart == firstPositionInNode(enclosingTableCell))
697             return;
698
699         // If the caret is at the start of a paragraph after a table, move content into the last table cell.
700         if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(CannotCrossEditingBoundary))) {
701             // Unless the caret is just before a table.  We don't want to move a table into the last table cell.
702             if (isLastPositionBeforeTable(visibleStart))
703                 return;
704             // Extend the selection backward into the last cell, then deletion will handle the move.
705             selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
706         // If the caret is just after a table, select the table and don't delete anything.
707         } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
708             setEndingSelection(VisibleSelection(positionBeforeNode(table), endingSelection().start(), DOWNSTREAM, endingSelection().isDirectional()));
709             typingAddedToOpenCommand(DeleteKey);
710             return;
711         }
712
713         selectionToDelete = selection.selection();
714
715         if (granularity == CharacterGranularity && selectionToDelete.end().containerNode() == selectionToDelete.start().containerNode()
716             && selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode() > 1) {
717             // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
718             selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
719         }
720
721         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
722             selectionAfterUndo = selectionToDelete;
723         else
724             // It's a little tricky to compute what the starting selection would have been in the original document.
725             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
726             // the current state of the document and we'll get the wrong result.
727             selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
728         break;
729     }
730     case VisibleSelection::NoSelection:
731         ASSERT_NOT_REACHED();
732         break;
733     }
734     
735     ASSERT(!selectionToDelete.isNone());
736     if (selectionToDelete.isNone()) {
737 #if PLATFORM(IOS)
738         // Workaround for this bug:
739         // <rdar://problem/4653755> UIKit text widgets should use WebKit editing API to manipulate text
740         setEndingSelection(frame.selection().selection());
741         closeTyping(&frame);
742 #endif
743         return;
744     }
745     
746     if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
747         return;
748     
749     if (!willAddTypingToOpenCommand(DeleteKey, granularity, { }, selectionToDelete.firstRange()))
750         return;
751
752     if (shouldAddToKillRing)
753         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::PrependText);
754
755     // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
756     postTextStateChangeNotificationForDeletion(selectionToDelete);
757
758     // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
759     // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
760     // more text than you insert.  In that case all of the text that was around originally should be selected.
761     if (m_openedByBackwardDelete)
762         setStartingSelection(selectionAfterUndo);
763     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
764     setSmartDelete(false);
765     typingAddedToOpenCommand(DeleteKey);
766 }
767
768 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
769 {
770     Frame& frame = this->frame();
771     Ref<Frame> protector(frame);
772
773     frame.editor().updateMarkersForWordsAffectedByEditing(false);
774
775     VisibleSelection selectionToDelete;
776     VisibleSelection selectionAfterUndo;
777
778     switch (endingSelection().selectionType()) {
779     case VisibleSelection::RangeSelection:
780         selectionToDelete = endingSelection();
781         selectionAfterUndo = selectionToDelete;
782         break;
783     case VisibleSelection::CaretSelection: {
784         m_smartDelete = false;
785
786         // Handle delete at beginning-of-block case.
787         // Do nothing in the case that the caret is at the start of a
788         // root editable element or at the start of a document.
789         FrameSelection selection;
790         selection.setSelection(endingSelection());
791         selection.modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
792         if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
793             selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
794
795         Position downstreamEnd = endingSelection().end().downstream();
796         VisiblePosition visibleEnd = endingSelection().visibleEnd();
797         Node* enclosingTableCell = enclosingNodeOfType(visibleEnd.deepEquivalent(), &isTableCell);
798         if (enclosingTableCell && visibleEnd == lastPositionInNode(enclosingTableCell))
799             return;
800         if (visibleEnd == endOfParagraph(visibleEnd))
801             downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
802         // When deleting tables: Select the table first, then perform the deletion
803         if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
804             && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(*downstreamEnd.containerNode())) {
805             setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
806             typingAddedToOpenCommand(ForwardDeleteKey);
807             return;
808         }
809
810         // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
811         if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
812             selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
813
814         selectionToDelete = selection.selection();
815         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
816             selectionAfterUndo = selectionToDelete;
817         else {
818             // It's a little tricky to compute what the starting selection would have been in the original document.
819             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
820             // the current state of the document and we'll get the wrong result.
821             Position extent = startingSelection().end();
822             if (extent.containerNode() != selectionToDelete.end().containerNode())
823                 extent = selectionToDelete.extent();
824             else {
825                 int extraCharacters;
826                 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
827                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
828                 else
829                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
830                 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
831             }
832             selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
833         }
834         break;
835     }
836     case VisibleSelection::NoSelection:
837         ASSERT_NOT_REACHED();
838         break;
839     }
840     
841     ASSERT(!selectionToDelete.isNone());
842     if (selectionToDelete.isNone()) {
843 #if PLATFORM(IOS)
844         // Workaround for this bug:
845         // <rdar://problem/4653755> UIKit text widgets should use WebKit editing API to manipulate text
846         setEndingSelection(frame.selection().selection());
847         closeTyping(&frame);
848 #endif
849         return;
850     }
851     
852     if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
853         return;
854
855     if (!willAddTypingToOpenCommand(ForwardDeleteKey, granularity, { }, selectionToDelete.firstRange()))
856         return;
857
858     // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
859     postTextStateChangeNotificationForDeletion(selectionToDelete);
860
861     if (shouldAddToKillRing)
862         frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::AppendText);
863     // make undo select what was deleted
864     setStartingSelection(selectionAfterUndo);
865     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
866     setSmartDelete(false);
867     typingAddedToOpenCommand(ForwardDeleteKey);
868 }
869
870 void TypingCommand::deleteSelection(bool smartDelete)
871 {
872     if (!willAddTypingToOpenCommand(DeleteSelection, CharacterGranularity))
873         return;
874
875     CompositeEditCommand::deleteSelection(smartDelete);
876     typingAddedToOpenCommand(DeleteSelection);
877 }
878
879 #if PLATFORM(IOS)
880 class FriendlyEditCommand : public EditCommand {
881 public:
882     void setEndingSelection(const VisibleSelection& selection)
883     {
884         EditCommand::setEndingSelection(selection);
885     }
886 };
887
888 void TypingCommand::setEndingSelectionOnLastInsertCommand(const VisibleSelection& selection)
889 {
890     if (!m_commands.isEmpty()) {
891         EditCommand* lastCommand = m_commands.last().get();
892         if (lastCommand->isInsertTextCommand())
893             static_cast<FriendlyEditCommand*>(lastCommand)->setEndingSelection(selection);
894     }
895 }
896 #endif
897
898 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
899 {
900     switch (commandType) {
901     case DeleteSelection:
902     case DeleteKey:
903     case ForwardDeleteKey:
904     case InsertParagraphSeparator:
905     case InsertLineBreak:
906         m_preservesTypingStyle = true;
907         return;
908     case InsertParagraphSeparatorInQuotedContent:
909     case InsertText:
910         m_preservesTypingStyle = false;
911         return;
912     }
913     ASSERT_NOT_REACHED();
914     m_preservesTypingStyle = false;
915 }
916
917 bool TypingCommand::isTypingCommand() const
918 {
919     return true;
920 }
921
922 } // namespace WebCore