InsertTextTransaction.cpp (8948B)
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 "InsertTextTransaction.h" 7 8 #include "ErrorList.h" 9 #include "mozilla/EditorBase.h" // mEditorBase 10 #include "mozilla/Logging.h" 11 #include "mozilla/SelectionState.h" // RangeUpdater 12 #include "mozilla/TextEditor.h" // TextEditor 13 #include "mozilla/ToString.h" 14 #include "mozilla/dom/Selection.h" // Selection local var 15 #include "mozilla/dom/Text.h" // mTextNode 16 17 #include "nsAString.h" // nsAString parameter 18 #include "nsDebug.h" // for NS_ASSERTION, etc. 19 #include "nsError.h" // for NS_OK, etc. 20 #include "nsQueryObject.h" // for do_QueryObject 21 22 namespace mozilla { 23 24 using namespace dom; 25 26 // static 27 already_AddRefed<InsertTextTransaction> InsertTextTransaction::Create( 28 EditorBase& aEditorBase, const nsAString& aStringToInsert, 29 const EditorDOMPointInText& aPointToInsert) { 30 MOZ_ASSERT(aPointToInsert.IsSetAndValid()); 31 RefPtr<InsertTextTransaction> transaction = 32 aEditorBase.IsTextEditor() 33 ? new InsertTextTransaction(aEditorBase, aStringToInsert, 34 aPointToInsert) 35 : new InsertTextIntoTextNodeTransaction(aEditorBase, aStringToInsert, 36 aPointToInsert); 37 return transaction.forget(); 38 } 39 40 InsertTextTransaction::InsertTextTransaction( 41 EditorBase& aEditorBase, const nsAString& aStringToInsert, 42 const EditorDOMPointInText& aPointToInsert) 43 : mEditorBase(&aEditorBase), 44 mStringToInsert(aStringToInsert), 45 mOffset(aPointToInsert.Offset()) {} 46 47 std::ostream& operator<<(std::ostream& aStream, 48 const InsertTextTransaction& aTransaction) { 49 const auto* transactionForHTMLEditor = 50 aTransaction.GetAsInsertTextIntoTextNodeTransaction(); 51 if (transactionForHTMLEditor) { 52 return aStream << *transactionForHTMLEditor; 53 } 54 aStream << "{ mOffset=" << aTransaction.mOffset << ", mStringToInsert=\"" 55 << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\"" 56 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 57 return aStream; 58 } 59 60 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction, EditTransactionBase, 61 mEditorBase) 62 63 NS_IMPL_ADDREF_INHERITED(InsertTextTransaction, EditTransactionBase) 64 NS_IMPL_RELEASE_INHERITED(InsertTextTransaction, EditTransactionBase) 65 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextTransaction) 66 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) 67 68 Text* InsertTextTransaction::GetTextNode() const { 69 if (MOZ_UNLIKELY(!mEditorBase)) { 70 return nullptr; 71 } 72 if (TextEditor* const textEditor = mEditorBase->GetAsTextEditor()) { 73 return textEditor->GetTextNode(); 74 } 75 MOZ_ASSERT(GetAsInsertTextIntoTextNodeTransaction()); 76 return GetAsInsertTextIntoTextNodeTransaction()->mTextNode; 77 } 78 79 NS_IMETHODIMP InsertTextTransaction::DoTransaction() { 80 MOZ_LOG(GetLogModule(), LogLevel::Info, 81 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__, 82 ToString(*this).c_str())); 83 84 if (NS_WARN_IF(!mEditorBase)) { 85 return NS_ERROR_NOT_AVAILABLE; 86 } 87 88 const RefPtr<Text> textNode = GetTextNode(); 89 if (NS_WARN_IF(!textNode)) { 90 return NS_ERROR_NOT_AVAILABLE; 91 } 92 93 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 94 ErrorResult error; 95 editorBase->DoInsertText(*textNode, mOffset, mStringToInsert, error); 96 if (error.Failed()) { 97 NS_WARNING("EditorBase::DoInsertText() failed"); 98 return error.StealNSResult(); 99 } 100 101 editorBase->RangeUpdaterRef().SelAdjInsertText(*textNode, mOffset, 102 mStringToInsert.Length()); 103 return NS_OK; 104 } 105 106 NS_IMETHODIMP InsertTextTransaction::UndoTransaction() { 107 MOZ_LOG(GetLogModule(), LogLevel::Info, 108 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__, 109 ToString(*this).c_str())); 110 111 if (NS_WARN_IF(!mEditorBase)) { 112 return NS_ERROR_NOT_INITIALIZED; 113 } 114 115 const RefPtr<Text> textNode = GetTextNode(); 116 if (NS_WARN_IF(!textNode)) { 117 return NS_ERROR_NOT_AVAILABLE; 118 } 119 120 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 121 ErrorResult error; 122 editorBase->DoDeleteText(*textNode, mOffset, mStringToInsert.Length(), error); 123 NS_WARNING_ASSERTION(!error.Failed(), "EditorBase::DoDeleteText() failed"); 124 return error.StealNSResult(); 125 } 126 127 NS_IMETHODIMP InsertTextTransaction::RedoTransaction() { 128 MOZ_LOG(GetLogModule(), LogLevel::Info, 129 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__, 130 !mEditorBase || mEditorBase->IsTextEditor() 131 ? ToString(*this).c_str() 132 : ToString(*GetAsInsertTextIntoTextNodeTransaction()).c_str())); 133 nsresult rv = DoTransaction(); 134 if (NS_FAILED(rv)) { 135 NS_WARNING("InsertTextTransaction::DoTransaction() failed"); 136 return rv; 137 } 138 if (RefPtr<EditorBase> editorBase = mEditorBase) { 139 nsresult rv = editorBase->CollapseSelectionTo( 140 SuggestPointToPutCaret<EditorRawDOMPoint>()); 141 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 142 return NS_ERROR_EDITOR_DESTROYED; 143 } 144 NS_WARNING_ASSERTION( 145 NS_SUCCEEDED(rv), 146 "EditorBase::CollapseSelectionTo() failed, but ignored"); 147 } 148 return NS_OK; 149 } 150 151 NS_IMETHODIMP InsertTextTransaction::Merge(nsITransaction* aOtherTransaction, 152 bool* aDidMerge) { 153 MOZ_LOG(GetLogModule(), LogLevel::Debug, 154 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) this=%s", this, 155 __FUNCTION__, aOtherTransaction, ToString(*this).c_str())); 156 157 if (NS_WARN_IF(!aOtherTransaction) || NS_WARN_IF(!aDidMerge)) { 158 return NS_ERROR_INVALID_ARG; 159 } 160 // Set out param default value 161 *aDidMerge = false; 162 163 RefPtr<EditTransactionBase> otherTransactionBase = 164 aOtherTransaction->GetAsEditTransactionBase(); 165 if (!otherTransactionBase) { 166 MOZ_LOG( 167 GetLogModule(), LogLevel::Debug, 168 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned false", 169 this, __FUNCTION__, aOtherTransaction)); 170 return NS_OK; 171 } 172 173 // If aTransaction is a InsertTextTransaction, and if the selection hasn't 174 // changed, then absorb it. 175 InsertTextTransaction* otherInsertTextTransaction = 176 otherTransactionBase->GetAsInsertTextTransaction(); 177 if (!otherInsertTextTransaction || 178 !IsSequentialInsert(*otherInsertTextTransaction)) { 179 MOZ_LOG( 180 GetLogModule(), LogLevel::Debug, 181 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned false", 182 this, __FUNCTION__, aOtherTransaction)); 183 return NS_OK; 184 } 185 186 mStringToInsert += otherInsertTextTransaction->GetData(); 187 *aDidMerge = true; 188 MOZ_LOG(GetLogModule(), LogLevel::Debug, 189 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned true", 190 this, __FUNCTION__, aOtherTransaction)); 191 return NS_OK; 192 } 193 194 bool InsertTextTransaction::IsSequentialInsert( 195 InsertTextTransaction& aOtherTransaction) const { 196 return aOtherTransaction.GetTextNode() == GetTextNode() && 197 aOtherTransaction.mOffset == mOffset + mStringToInsert.Length(); 198 } 199 200 /****************************************************************************** 201 * mozilla::InsertTextIntoTextNodeTransaction 202 ******************************************************************************/ 203 204 InsertTextIntoTextNodeTransaction::InsertTextIntoTextNodeTransaction( 205 EditorBase& aEditorBase, const nsAString& aStringToInsert, 206 const EditorDOMPointInText& aPointToInsert) 207 : InsertTextTransaction(aEditorBase, aStringToInsert, aPointToInsert), 208 mTextNode(aPointToInsert.ContainerAs<Text>()) { 209 MOZ_ASSERT(aEditorBase.IsHTMLEditor()); 210 } 211 212 std::ostream& operator<<( 213 std::ostream& aStream, 214 const InsertTextIntoTextNodeTransaction& aTransaction) { 215 aStream << "{ mTextNode=" << aTransaction.mTextNode.get(); 216 if (aTransaction.mTextNode) { 217 aStream << " (" << *aTransaction.mTextNode << ")"; 218 } 219 aStream << ", mOffset=" << aTransaction.mOffset << ", mStringToInsert=\"" 220 << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\"" 221 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 222 return aStream; 223 } 224 225 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextIntoTextNodeTransaction, 226 InsertTextTransaction, mTextNode) 227 228 NS_IMPL_ADDREF_INHERITED(InsertTextIntoTextNodeTransaction, 229 InsertTextTransaction) 230 NS_IMPL_RELEASE_INHERITED(InsertTextIntoTextNodeTransaction, 231 InsertTextTransaction) 232 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextIntoTextNodeTransaction) 233 NS_INTERFACE_MAP_END_INHERITING(InsertTextTransaction) 234 235 } // namespace mozilla