summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing/TypingCommand.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/editing/TypingCommand.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/editing/TypingCommand.cpp')
-rw-r--r--Source/WebCore/editing/TypingCommand.cpp352
1 files changed, 292 insertions, 60 deletions
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index 1f8768787..b61304a7f 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2008, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,17 +26,25 @@
#include "config.h"
#include "TypingCommand.h"
+#include "AXObjectCache.h"
#include "BreakBlockquoteCommand.h"
+#include "DataTransfer.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
#include "Editor.h"
#include "Element.h"
#include "Frame.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "InsertLineBreakCommand.h"
#include "InsertParagraphSeparatorCommand.h"
#include "InsertTextCommand.h"
+#include "Logging.h"
+#include "MarkupAccumulator.h"
+#include "MathMLElement.h"
#include "RenderElement.h"
+#include "StaticRange.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
@@ -71,24 +79,89 @@ private:
const String& m_text;
};
+static inline EditAction editActionForTypingCommand(TypingCommand::ETypingCommand command, TextGranularity granularity, TypingCommand::TextCompositionType compositionType, bool isAutocompletion)
+{
+ if (compositionType == TypingCommand::TextCompositionPending) {
+ if (command == TypingCommand::InsertText)
+ return EditActionTypingInsertPendingComposition;
+ if (command == TypingCommand::DeleteSelection)
+ return EditActionTypingDeletePendingComposition;
+ ASSERT_NOT_REACHED();
+ }
+
+ if (compositionType == TypingCommand::TextCompositionFinal) {
+ if (command == TypingCommand::InsertText)
+ return EditActionTypingInsertFinalComposition;
+ if (command == TypingCommand::DeleteSelection)
+ return EditActionTypingDeleteFinalComposition;
+ ASSERT_NOT_REACHED();
+ }
+
+ switch (command) {
+ case TypingCommand::DeleteSelection:
+ return EditActionTypingDeleteSelection;
+ case TypingCommand::DeleteKey: {
+ if (granularity == WordGranularity)
+ return EditActionTypingDeleteWordBackward;
+ if (granularity == LineBoundary)
+ return EditActionTypingDeleteLineBackward;
+ return EditActionTypingDeleteBackward;
+ }
+ case TypingCommand::ForwardDeleteKey:
+ if (granularity == WordGranularity)
+ return EditActionTypingDeleteWordForward;
+ if (granularity == LineBoundary)
+ return EditActionTypingDeleteLineForward;
+ return EditActionTypingDeleteForward;
+ case TypingCommand::InsertText:
+ return isAutocompletion ? EditActionInsertReplacement : EditActionTypingInsertText;
+ case TypingCommand::InsertLineBreak:
+ return EditActionTypingInsertLineBreak;
+ case TypingCommand::InsertParagraphSeparator:
+ case TypingCommand::InsertParagraphSeparatorInQuotedContent:
+ return EditActionTypingInsertParagraph;
+ default:
+ return EditActionUnspecified;
+ }
+}
+
+static inline bool editActionIsDeleteByTyping(EditAction action)
+{
+ switch (action) {
+ case EditActionTypingDeleteSelection:
+ case EditActionTypingDeleteBackward:
+ case EditActionTypingDeleteWordBackward:
+ case EditActionTypingDeleteLineBackward:
+ case EditActionTypingDeleteForward:
+ case EditActionTypingDeleteWordForward:
+ case EditActionTypingDeleteLineForward:
+ return true;
+ default:
+ return false;
+ }
+}
+
TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
- : TextInsertionBaseCommand(document)
+ : TextInsertionBaseCommand(document, editActionForTypingCommand(commandType, granularity, compositionType, options & IsAutocompletion))
, m_commandType(commandType)
, m_textToInsert(textToInsert)
+ , m_currentTextToInsert(textToInsert)
, m_openForMoreTyping(true)
, m_selectInsertedText(options & SelectInsertedText)
, m_smartDelete(options & SmartDelete)
, m_granularity(granularity)
, m_compositionType(compositionType)
- , m_killRing(options & KillRing)
+ , m_shouldAddToKillRing(options & AddsToKillRing)
+ , m_isAutocompletion(options & IsAutocompletion)
, m_openedByBackwardDelete(false)
, m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
, m_shouldPreventSpellChecking(options & PreventSpellChecking)
{
+ m_currentTypingEditAction = editingAction();
updatePreservesTypingStyle(m_commandType);
}
-void TypingCommand::deleteSelection(Document& document, Options options)
+void TypingCommand::deleteSelection(Document& document, Options options, TextCompositionType compositionType)
{
Frame* frame = document.frame();
ASSERT(frame);
@@ -96,27 +169,31 @@ void TypingCommand::deleteSelection(Document& document, Options options)
if (!frame->selection().isRange())
return;
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(compositionType);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
lastTypingCommand->deleteSelection(options & SmartDelete);
return;
}
- TypingCommand::create(document, DeleteSelection, "", options)->apply();
+ TypingCommand::create(document, DeleteSelection, emptyString(), options, compositionType)->apply();
}
void TypingCommand::deleteKeyPressed(Document& document, Options options, TextGranularity granularity)
{
if (granularity == CharacterGranularity) {
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), document.frame());
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->deleteKeyPressed(granularity, options & KillRing);
+ lastTypingCommand->deleteKeyPressed(granularity, options & AddsToKillRing);
return;
}
}
- TypingCommand::create(document, DeleteKey, "", options, granularity)->apply();
+ TypingCommand::create(document, DeleteKey, emptyString(), options, granularity)->apply();
}
void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options, TextGranularity granularity)
@@ -124,15 +201,17 @@ void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options,
// FIXME: Forward delete in TextEdit appears to open and close a new typing command.
Frame* frame = document.frame();
if (granularity == CharacterGranularity) {
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->forwardDeleteKeyPressed(granularity, options & KillRing);
+ lastTypingCommand->forwardDeleteKeyPressed(granularity, options & AddsToKillRing);
return;
}
}
- TypingCommand::create(document, ForwardDeleteKey, "", options, granularity)->apply();
+ TypingCommand::create(document, ForwardDeleteKey, emptyString(), options, granularity)->apply();
}
void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
@@ -152,7 +231,7 @@ void TypingCommand::insertText(Document& document, const String& text, Options o
ASSERT(frame);
if (!text.isEmpty())
- frame->editor().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text.deprecatedCharacters()[0]));
+ frame->editor().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
insertText(document, text, frame->selection().selection(), options, composition);
}
@@ -163,45 +242,52 @@ void TypingCommand::insertText(Document& document, const String& text, const Vis
RefPtr<Frame> frame = document.frame();
ASSERT(frame);
+ LOG(Editing, "TypingCommand::insertText (text %s)", text.utf8().data());
+
VisibleSelection currentSelection = frame->selection().selection();
- String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionUpdate);
+ String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionPending);
// Set the starting and ending selection appropriately if we are using a selection
// that is different from the current selection. In the future, we should change EditCommand
// to deal with custom selections in a general way that can be used by all of the commands.
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame.get())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
if (lastTypingCommand->endingSelection() != selectionForInsertion) {
lastTypingCommand->setStartingSelection(selectionForInsertion);
lastTypingCommand->setEndingSelection(selectionForInsertion);
}
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
lastTypingCommand->setCompositionType(compositionType);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->insertText(newText, options & SelectInsertedText);
+ lastTypingCommand->insertTextAndNotifyAccessibility(newText, options & SelectInsertedText);
return;
}
RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
- applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
+ applyTextInsertionCommand(frame.get(), *cmd, selectionForInsertion, currentSelection);
}
void TypingCommand::insertLineBreak(Document& document, Options options)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
- lastTypingCommand->insertLineBreak();
+ lastTypingCommand->insertLineBreakAndNotifyAccessibility();
return;
}
- applyCommand(TypingCommand::create(document, InsertLineBreak, "", options));
+ applyCommand(TypingCommand::create(document, InsertLineBreak, emptyString(), options));
}
void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
- lastTypingCommand->insertParagraphSeparatorInQuotedContent();
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(false);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
+ lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
return;
}
@@ -210,47 +296,73 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
void TypingCommand::insertParagraphSeparator(Document& document, Options options)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
- lastTypingCommand->insertParagraphSeparator();
+ lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
return;
}
- applyCommand(TypingCommand::create(document, InsertParagraphSeparator, "", options));
+ applyCommand(TypingCommand::create(document, InsertParagraphSeparator, emptyString(), options));
}
-PassRefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame* frame)
+RefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame& frame)
{
- ASSERT(frame);
-
- RefPtr<CompositeEditCommand> lastEditCommand = frame->editor().lastEditCommand();
+ RefPtr<CompositeEditCommand> lastEditCommand = frame.editor().lastEditCommand();
if (!lastEditCommand || !lastEditCommand->isTypingCommand() || !static_cast<TypingCommand*>(lastEditCommand.get())->isOpenForMoreTyping())
- return 0;
+ return nullptr;
return static_cast<TypingCommand*>(lastEditCommand.get());
}
+bool TypingCommand::shouldDeferWillApplyCommandUntilAddingTypingCommand() const
+{
+ return !m_isHandlingInitialTypingCommand || editActionIsDeleteByTyping(editingAction());
+}
+
void TypingCommand::closeTyping(Frame* frame)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame))
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame))
lastTypingCommand->closeTyping();
}
#if PLATFORM(IOS)
void TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(Frame* frame, const VisibleSelection& newSelection)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
lastTypingCommand->setEndingSelection(newSelection);
lastTypingCommand->setEndingSelectionOnLastInsertCommand(newSelection);
}
}
#endif
+void TypingCommand::postTextStateChangeNotificationForDeletion(const VisibleSelection& selection)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ postTextStateChangeNotification(AXTextEditTypeDelete, AccessibilityObject::stringForVisiblePositionRange(selection), selection.start());
+ VisiblePositionIndexRange range;
+ range.startIndex.value = indexForVisiblePosition(selection.start(), range.startIndex.scope);
+ range.endIndex.value = indexForVisiblePosition(selection.end(), range.endIndex.scope);
+ composition()->setRangeDeletedByUnapply(range);
+}
+
+bool TypingCommand::willApplyCommand()
+{
+ if (shouldDeferWillApplyCommandUntilAddingTypingCommand()) {
+ // The TypingCommand will handle the willApplyCommand logic separately in TypingCommand::willAddTypingToOpenCommand.
+ return true;
+ }
+
+ return CompositeEditCommand::willApplyCommand();
+}
+
void TypingCommand::doApply()
{
- if (!endingSelection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned())
return;
-
+
if (m_commandType == DeleteKey)
if (m_commands.isEmpty())
m_openedByBackwardDelete = true;
@@ -260,44 +372,81 @@ void TypingCommand::doApply()
deleteSelection(m_smartDelete);
return;
case DeleteKey:
- deleteKeyPressed(m_granularity, m_killRing);
+ deleteKeyPressed(m_granularity, m_shouldAddToKillRing);
return;
case ForwardDeleteKey:
- forwardDeleteKeyPressed(m_granularity, m_killRing);
+ forwardDeleteKeyPressed(m_granularity, m_shouldAddToKillRing);
return;
case InsertLineBreak:
- insertLineBreak();
+ insertLineBreakAndNotifyAccessibility();
return;
case InsertParagraphSeparator:
- insertParagraphSeparator();
+ insertParagraphSeparatorAndNotifyAccessibility();
return;
case InsertParagraphSeparatorInQuotedContent:
- insertParagraphSeparatorInQuotedContent();
+ insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
return;
case InsertText:
- insertText(m_textToInsert, m_selectInsertedText);
+ insertTextAndNotifyAccessibility(m_textToInsert, m_selectInsertedText);
return;
}
ASSERT_NOT_REACHED();
}
-EditAction TypingCommand::editingAction() const
+String TypingCommand::inputEventTypeName() const
{
- return EditActionTyping;
+ return inputTypeNameForEditingAction(m_currentTypingEditAction);
+}
+
+bool TypingCommand::isBeforeInputEventCancelable() const
+{
+ return m_currentTypingEditAction != EditActionTypingInsertPendingComposition && m_currentTypingEditAction != EditActionTypingDeletePendingComposition;
+}
+
+String TypingCommand::inputEventData() const
+{
+ switch (m_currentTypingEditAction) {
+ case EditActionTypingInsertText:
+ case EditActionTypingInsertPendingComposition:
+ case EditActionTypingInsertFinalComposition:
+ return m_currentTextToInsert;
+ case EditActionInsertReplacement:
+ return isEditingTextAreaOrTextInput() ? m_currentTextToInsert : String();
+ default:
+ return CompositeEditCommand::inputEventData();
+ }
+}
+
+RefPtr<DataTransfer> TypingCommand::inputEventDataTransfer() const
+{
+ if (m_currentTypingEditAction != EditActionInsertReplacement || isEditingTextAreaOrTextInput())
+ return nullptr;
+
+ StringBuilder htmlText;
+ MarkupAccumulator::appendCharactersReplacingEntities(htmlText, m_currentTextToInsert, 0, m_currentTextToInsert.length(), EntityMaskInHTMLPCDATA);
+ return DataTransfer::createForInputEvent(m_currentTextToInsert, htmlText.toString());
+}
+
+void TypingCommand::didApplyCommand()
+{
+ // TypingCommands handle applied editing separately (see TypingCommand::typingAddedToOpenCommand).
+ m_isHandlingInitialTypingCommand = false;
}
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
Frame& frame = this->frame();
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
if (!frame.editor().isContinuousSpellCheckingEnabled()
&& !frame.editor().isAutomaticQuoteSubstitutionEnabled()
&& !frame.editor().isAutomaticLinkDetectionEnabled()
&& !frame.editor().isAutomaticDashSubstitutionEnabled()
&& !frame.editor().isAutomaticTextReplacementEnabled())
return;
+ if (frame.editor().isHandlingAcceptedCandidate())
+ return;
#else
if (!frame.editor().isContinuousSpellCheckingEnabled())
return;
@@ -339,13 +488,28 @@ void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
}
}
+bool TypingCommand::willAddTypingToOpenCommand(ETypingCommand commandType, TextGranularity granularity, const String& text, RefPtr<Range>&& range)
+{
+ m_currentTextToInsert = text;
+ m_currentTypingEditAction = editActionForTypingCommand(commandType, granularity, m_compositionType, m_isAutocompletion);
+
+ if (!shouldDeferWillApplyCommandUntilAddingTypingCommand())
+ return true;
+
+ if (!range || isEditingTextAreaOrTextInput())
+ return frame().editor().willApplyEditing(*this, CompositeEditCommand::targetRangesForBindings());
+
+ RefPtr<StaticRange> staticRange = StaticRange::createFromRange(*range);
+ return frame().editor().willApplyEditing(*this, { 1, staticRange });
+}
+
void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
{
Frame& frame = this->frame();
updatePreservesTypingStyle(commandTypeForAddedTyping);
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
frame.editor().appliedEditing(this);
// Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
if (!m_shouldPreventSpellChecking)
@@ -368,10 +532,23 @@ void TypingCommand::insertText(const String &text, bool selectInsertedText)
forEachLineInString(text, operation);
}
+void TypingCommand::insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText)
+{
+ LOG(Editing, "TypingCommand %p insertTextAndNotifyAccessibility (text %s, selectInsertedText %d)", this, text.utf8().data(), selectInsertedText);
+
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertText(text, selectInsertedText);
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, text, frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
{
+ if (!willAddTypingToOpenCommand(InsertText, CharacterGranularity, text))
+ return;
+
RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
- m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
+ m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces, EditActionTypingInsertText);
applyCommandToComposite(command, endingSelection());
@@ -383,21 +560,46 @@ void TypingCommand::insertLineBreak()
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
+ if (!willAddTypingToOpenCommand(InsertLineBreak, LineGranularity))
+ return;
+
applyCommandToComposite(InsertLineBreakCommand::create(document()));
typingAddedToOpenCommand(InsertLineBreak);
}
+void TypingCommand::insertLineBreakAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertLineBreak();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertParagraphSeparator()
{
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+ if (!willAddTypingToOpenCommand(InsertParagraphSeparator, ParagraphGranularity))
+ return;
+
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionTypingInsertParagraph));
typingAddedToOpenCommand(InsertParagraphSeparator);
}
+void TypingCommand::insertParagraphSeparatorAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertParagraphSeparator();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertParagraphSeparatorInQuotedContent()
{
+ if (!willAddTypingToOpenCommand(InsertParagraphSeparatorInQuotedContent, ParagraphGranularity))
+ return;
+
// If the selection starts inside a table, just insert the paragraph separator normally
// Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
@@ -409,6 +611,14 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent()
typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
}
+void TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertParagraphSeparatorInQuotedContent();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
bool TypingCommand::makeEditableRootEmpty()
{
Element* root = endingSelection().rootEditableElement();
@@ -430,9 +640,10 @@ bool TypingCommand::makeEditableRootEmpty()
return true;
}
-void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
+void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
{
Frame& frame = this->frame();
+ Ref<Frame> protector(frame);
frame.editor().updateMarkersForWordsAffectedByEditing(false);
@@ -455,16 +666,20 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
FrameSelection selection;
selection.setSelection(endingSelection());
selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
- if (killRing && selection.isCaret() && granularity != CharacterGranularity)
+ if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
if (endingSelection().visibleStart().previous(CannotCrossEditingBoundary).isNull()) {
// When the caret is at the start of the editable area in an empty list item, break out of the list item.
- if (breakOutOfEmptyListItem()) {
- typingAddedToOpenCommand(DeleteKey);
+ if (auto deleteListSelection = shouldBreakOutOfEmptyListItem()) {
+ if (willAddTypingToOpenCommand(DeleteKey, granularity, { }, Range::create(document(), deleteListSelection.value().start(), deleteListSelection.value().end()))) {
+ breakOutOfEmptyListItem();
+ typingAddedToOpenCommand(DeleteKey);
+ }
return;
}
// When there are no visible positions in the editing root, delete its entire contents.
+ // FIXME: Dispatch a `beforeinput` event here and bail if preventDefault() was invoked.
if (endingSelection().visibleStart().next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
typingAddedToOpenCommand(DeleteKey);
return;
@@ -527,8 +742,15 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
return;
- if (killRing)
- frame.editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+ if (!willAddTypingToOpenCommand(DeleteKey, granularity, { }, selectionToDelete.firstRange()))
+ return;
+
+ if (shouldAddToKillRing)
+ frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::PrependText);
+
+ // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+ postTextStateChangeNotificationForDeletion(selectionToDelete);
+
// Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
// FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
// more text than you insert. In that case all of the text that was around originally should be selected.
@@ -539,9 +761,10 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
typingAddedToOpenCommand(DeleteKey);
}
-void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
+void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
{
Frame& frame = this->frame();
+ Ref<Frame> protector(frame);
frame.editor().updateMarkersForWordsAffectedByEditing(false);
@@ -562,7 +785,7 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
FrameSelection selection;
selection.setSelection(endingSelection());
selection.modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
- if (killRing && selection.isCaret() && granularity != CharacterGranularity)
+ if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
Position downstreamEnd = endingSelection().end().downstream();
@@ -574,7 +797,7 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
// When deleting tables: Select the table first, then perform the deletion
if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
- && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
+ && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(*downstreamEnd.containerNode())) {
setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
typingAddedToOpenCommand(ForwardDeleteKey);
return;
@@ -624,9 +847,15 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
return;
-
- if (killRing)
- frame.editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+
+ if (!willAddTypingToOpenCommand(ForwardDeleteKey, granularity, { }, selectionToDelete.firstRange()))
+ return;
+
+ // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+ postTextStateChangeNotificationForDeletion(selectionToDelete);
+
+ if (shouldAddToKillRing)
+ frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::AppendText);
// make undo select what was deleted
setStartingSelection(selectionAfterUndo);
CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
@@ -636,6 +865,9 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
void TypingCommand::deleteSelection(bool smartDelete)
{
+ if (!willAddTypingToOpenCommand(DeleteSelection, CharacterGranularity))
+ return;
+
CompositeEditCommand::deleteSelection(smartDelete);
typingAddedToOpenCommand(DeleteSelection);
}