ReplaceTextTransaction.cpp (9841B)
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 "ReplaceTextTransaction.h" 7 8 #include "HTMLEditUtils.h" 9 10 #include "mozilla/Logging.h" 11 #include "mozilla/OwningNonNull.h" 12 #include "mozilla/TextEditor.h" 13 #include "mozilla/ToString.h" 14 15 namespace mozilla { 16 17 using namespace dom; 18 19 // static 20 already_AddRefed<ReplaceTextTransaction> ReplaceTextTransaction::Create( 21 EditorBase& aEditorBase, const nsAString& aStringToInsert, 22 dom::Text& aTextNode, uint32_t aStartOffset, uint32_t aLength) { 23 MOZ_ASSERT(aLength > 0, "Use InsertTextTransaction instead"); 24 MOZ_ASSERT(!aStringToInsert.IsEmpty(), "Use DeleteTextTransaction instead"); 25 MOZ_ASSERT(aTextNode.Length() >= aStartOffset); 26 MOZ_ASSERT(aTextNode.Length() >= aStartOffset + aLength); 27 28 RefPtr<ReplaceTextTransaction> transaction = 29 aEditorBase.IsTextEditor() 30 ? new ReplaceTextTransaction(aEditorBase, aStringToInsert, aTextNode, 31 aStartOffset, aLength) 32 : new ReplaceTextInTextNodeTransaction( 33 aEditorBase, aStringToInsert, aTextNode, aStartOffset, aLength); 34 return transaction.forget(); 35 } 36 37 std::ostream& operator<<(std::ostream& aStream, 38 const ReplaceTextTransaction& aTransaction) { 39 const auto* transactionForHTMLEditor = 40 aTransaction.GetAsReplaceTextInTextNodeTransaction(); 41 if (transactionForHTMLEditor) { 42 return aStream << *transactionForHTMLEditor; 43 } 44 aStream << "{ mStringToInsert=\"" 45 << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\"" 46 << ", mStringToBeReplaced=\"" 47 << NS_ConvertUTF16toUTF8(aTransaction.mStringToBeReplaced).get() 48 << "\", mOffset=" << aTransaction.mOffset 49 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 50 return aStream; 51 } 52 53 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReplaceTextTransaction, EditTransactionBase, 54 mEditorBase) 55 56 NS_IMPL_ADDREF_INHERITED(ReplaceTextTransaction, EditTransactionBase) 57 NS_IMPL_RELEASE_INHERITED(ReplaceTextTransaction, EditTransactionBase) 58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReplaceTextTransaction) 59 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) 60 61 Text* ReplaceTextTransaction::GetTextNode() const { 62 if (MOZ_UNLIKELY(!mEditorBase)) { 63 return nullptr; 64 } 65 if (TextEditor* const textEditor = mEditorBase->GetAsTextEditor()) { 66 return textEditor->GetTextNode(); 67 } 68 MOZ_ASSERT(GetAsReplaceTextInTextNodeTransaction()); 69 return GetAsReplaceTextInTextNodeTransaction()->mTextNode; 70 } 71 72 NS_IMETHODIMP ReplaceTextTransaction::DoTransaction() { 73 MOZ_LOG(GetLogModule(), LogLevel::Info, 74 ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__, 75 ToString(*this).c_str())); 76 77 if (NS_WARN_IF(!mEditorBase)) { 78 return NS_ERROR_NOT_AVAILABLE; 79 } 80 const RefPtr<Text> textNode = GetTextNode(); 81 if (NS_WARN_IF(!textNode) || 82 (mEditorBase->IsHTMLEditor() && 83 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 84 return NS_ERROR_NOT_AVAILABLE; 85 } 86 87 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 88 89 IgnoredErrorResult error; 90 editorBase->DoReplaceText(*textNode, mOffset, mStringToBeReplaced.Length(), 91 mStringToInsert, error); 92 if (MOZ_UNLIKELY(error.Failed())) { 93 NS_WARNING("EditorBase::DoReplaceText() failed"); 94 return error.StealNSResult(); 95 } 96 // XXX What should we do if mutation event listener changed the node? 97 editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset, 98 mStringToBeReplaced.Length(), 99 mStringToInsert.Length()); 100 return NS_OK; 101 } 102 103 NS_IMETHODIMP ReplaceTextTransaction::UndoTransaction() { 104 MOZ_LOG(GetLogModule(), LogLevel::Info, 105 ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__, 106 ToString(*this).c_str())); 107 108 if (NS_WARN_IF(!mEditorBase)) { 109 return NS_ERROR_NOT_AVAILABLE; 110 } 111 const RefPtr<Text> textNode = GetTextNode(); 112 if (NS_WARN_IF(!textNode) || 113 (mEditorBase->IsHTMLEditor() && 114 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 115 return NS_ERROR_NOT_AVAILABLE; 116 } 117 118 IgnoredErrorResult error; 119 nsAutoString insertedString; 120 textNode->SubstringData(mOffset, mStringToInsert.Length(), insertedString, 121 error); 122 if (MOZ_UNLIKELY(error.Failed())) { 123 NS_WARNING("CharacterData::SubstringData() failed"); 124 return error.StealNSResult(); 125 } 126 if (MOZ_UNLIKELY(insertedString != mStringToInsert)) { 127 NS_WARNING( 128 "ReplaceTextTransaction::UndoTransaction() did nothing due to " 129 "unexpected text"); 130 return NS_OK; 131 } 132 133 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 134 135 editorBase->DoReplaceText(*textNode, mOffset, mStringToInsert.Length(), 136 mStringToBeReplaced, error); 137 if (MOZ_UNLIKELY(error.Failed())) { 138 NS_WARNING("EditorBase::DoReplaceText() failed"); 139 return error.StealNSResult(); 140 } 141 // XXX What should we do if mutation event listener changed the node? 142 editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset, 143 mStringToInsert.Length(), 144 mStringToBeReplaced.Length()); 145 146 if (!editorBase->AllowsTransactionsToChangeSelection()) { 147 return NS_OK; 148 } 149 150 // XXX Should we stop setting selection when mutation event listener 151 // modifies the text node? 152 editorBase->CollapseSelectionTo( 153 EditorRawDOMPoint(textNode, mOffset + mStringToBeReplaced.Length()), 154 error); 155 if (MOZ_UNLIKELY(error.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) { 156 NS_WARNING( 157 "EditorBase::CollapseSelectionTo() caused destroying the editor"); 158 return NS_ERROR_EDITOR_DESTROYED; 159 } 160 NS_ASSERTION(!error.Failed(), 161 "EditorBase::CollapseSelectionTo() failed, but ignored"); 162 return NS_OK; 163 } 164 165 NS_IMETHODIMP ReplaceTextTransaction::RedoTransaction() { 166 MOZ_LOG(GetLogModule(), LogLevel::Info, 167 ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__, 168 ToString(*this).c_str())); 169 170 if (NS_WARN_IF(!mEditorBase)) { 171 return NS_ERROR_NOT_AVAILABLE; 172 } 173 const RefPtr<Text> textNode = GetTextNode(); 174 if (NS_WARN_IF(!textNode) || 175 (mEditorBase->IsHTMLEditor() && 176 NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) { 177 return NS_ERROR_NOT_AVAILABLE; 178 } 179 180 IgnoredErrorResult error; 181 nsAutoString undoneString; 182 textNode->SubstringData(mOffset, mStringToBeReplaced.Length(), undoneString, 183 error); 184 if (MOZ_UNLIKELY(error.Failed())) { 185 NS_WARNING("CharacterData::SubstringData() failed"); 186 return error.StealNSResult(); 187 } 188 if (MOZ_UNLIKELY(undoneString != mStringToBeReplaced)) { 189 NS_WARNING( 190 "ReplaceTextTransaction::RedoTransaction() did nothing due to " 191 "unexpected text"); 192 return NS_OK; 193 } 194 195 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 196 editorBase->DoReplaceText(*textNode, mOffset, mStringToBeReplaced.Length(), 197 mStringToInsert, error); 198 if (MOZ_UNLIKELY(error.Failed())) { 199 NS_WARNING("EditorBase::DoReplaceText() failed"); 200 return error.StealNSResult(); 201 } 202 // XXX What should we do if mutation event listener changed the node? 203 editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset, 204 mStringToBeReplaced.Length(), 205 mStringToInsert.Length()); 206 207 if (!editorBase->AllowsTransactionsToChangeSelection()) { 208 return NS_OK; 209 } 210 211 // XXX Should we stop setting selection when mutation event listener 212 // modifies the text node? 213 editorBase->CollapseSelectionTo(SuggestPointToPutCaret<EditorRawDOMPoint>(), 214 error); 215 if (MOZ_UNLIKELY(error.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) { 216 NS_WARNING( 217 "EditorBase::CollapseSelectionTo() caused destroying the editor"); 218 return NS_ERROR_EDITOR_DESTROYED; 219 } 220 NS_ASSERTION(!error.Failed(), 221 "EditorBase::CollapseSelectionTo() failed, but ignored"); 222 return NS_OK; 223 } 224 225 /****************************************************************************** 226 * 227 ******************************************************************************/ 228 229 std::ostream& operator<<(std::ostream& aStream, 230 const ReplaceTextInTextNodeTransaction& aTransaction) { 231 aStream << "{ mTextNode=" << aTransaction.mTextNode.get(); 232 if (aTransaction.mTextNode) { 233 aStream << " (" << *aTransaction.mTextNode << ")"; 234 } 235 aStream << ", mStringToInsert=\"" 236 << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\"" 237 << ", mStringToBeReplaced=\"" 238 << NS_ConvertUTF16toUTF8(aTransaction.mStringToBeReplaced).get() 239 << "\", mOffset=" << aTransaction.mOffset 240 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 241 return aStream; 242 } 243 244 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReplaceTextInTextNodeTransaction, 245 ReplaceTextTransaction, mTextNode) 246 247 NS_IMPL_ADDREF_INHERITED(ReplaceTextInTextNodeTransaction, 248 ReplaceTextTransaction) 249 NS_IMPL_RELEASE_INHERITED(ReplaceTextInTextNodeTransaction, 250 ReplaceTextTransaction) 251 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReplaceTextInTextNodeTransaction) 252 NS_INTERFACE_MAP_END_INHERITING(ReplaceTextTransaction) 253 254 } // namespace mozilla