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/. */
6 #include "InsertTextTransaction.h"
8 #include "mozilla/EditorBase.h" // mEditorBase
9 #include "mozilla/Logging.h"
10 #include "mozilla/SelectionState.h" // RangeUpdater
11 #include "mozilla/ToString.h"
12 #include "mozilla/dom/Selection.h" // Selection local var
13 #include "mozilla/dom/Text.h" // mTextNode
14 #include "nsAString.h" // nsAString parameter
15 #include "nsDebug.h" // for NS_ASSERTION, etc.
16 #include "nsError.h" // for NS_OK, etc.
17 #include "nsQueryObject.h" // for do_QueryObject
24 already_AddRefed
<InsertTextTransaction
> InsertTextTransaction::Create(
25 EditorBase
& aEditorBase
, const nsAString
& aStringToInsert
,
26 const EditorDOMPointInText
& aPointToInsert
) {
27 MOZ_ASSERT(aPointToInsert
.IsSetAndValid());
28 RefPtr
<InsertTextTransaction
> transaction
=
29 new InsertTextTransaction(aEditorBase
, aStringToInsert
, aPointToInsert
);
30 return transaction
.forget();
33 InsertTextTransaction::InsertTextTransaction(
34 EditorBase
& aEditorBase
, const nsAString
& aStringToInsert
,
35 const EditorDOMPointInText
& aPointToInsert
)
36 : mTextNode(aPointToInsert
.ContainerAsText()),
37 mOffset(aPointToInsert
.Offset()),
38 mStringToInsert(aStringToInsert
),
39 mEditorBase(&aEditorBase
) {}
41 std::ostream
& operator<<(std::ostream
& aStream
,
42 const InsertTextTransaction
& aTransaction
) {
43 aStream
<< "{ mTextNode=" << aTransaction
.mTextNode
.get();
44 if (aTransaction
.mTextNode
) {
45 aStream
<< " (" << *aTransaction
.mTextNode
<< ")";
47 aStream
<< ", mOffset=" << aTransaction
.mOffset
<< ", mStringToInsert=\""
48 << NS_ConvertUTF16toUTF8(aTransaction
.mStringToInsert
).get() << "\""
49 << ", mEditorBase=" << aTransaction
.mEditorBase
.get() << " }";
53 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction
, EditTransactionBase
,
54 mEditorBase
, mTextNode
)
56 NS_IMPL_ADDREF_INHERITED(InsertTextTransaction
, EditTransactionBase
)
57 NS_IMPL_RELEASE_INHERITED(InsertTextTransaction
, EditTransactionBase
)
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextTransaction
)
59 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase
)
61 NS_IMETHODIMP
InsertTextTransaction::DoTransaction() {
62 MOZ_LOG(GetLogModule(), LogLevel::Info
,
63 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__
,
64 ToString(*this).c_str()));
66 if (NS_WARN_IF(!mEditorBase
) || NS_WARN_IF(!mTextNode
)) {
67 return NS_ERROR_NOT_AVAILABLE
;
70 OwningNonNull
<EditorBase
> editorBase
= *mEditorBase
;
71 OwningNonNull
<Text
> textNode
= *mTextNode
;
74 editorBase
->DoInsertText(textNode
, mOffset
, mStringToInsert
, error
);
76 NS_WARNING("EditorBase::DoInsertText() failed");
77 return error
.StealNSResult();
80 // Only set selection to insertion point if editor gives permission
81 if (editorBase
->AllowsTransactionsToChangeSelection()) {
82 RefPtr
<Selection
> selection
= editorBase
->GetSelection();
83 if (NS_WARN_IF(!selection
)) {
84 return NS_ERROR_FAILURE
;
86 DebugOnly
<nsresult
> rvIgnored
= editorBase
->CollapseSelectionTo(
87 EditorRawDOMPoint(textNode
, mOffset
+ mStringToInsert
.Length()));
88 NS_ASSERTION(NS_SUCCEEDED(rvIgnored
),
89 "EditorBase::CollapseSelectionTo() failed, but ignored");
90 // Keep handling to adjust the ranges in the range updater even if the
91 // editor is destroyed.
93 // Do nothing - DOM Range gravity will adjust selection
95 // XXX Other transactions do not do this but its callers do.
96 // Why do this transaction do this by itself?
97 editorBase
->RangeUpdaterRef().SelAdjInsertText(textNode
, mOffset
,
98 mStringToInsert
.Length());
100 return MOZ_UNLIKELY(editorBase
->Destroyed()) ? NS_ERROR_EDITOR_DESTROYED
104 NS_IMETHODIMP
InsertTextTransaction::UndoTransaction() {
105 MOZ_LOG(GetLogModule(), LogLevel::Info
,
106 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__
,
107 ToString(*this).c_str()));
109 if (NS_WARN_IF(!mEditorBase
) || NS_WARN_IF(!mTextNode
)) {
110 return NS_ERROR_NOT_INITIALIZED
;
112 OwningNonNull
<EditorBase
> editorBase
= *mEditorBase
;
113 OwningNonNull
<Text
> textNode
= *mTextNode
;
115 editorBase
->DoDeleteText(textNode
, mOffset
, mStringToInsert
.Length(), error
);
116 NS_WARNING_ASSERTION(!error
.Failed(), "EditorBase::DoDeleteText() failed");
117 return error
.StealNSResult();
120 NS_IMETHODIMP
InsertTextTransaction::RedoTransaction() {
121 MOZ_LOG(GetLogModule(), LogLevel::Info
,
122 ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__
,
123 ToString(*this).c_str()));
124 return DoTransaction();
127 NS_IMETHODIMP
InsertTextTransaction::Merge(nsITransaction
* aOtherTransaction
,
129 MOZ_LOG(GetLogModule(), LogLevel::Debug
,
130 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) this=%s", this,
131 __FUNCTION__
, aOtherTransaction
, ToString(*this).c_str()));
133 if (NS_WARN_IF(!aOtherTransaction
) || NS_WARN_IF(!aDidMerge
)) {
134 return NS_ERROR_INVALID_ARG
;
136 // Set out param default value
139 RefPtr
<EditTransactionBase
> otherTransactionBase
=
140 aOtherTransaction
->GetAsEditTransactionBase();
141 if (!otherTransactionBase
) {
143 GetLogModule(), LogLevel::Debug
,
144 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned false",
145 this, __FUNCTION__
, aOtherTransaction
));
149 // If aTransaction is a InsertTextTransaction, and if the selection hasn't
150 // changed, then absorb it.
151 InsertTextTransaction
* otherInsertTextTransaction
=
152 otherTransactionBase
->GetAsInsertTextTransaction();
153 if (!otherInsertTextTransaction
||
154 !IsSequentialInsert(*otherInsertTextTransaction
)) {
156 GetLogModule(), LogLevel::Debug
,
157 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned false",
158 this, __FUNCTION__
, aOtherTransaction
));
162 nsAutoString otherData
;
163 otherInsertTextTransaction
->GetData(otherData
);
164 mStringToInsert
+= otherData
;
166 MOZ_LOG(GetLogModule(), LogLevel::Debug
,
167 ("%p InsertTextTransaction::%s(aOtherTransaction=%p) returned true",
168 this, __FUNCTION__
, aOtherTransaction
));
172 /* ============ private methods ================== */
174 void InsertTextTransaction::GetData(nsString
& aResult
) {
175 aResult
= mStringToInsert
;
178 bool InsertTextTransaction::IsSequentialInsert(
179 InsertTextTransaction
& aOtherTransaction
) {
180 return aOtherTransaction
.mTextNode
== mTextNode
&&
181 aOtherTransaction
.mOffset
== mOffset
+ mStringToInsert
.Length();
184 } // namespace mozilla