DeleteTextTransaction.cpp (9392B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "DeleteTextTransaction.h" 7 8 #include "EditorBase.h" 9 #include "EditorDOMPoint.h" 10 #include "HTMLEditUtils.h" 11 #include "SelectionState.h" 12 13 #include "mozilla/Assertions.h" 14 #include "mozilla/TextEditor.h" 15 #include "mozilla/dom/Selection.h" 16 17 #include "nsDebug.h" 18 #include "nsError.h" 19 #include "nsISupportsImpl.h" 20 #include "nsAString.h" 21 22 namespace mozilla { 23 24 using namespace dom; 25 26 // static 27 already_AddRefed<DeleteTextTransaction> DeleteTextTransaction::MaybeCreate( 28 EditorBase& aEditorBase, Text& aTextNode, uint32_t aOffset, 29 uint32_t aLengthToDelete) { 30 RefPtr<DeleteTextTransaction> transaction = 31 aEditorBase.IsTextEditor() 32 ? new DeleteTextTransaction(aEditorBase, aTextNode, aOffset, 33 aLengthToDelete) 34 : new DeleteTextFromTextNodeTransaction(aEditorBase, aTextNode, 35 aOffset, aLengthToDelete); 36 return transaction.forget(); 37 } 38 39 // static 40 already_AddRefed<DeleteTextTransaction> 41 DeleteTextTransaction::MaybeCreateForPreviousCharacter(EditorBase& aEditorBase, 42 Text& aTextNode, 43 uint32_t aOffset) { 44 if (NS_WARN_IF(!aOffset)) { 45 return nullptr; 46 } 47 48 nsAutoString data; 49 aTextNode.GetData(data); 50 if (NS_WARN_IF(data.IsEmpty())) { 51 return nullptr; 52 } 53 54 uint32_t length = 1; 55 uint32_t offset = aOffset - 1; 56 if (offset && NS_IS_SURROGATE_PAIR(data[offset - 1], data[offset])) { 57 ++length; 58 --offset; 59 } 60 return DeleteTextTransaction::MaybeCreate(aEditorBase, aTextNode, offset, 61 length); 62 } 63 64 // static 65 already_AddRefed<DeleteTextTransaction> 66 DeleteTextTransaction::MaybeCreateForNextCharacter(EditorBase& aEditorBase, 67 Text& aTextNode, 68 uint32_t aOffset) { 69 nsAutoString data; 70 aTextNode.GetData(data); 71 if (NS_WARN_IF(aOffset >= data.Length()) || NS_WARN_IF(data.IsEmpty())) { 72 return nullptr; 73 } 74 75 uint32_t length = 1; 76 if (aOffset + 1 < data.Length() && 77 NS_IS_SURROGATE_PAIR(data[aOffset], data[aOffset + 1])) { 78 ++length; 79 } 80 return DeleteTextTransaction::MaybeCreate(aEditorBase, aTextNode, aOffset, 81 length); 82 } 83 84 DeleteTextTransaction::DeleteTextTransaction(EditorBase& aEditorBase, 85 Text& aTextNode, uint32_t aOffset, 86 uint32_t aLengthToDelete) 87 : DeleteContentTransactionBase(aEditorBase), 88 mOffset(aOffset), 89 mLengthToDelete(aLengthToDelete) { 90 MOZ_ASSERT(aTextNode.TextDataLength() >= aOffset + aLengthToDelete); 91 } 92 93 std::ostream& operator<<(std::ostream& aStream, 94 const DeleteTextTransaction& aTransaction) { 95 const auto* transactionForHTMLEditor = 96 aTransaction.GetAsDeleteTextFromTextNodeTransaction(); 97 if (transactionForHTMLEditor) { 98 return aStream << *transactionForHTMLEditor; 99 } 100 aStream << "{ mOffset=" << aTransaction.mOffset 101 << ", mLengthToDelete=" << aTransaction.mLengthToDelete 102 << ", mDeletedText=\"" 103 << NS_ConvertUTF16toUTF8(aTransaction.mDeletedText).get() << "\"" 104 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 105 return aStream; 106 } 107 108 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction, 109 DeleteContentTransactionBase) 110 111 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction) 112 NS_INTERFACE_MAP_END_INHERITING(DeleteContentTransactionBase) 113 114 Text* DeleteTextTransaction::GetTextNode() const { 115 if (MOZ_UNLIKELY(!mEditorBase)) { 116 return nullptr; 117 } 118 if (TextEditor* const textEditor = mEditorBase->GetAsTextEditor()) { 119 return textEditor->GetTextNode(); 120 } 121 MOZ_ASSERT(GetAsDeleteTextFromTextNodeTransaction()); 122 return GetAsDeleteTextFromTextNodeTransaction()->mTextNode; 123 } 124 125 NS_IMETHODIMP DeleteTextTransaction::DoTransaction() { 126 MOZ_LOG(GetLogModule(), LogLevel::Info, 127 ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__, 128 ToString(*this).c_str())); 129 130 if (NS_WARN_IF(!mEditorBase)) { 131 return NS_ERROR_NOT_AVAILABLE; 132 } 133 const RefPtr<Text> textNode = GetTextNode(); 134 if (NS_WARN_IF(!textNode) || 135 (mEditorBase->IsHTMLEditor() && 136 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 137 return NS_ERROR_NOT_AVAILABLE; 138 } 139 140 // Get the text that we're about to delete 141 IgnoredErrorResult error; 142 textNode->SubstringData(mOffset, mLengthToDelete, mDeletedText, error); 143 if (MOZ_UNLIKELY(error.Failed())) { 144 NS_WARNING("Text::SubstringData() failed"); 145 return error.StealNSResult(); 146 } 147 148 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 149 editorBase->DoDeleteText(*textNode, mOffset, mLengthToDelete, error); 150 if (MOZ_UNLIKELY(error.Failed())) { 151 NS_WARNING("EditorBase::DoDeleteText() failed"); 152 return error.StealNSResult(); 153 } 154 155 editorBase->RangeUpdaterRef().SelAdjDeleteText(*textNode, mOffset, 156 mLengthToDelete); 157 return NS_OK; 158 } 159 160 EditorDOMPoint DeleteTextTransaction::SuggestPointToPutCaret() const { 161 if (NS_WARN_IF(!mEditorBase)) { 162 return EditorDOMPoint(); 163 } 164 Text* const textNode = GetTextNode(); 165 if (NS_WARN_IF(!textNode) || 166 (mEditorBase->IsHTMLEditor() && 167 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 168 return EditorDOMPoint(); 169 } 170 if (NS_WARN_IF(textNode->TextDataLength() < mOffset)) { 171 return EditorDOMPoint(); 172 } 173 EditorDOMPoint candidatePoint(textNode, mOffset); 174 if (!candidatePoint.IsInNativeAnonymousSubtreeInTextControl() && 175 !HTMLEditUtils::IsSimplyEditableNode(*textNode)) { 176 return EditorDOMPoint(); 177 } 178 return candidatePoint; 179 } 180 181 // XXX: We may want to store the selection state and restore it properly. Was 182 // it an insertion point or an extended selection? 183 NS_IMETHODIMP DeleteTextTransaction::UndoTransaction() { 184 MOZ_LOG(GetLogModule(), LogLevel::Info, 185 ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__, 186 ToString(*this).c_str())); 187 188 if (NS_WARN_IF(!mEditorBase)) { 189 return NS_ERROR_NOT_AVAILABLE; 190 } 191 const RefPtr<Text> textNode = GetTextNode(); 192 if (NS_WARN_IF(!textNode) || 193 (mEditorBase->IsHTMLEditor() && 194 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 195 return NS_ERROR_NOT_AVAILABLE; 196 } 197 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 198 IgnoredErrorResult error; 199 editorBase->DoInsertText(*textNode, mOffset, mDeletedText, error); 200 NS_WARNING_ASSERTION(!error.Failed(), "EditorBase::DoInsertText() failed"); 201 return error.StealNSResult(); 202 } 203 204 NS_IMETHODIMP DeleteTextTransaction::RedoTransaction() { 205 MOZ_LOG(GetLogModule(), LogLevel::Info, 206 ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__, 207 !mEditorBase || mEditorBase->IsTextEditor() 208 ? ToString(*this).c_str() 209 : ToString(*GetAsDeleteTextFromTextNodeTransaction()).c_str())); 210 nsresult rv = DoTransaction(); 211 if (NS_FAILED(rv)) { 212 NS_WARNING("DeleteTextTransaction::DoTransaction() failed"); 213 return rv; 214 } 215 if (!mEditorBase || !mEditorBase->AllowsTransactionsToChangeSelection()) { 216 return NS_OK; 217 } 218 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 219 rv = editorBase->CollapseSelectionTo(SuggestPointToPutCaret()); 220 if (NS_FAILED(rv)) { 221 NS_WARNING("EditorBase::CollapseSelectionTo() failed"); 222 return rv; 223 } 224 return NS_OK; 225 } 226 227 /****************************************************************************** 228 * mozilla::DeleteTextFromTextNodeTransaction 229 ******************************************************************************/ 230 231 DeleteTextFromTextNodeTransaction::DeleteTextFromTextNodeTransaction( 232 EditorBase& aEditorBase, Text& aTextNode, uint32_t aOffset, 233 uint32_t aLengthToDelete) 234 : DeleteTextTransaction(aEditorBase, aTextNode, aOffset, aLengthToDelete), 235 mTextNode(&aTextNode) { 236 MOZ_ASSERT(aEditorBase.IsHTMLEditor()); 237 MOZ_ASSERT(mTextNode->TextDataLength() >= aOffset + aLengthToDelete); 238 } 239 240 std::ostream& operator<<( 241 std::ostream& aStream, 242 const DeleteTextFromTextNodeTransaction& aTransaction) { 243 aStream << "{ mTextNode=" << aTransaction.mTextNode.get(); 244 if (aTransaction.mTextNode) { 245 aStream << " (" << *aTransaction.mTextNode << ")"; 246 } 247 aStream << ", mOffset=" << aTransaction.mOffset 248 << ", mLengthToDelete=" << aTransaction.mLengthToDelete 249 << ", mDeletedText=\"" 250 << NS_ConvertUTF16toUTF8(aTransaction.mDeletedText).get() << "\"" 251 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 252 return aStream; 253 } 254 255 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextFromTextNodeTransaction, 256 DeleteTextTransaction, mTextNode) 257 258 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextFromTextNodeTransaction) 259 NS_INTERFACE_MAP_END_INHERITING(DeleteTextTransaction) 260 261 } // namespace mozilla