• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 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 COMPUTER, 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 COMPUTER, 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 "BeforeTextInsertedEvent.h"
30 #include "BreakBlockquoteCommand.h"
31 #include "DeleteSelectionCommand.h"
32 #include "Document.h"
33 #include "Editor.h"
34 #include "Element.h"
35 #include "Frame.h"
36 #include "InsertLineBreakCommand.h"
37 #include "InsertParagraphSeparatorCommand.h"
38 #include "InsertTextCommand.h"
39 #include "SelectionController.h"
40 #include "VisiblePosition.h"
41 #include "htmlediting.h"
42 #include "visible_units.h"
43 
44 namespace WebCore {
45 
TypingCommand(Document * document,ETypingCommand commandType,const String & textToInsert,bool selectInsertedText,TextGranularity granularity,bool killRing)46 TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity, bool killRing)
47     : CompositeEditCommand(document),
48       m_commandType(commandType),
49       m_textToInsert(textToInsert),
50       m_openForMoreTyping(true),
51       m_applyEditing(false),
52       m_selectInsertedText(selectInsertedText),
53       m_smartDelete(false),
54       m_granularity(granularity),
55       m_killRing(killRing),
56       m_openedByBackwardDelete(false)
57 {
58 }
59 
deleteSelection(Document * document,bool smartDelete)60 void TypingCommand::deleteSelection(Document* document, bool smartDelete)
61 {
62     ASSERT(document);
63 
64     Frame* frame = document->frame();
65     ASSERT(frame);
66 
67     if (!frame->selection()->isRange())
68         return;
69 
70     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
71     if (isOpenForMoreTypingCommand(lastEditCommand)) {
72         static_cast<TypingCommand*>(lastEditCommand)->deleteSelection(smartDelete);
73         return;
74     }
75 
76     RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", false);
77     typingCommand->setSmartDelete(smartDelete);
78     typingCommand->apply();
79 }
80 
deleteKeyPressed(Document * document,bool smartDelete,TextGranularity granularity,bool killRing)81 void TypingCommand::deleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity, bool killRing)
82 {
83     ASSERT(document);
84 
85     Frame *frame = document->frame();
86     ASSERT(frame);
87 
88     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
89     if (isOpenForMoreTypingCommand(lastEditCommand)) {
90         static_cast<TypingCommand*>(lastEditCommand)->deleteKeyPressed(granularity, killRing);
91         return;
92     }
93 
94     RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", false, granularity, killRing);
95     typingCommand->setSmartDelete(smartDelete);
96     typingCommand->apply();
97 }
98 
forwardDeleteKeyPressed(Document * document,bool smartDelete,TextGranularity granularity,bool killRing)99 void TypingCommand::forwardDeleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity, bool killRing)
100 {
101     // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
102     ASSERT(document);
103 
104     Frame *frame = document->frame();
105     ASSERT(frame);
106 
107     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
108     if (isOpenForMoreTypingCommand(lastEditCommand)) {
109         static_cast<TypingCommand*>(lastEditCommand)->forwardDeleteKeyPressed(granularity, killRing);
110         return;
111     }
112 
113     RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", false, granularity, killRing);
114     typingCommand->setSmartDelete(smartDelete);
115     typingCommand->apply();
116 }
117 
insertText(Document * document,const String & text,bool selectInsertedText,bool insertedTextIsComposition)118 void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, bool insertedTextIsComposition)
119 {
120     ASSERT(document);
121 
122     Frame* frame = document->frame();
123     ASSERT(frame);
124 
125     insertText(document, text, frame->selection()->selection(), selectInsertedText, insertedTextIsComposition);
126 }
127 
insertText(Document * document,const String & text,const Selection & selectionForInsertion,bool selectInsertedText,bool insertedTextIsComposition)128 void TypingCommand::insertText(Document* document, const String& text, const Selection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition)
129 {
130     ASSERT(document);
131 
132     RefPtr<Frame> frame = document->frame();
133     ASSERT(frame);
134 
135     Selection currentSelection = frame->selection()->selection();
136     bool changeSelection = currentSelection != selectionForInsertion;
137 
138     String newText = text;
139     Node* startNode = selectionForInsertion.start().node();
140 
141     if (startNode && startNode->rootEditableElement() && !insertedTextIsComposition) {
142         // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
143         ExceptionCode ec = 0;
144         RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
145         startNode->rootEditableElement()->dispatchEvent(evt, ec);
146         newText = evt->text();
147     }
148 
149     if (newText.isEmpty())
150         return;
151 
152     // Set the starting and ending selection appropriately if we are using a selection
153     // that is different from the current selection.  In the future, we should change EditCommand
154     // to deal with custom selections in a general way that can be used by all of the commands.
155     RefPtr<EditCommand> lastEditCommand = frame->editor()->lastEditCommand();
156     if (isOpenForMoreTypingCommand(lastEditCommand.get())) {
157         TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand.get());
158         if (changeSelection) {
159             lastTypingCommand->setStartingSelection(selectionForInsertion);
160             lastTypingCommand->setEndingSelection(selectionForInsertion);
161         }
162         lastTypingCommand->insertText(newText, selectInsertedText);
163         if (changeSelection) {
164             lastTypingCommand->setEndingSelection(currentSelection);
165             frame->selection()->setSelection(currentSelection);
166         }
167         return;
168     }
169 
170     RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText);
171     if (changeSelection)  {
172         cmd->setStartingSelection(selectionForInsertion);
173         cmd->setEndingSelection(selectionForInsertion);
174     }
175     applyCommand(cmd);
176     if (changeSelection) {
177         cmd->setEndingSelection(currentSelection);
178         frame->selection()->setSelection(currentSelection);
179     }
180 }
181 
insertLineBreak(Document * document)182 void TypingCommand::insertLineBreak(Document *document)
183 {
184     ASSERT(document);
185 
186     Frame *frame = document->frame();
187     ASSERT(frame);
188 
189     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
190     if (isOpenForMoreTypingCommand(lastEditCommand)) {
191         static_cast<TypingCommand*>(lastEditCommand)->insertLineBreak();
192         return;
193     }
194 
195     applyCommand(TypingCommand::create(document, InsertLineBreak));
196 }
197 
insertParagraphSeparatorInQuotedContent(Document * document)198 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document)
199 {
200     ASSERT(document);
201 
202     Frame *frame = document->frame();
203     ASSERT(frame);
204 
205     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
206     if (isOpenForMoreTypingCommand(lastEditCommand)) {
207         static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparatorInQuotedContent();
208         return;
209     }
210 
211     applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent));
212 }
213 
insertParagraphSeparator(Document * document)214 void TypingCommand::insertParagraphSeparator(Document *document)
215 {
216     ASSERT(document);
217 
218     Frame *frame = document->frame();
219     ASSERT(frame);
220 
221     EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
222     if (isOpenForMoreTypingCommand(lastEditCommand)) {
223         static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparator();
224         return;
225     }
226 
227     applyCommand(TypingCommand::create(document, InsertParagraphSeparator));
228 }
229 
isOpenForMoreTypingCommand(const EditCommand * cmd)230 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd)
231 {
232     return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping();
233 }
234 
closeTyping(EditCommand * cmd)235 void TypingCommand::closeTyping(EditCommand* cmd)
236 {
237     if (isOpenForMoreTypingCommand(cmd))
238         static_cast<TypingCommand*>(cmd)->closeTyping();
239 }
240 
doApply()241 void TypingCommand::doApply()
242 {
243     if (endingSelection().isNone())
244         return;
245 
246     if (m_commandType == DeleteKey)
247         if (m_commands.isEmpty())
248             m_openedByBackwardDelete = true;
249 
250     switch (m_commandType) {
251         case DeleteSelection:
252             deleteSelection(m_smartDelete);
253             return;
254         case DeleteKey:
255             deleteKeyPressed(m_granularity, m_killRing);
256             return;
257         case ForwardDeleteKey:
258             forwardDeleteKeyPressed(m_granularity, m_killRing);
259             return;
260         case InsertLineBreak:
261             insertLineBreak();
262             return;
263         case InsertParagraphSeparator:
264             insertParagraphSeparator();
265             return;
266         case InsertParagraphSeparatorInQuotedContent:
267             insertParagraphSeparatorInQuotedContent();
268             return;
269         case InsertText:
270             insertText(m_textToInsert, m_selectInsertedText);
271             return;
272     }
273 
274     ASSERT_NOT_REACHED();
275 }
276 
editingAction() const277 EditAction TypingCommand::editingAction() const
278 {
279     return EditActionTyping;
280 }
281 
markMisspellingsAfterTyping()282 void TypingCommand::markMisspellingsAfterTyping()
283 {
284     if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
285         return;
286     // Take a look at the selection that results after typing and determine whether we need to spellcheck.
287     // Since the word containing the current selection is never marked, this does a check to
288     // see if typing made a new word that is not in the current selection. Basically, you
289     // get this by being at the end of a word and typing a space.
290     VisiblePosition start(endingSelection().start(), endingSelection().affinity());
291     VisiblePosition previous = start.previous();
292     if (previous.isNotNull()) {
293         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
294         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
295         if (p1 != p2)
296             document()->frame()->editor()->markMisspellingsAfterTypingToPosition(p1);
297     }
298 }
299 
typingAddedToOpenCommand()300 void TypingCommand::typingAddedToOpenCommand()
301 {
302     markMisspellingsAfterTyping();
303     // Do not apply editing to the frame on the first time through.
304     // The frame will get told in the same way as all other commands.
305     // But since this command stays open and is used for additional typing,
306     // we need to tell the frame here as other commands are added.
307     if (m_applyEditing)
308         document()->frame()->editor()->appliedEditing(this);
309     m_applyEditing = true;
310 }
311 
insertText(const String & text,bool selectInsertedText)312 void TypingCommand::insertText(const String &text, bool selectInsertedText)
313 {
314     // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
315     // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
316     // an existing selection; at the moment they can either put the caret after what's inserted or
317     // select what's inserted, but there's no way to "extend selection" to include both an old selection
318     // that ends just before where we want to insert text and the newly inserted text.
319     int offset = 0;
320     int newline;
321     while ((newline = text.find('\n', offset)) != -1) {
322         if (newline != offset)
323             insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false);
324         insertParagraphSeparator();
325         offset = newline + 1;
326     }
327     if (offset == 0)
328         insertTextRunWithoutNewlines(text, selectInsertedText);
329     else {
330         int length = text.length();
331         if (length != offset) {
332             insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText);
333         }
334     }
335 }
336 
insertTextRunWithoutNewlines(const String & text,bool selectInsertedText)337 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
338 {
339     RefPtr<InsertTextCommand> command;
340     if (!document()->frame()->typingStyle() && !m_commands.isEmpty()) {
341         EditCommand* lastCommand = m_commands.last().get();
342         if (lastCommand->isInsertTextCommand())
343             command = static_cast<InsertTextCommand*>(lastCommand);
344     }
345     if (!command) {
346         command = InsertTextCommand::create(document());
347         applyCommandToComposite(command);
348     }
349     command->input(text, selectInsertedText);
350     typingAddedToOpenCommand();
351 }
352 
insertLineBreak()353 void TypingCommand::insertLineBreak()
354 {
355     applyCommandToComposite(InsertLineBreakCommand::create(document()));
356     typingAddedToOpenCommand();
357 }
358 
insertParagraphSeparator()359 void TypingCommand::insertParagraphSeparator()
360 {
361     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
362     typingAddedToOpenCommand();
363 }
364 
insertParagraphSeparatorInQuotedContent()365 void TypingCommand::insertParagraphSeparatorInQuotedContent()
366 {
367     applyCommandToComposite(BreakBlockquoteCommand::create(document()));
368     typingAddedToOpenCommand();
369 }
370 
deleteKeyPressed(TextGranularity granularity,bool killRing)371 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
372 {
373     Selection selectionToDelete;
374     Selection selectionAfterUndo;
375 
376     switch (endingSelection().state()) {
377         case Selection::RANGE:
378             selectionToDelete = endingSelection();
379             selectionAfterUndo = selectionToDelete;
380             break;
381         case Selection::CARET: {
382             if (breakOutOfEmptyMailBlockquotedParagraph()) {
383                 typingAddedToOpenCommand();
384                 return;
385             }
386 
387             m_smartDelete = false;
388 
389             SelectionController selection;
390             selection.setSelection(endingSelection());
391             selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity);
392             if (killRing && selection.isCaret() && granularity != CharacterGranularity)
393                 selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity);
394 
395             // When the caret is at the start of the editable area in an empty list item, break out of the list item.
396             if (endingSelection().visibleStart().previous(true).isNull()) {
397                 if (breakOutOfEmptyListItem()) {
398                     typingAddedToOpenCommand();
399                     return;
400                 }
401             }
402 
403             VisiblePosition visibleStart(endingSelection().visibleStart());
404             // If the caret is at the start of a paragraph after a table, move content into the last table cell.
405             if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) {
406                 // Unless the caret is just before a table.  We don't want to move a table into the last table cell.
407                 if (isLastPositionBeforeTable(visibleStart))
408                     return;
409                 // Extend the selection backward into the last cell, then deletion will handle the move.
410                 selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity);
411             // If the caret is just after a table, select the table and don't delete anything.
412             } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
413                 setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM));
414                 typingAddedToOpenCommand();
415                 return;
416             }
417 
418             selectionToDelete = selection.selection();
419 
420             if (granularity == CharacterGranularity && selectionToDelete.end().offset() - selectionToDelete.start().offset() > 1) {
421                 // When we delete a ligature consisting of multiple Unicode characters with a backspace key,
422                 // we should not delete the ligature but delete only its last characeter. To check we are deleting
423                 // a ligature, we retrieve the previous position of the caret and count the number of
424                 // characters to be deleted.
425                 // To prevent from calculating the previous position every time when pressing a backspace key,
426                 // we retrieve the previous position only when the given selection consists of two or more characters.
427                 if (selectionToDelete.end().offset() - selectionToDelete.end().previous(UsingComposedCharacters).offset() > 1)
428                     selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(NotUsingComposedCharacters));
429             }
430 
431             if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
432                 selectionAfterUndo = selectionToDelete;
433             else
434                 // It's a little tricky to compute what the starting selection would have been in the original document.
435                 // We can't let the Selection class's validation kick in or it'll adjust for us based on
436                 // the current state of the document and we'll get the wrong result.
437                 selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
438             break;
439         }
440         case Selection::NONE:
441             ASSERT_NOT_REACHED();
442             break;
443     }
444 
445     if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) {
446         if (killRing)
447             document()->frame()->editor()->addToKillRing(selectionToDelete.toRange().get(), false);
448         // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
449         // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
450         // more text than you insert.  In that case all of the text that was around originally should be selected.
451         if (m_openedByBackwardDelete)
452             setStartingSelection(selectionAfterUndo);
453         CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
454         setSmartDelete(false);
455         typingAddedToOpenCommand();
456     }
457 }
458 
forwardDeleteKeyPressed(TextGranularity granularity,bool killRing)459 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
460 {
461     Selection selectionToDelete;
462     Selection selectionAfterUndo;
463 
464     switch (endingSelection().state()) {
465         case Selection::RANGE:
466             selectionToDelete = endingSelection();
467             selectionAfterUndo = selectionToDelete;
468             break;
469         case Selection::CARET: {
470             m_smartDelete = false;
471 
472             // Handle delete at beginning-of-block case.
473             // Do nothing in the case that the caret is at the start of a
474             // root editable element or at the start of a document.
475             SelectionController selection;
476             selection.setSelection(endingSelection());
477             selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, granularity);
478             if (killRing && selection.isCaret() && granularity != CharacterGranularity)
479                 selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity);
480 
481             Position downstreamEnd = endingSelection().end().downstream();
482             VisiblePosition visibleEnd = endingSelection().visibleEnd();
483             if (visibleEnd == endOfParagraph(visibleEnd))
484                 downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream();
485             // When deleting tables: Select the table first, then perform the deletion
486             if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.offset() == 0) {
487                 setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM));
488                 typingAddedToOpenCommand();
489                 return;
490             }
491 
492             // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
493             if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
494                 selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity);
495 
496             selectionToDelete = selection.selection();
497             if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
498                 selectionAfterUndo = selectionToDelete;
499             else {
500                 // It's a little tricky to compute what the starting selection would have been in the original document.
501                 // We can't let the Selection class's validation kick in or it'll adjust for us based on
502                 // the current state of the document and we'll get the wrong result.
503                 Position extent = startingSelection().end();
504                 if (extent.node() != selectionToDelete.end().node())
505                     extent = selectionToDelete.extent();
506                 else {
507                     int extraCharacters;
508                     if (selectionToDelete.start().node() == selectionToDelete.end().node())
509                         extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset();
510                     else
511                         extraCharacters = selectionToDelete.end().offset();
512                     extent = Position(extent.node(), extent.offset() + extraCharacters);
513                 }
514                 selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
515             }
516             break;
517         }
518         case Selection::NONE:
519             ASSERT_NOT_REACHED();
520             break;
521     }
522 
523     if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) {
524         if (killRing)
525             document()->frame()->editor()->addToKillRing(selectionToDelete.toRange().get(), false);
526         // make undo select what was deleted
527         setStartingSelection(selectionAfterUndo);
528         CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
529         setSmartDelete(false);
530         typingAddedToOpenCommand();
531     }
532 }
533 
deleteSelection(bool smartDelete)534 void TypingCommand::deleteSelection(bool smartDelete)
535 {
536     CompositeEditCommand::deleteSelection(smartDelete);
537     typingAddedToOpenCommand();
538 }
539 
preservesTypingStyle() const540 bool TypingCommand::preservesTypingStyle() const
541 {
542     switch (m_commandType) {
543         case DeleteSelection:
544         case DeleteKey:
545         case ForwardDeleteKey:
546         case InsertParagraphSeparator:
547         case InsertLineBreak:
548             return true;
549         case InsertParagraphSeparatorInQuotedContent:
550         case InsertText:
551             return false;
552     }
553     ASSERT_NOT_REACHED();
554     return false;
555 }
556 
isTypingCommand() const557 bool TypingCommand::isTypingCommand() const
558 {
559     return true;
560 }
561 
562 } // namespace WebCore
563