/src/mozilla-central/editor/libeditor/PlaceholderTransaction.cpp
Line | Count | Source (jump to first uncovered line) |
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 "PlaceholderTransaction.h" |
7 | | |
8 | | #include "CompositionTransaction.h" |
9 | | #include "mozilla/EditorBase.h" |
10 | | #include "mozilla/dom/Selection.h" |
11 | | #include "mozilla/Move.h" |
12 | | #include "nsGkAtoms.h" |
13 | | #include "nsQueryObject.h" |
14 | | |
15 | | namespace mozilla { |
16 | | |
17 | | using namespace dom; |
18 | | |
19 | | PlaceholderTransaction::PlaceholderTransaction( |
20 | | EditorBase& aEditorBase, |
21 | | nsAtom* aName, |
22 | | Maybe<SelectionState>&& aSelState) |
23 | | : mEditorBase(&aEditorBase) |
24 | | , mForwarding(nullptr) |
25 | | , mCompositionTransaction(nullptr) |
26 | | , mStartSel(*std::move(aSelState)) |
27 | | , mAbsorb(true) |
28 | | , mCommitted(false) |
29 | 0 | { |
30 | 0 | mName = aName; |
31 | 0 | } |
32 | | |
33 | | PlaceholderTransaction::~PlaceholderTransaction() |
34 | 0 | { |
35 | 0 | } |
36 | | |
37 | | NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTransaction) |
38 | | |
39 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction, |
40 | 0 | EditAggregateTransaction) |
41 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase); |
42 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSel); |
43 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel); |
44 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
45 | | |
46 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction, |
47 | 0 | EditAggregateTransaction) |
48 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase); |
49 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSel); |
50 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel); |
51 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
52 | | |
53 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTransaction) |
54 | 0 | NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction) |
55 | 0 | NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction) |
56 | | |
57 | | NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction) |
58 | | NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction) |
59 | | |
60 | | NS_IMETHODIMP |
61 | | PlaceholderTransaction::DoTransaction() |
62 | 0 | { |
63 | 0 | return NS_OK; |
64 | 0 | } |
65 | | |
66 | | NS_IMETHODIMP |
67 | | PlaceholderTransaction::UndoTransaction() |
68 | 0 | { |
69 | 0 | if (NS_WARN_IF(!mEditorBase)) { |
70 | 0 | return NS_ERROR_NOT_INITIALIZED; |
71 | 0 | } |
72 | 0 | |
73 | 0 | // Undo transactions. |
74 | 0 | nsresult rv = EditAggregateTransaction::UndoTransaction(); |
75 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
76 | 0 |
|
77 | 0 | // now restore selection |
78 | 0 | RefPtr<Selection> selection = mEditorBase->GetSelection(); |
79 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
80 | 0 | return mStartSel.RestoreSelection(selection); |
81 | 0 | } |
82 | | |
83 | | NS_IMETHODIMP |
84 | | PlaceholderTransaction::RedoTransaction() |
85 | 0 | { |
86 | 0 | if (NS_WARN_IF(!mEditorBase)) { |
87 | 0 | return NS_ERROR_NOT_INITIALIZED; |
88 | 0 | } |
89 | 0 | |
90 | 0 | // Redo transactions. |
91 | 0 | nsresult rv = EditAggregateTransaction::RedoTransaction(); |
92 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
93 | 0 |
|
94 | 0 | // now restore selection |
95 | 0 | RefPtr<Selection> selection = mEditorBase->GetSelection(); |
96 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
97 | 0 | return mEndSel.RestoreSelection(selection); |
98 | 0 | } |
99 | | |
100 | | |
101 | | NS_IMETHODIMP |
102 | | PlaceholderTransaction::Merge(nsITransaction* aTransaction, |
103 | | bool* aDidMerge) |
104 | 0 | { |
105 | 0 | NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER); |
106 | 0 |
|
107 | 0 | // set out param default value |
108 | 0 | *aDidMerge=false; |
109 | 0 |
|
110 | 0 | if (mForwarding) { |
111 | 0 | MOZ_ASSERT_UNREACHABLE("tried to merge into a placeholder that was in " |
112 | 0 | "forwarding mode!"); |
113 | 0 | return NS_ERROR_FAILURE; |
114 | 0 | } |
115 | 0 |
|
116 | 0 | // XXX: hack, not safe! need nsIEditTransaction! |
117 | 0 | EditTransactionBase* editTransactionBase = (EditTransactionBase*)aTransaction; |
118 | 0 | // determine if this incoming txn is a placeholder txn |
119 | 0 | nsCOMPtr<nsIAbsorbingTransaction> absorbingTransaction = |
120 | 0 | do_QueryObject(editTransactionBase); |
121 | 0 |
|
122 | 0 | // We are absorbing all transactions if mAbsorb is lit. |
123 | 0 | if (mAbsorb) { |
124 | 0 | RefPtr<CompositionTransaction> otherTransaction = |
125 | 0 | do_QueryObject(aTransaction); |
126 | 0 | if (otherTransaction) { |
127 | 0 | // special handling for CompositionTransaction's: they need to merge with |
128 | 0 | // any previous CompositionTransaction in this placeholder, if possible. |
129 | 0 | if (!mCompositionTransaction) { |
130 | 0 | // this is the first IME txn in the placeholder |
131 | 0 | mCompositionTransaction = otherTransaction; |
132 | 0 | AppendChild(editTransactionBase); |
133 | 0 | } else { |
134 | 0 | bool didMerge; |
135 | 0 | mCompositionTransaction->Merge(otherTransaction, &didMerge); |
136 | 0 | if (!didMerge) { |
137 | 0 | // it wouldn't merge. Earlier IME txn is already committed and will |
138 | 0 | // not absorb further IME txns. So just stack this one after it |
139 | 0 | // and remember it as a candidate for further merges. |
140 | 0 | mCompositionTransaction = otherTransaction; |
141 | 0 | AppendChild(editTransactionBase); |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } else if (!absorbingTransaction) { |
145 | 0 | // See bug 171243: just drop incoming placeholders on the floor. |
146 | 0 | // Their children will be swallowed by this preexisting one. |
147 | 0 | AppendChild(editTransactionBase); |
148 | 0 | } |
149 | 0 | *aDidMerge = true; |
150 | 0 | // RememberEndingSelection(); |
151 | 0 | // efficiency hack: no need to remember selection here, as we haven't yet |
152 | 0 | // finished the initial batch and we know we will be told when the batch ends. |
153 | 0 | // we can remeber the selection then. |
154 | 0 | } else { |
155 | 0 | // merge typing or IME or deletion transactions if the selection matches |
156 | 0 | if ((mName.get() == nsGkAtoms::TypingTxnName || |
157 | 0 | mName.get() == nsGkAtoms::IMETxnName || |
158 | 0 | mName.get() == nsGkAtoms::DeleteTxnName) && !mCommitted) { |
159 | 0 | if (absorbingTransaction) { |
160 | 0 | RefPtr<nsAtom> atom; |
161 | 0 | absorbingTransaction->GetTxnName(getter_AddRefs(atom)); |
162 | 0 | if (atom && atom == mName) { |
163 | 0 | // check if start selection of next placeholder matches |
164 | 0 | // end selection of this placeholder |
165 | 0 | bool isSame; |
166 | 0 | absorbingTransaction->StartSelectionEquals(&mEndSel, &isSame); |
167 | 0 | if (isSame) { |
168 | 0 | mAbsorb = true; // we need to start absorbing again |
169 | 0 | absorbingTransaction->ForwardEndBatchTo(this); |
170 | 0 | // AppendChild(editTransactionBase); |
171 | 0 | // see bug 171243: we don't need to merge placeholders |
172 | 0 | // into placeholders. We just reactivate merging in the pre-existing |
173 | 0 | // placeholder and drop the new one on the floor. The EndPlaceHolderBatch() |
174 | 0 | // call on the new placeholder will be forwarded to this older one. |
175 | 0 | RememberEndingSelection(); |
176 | 0 | *aDidMerge = true; |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | | |
185 | | NS_IMETHODIMP |
186 | | PlaceholderTransaction::GetTxnName(nsAtom** aName) |
187 | 0 | { |
188 | 0 | return GetName(aName); |
189 | 0 | } |
190 | | |
191 | | NS_IMETHODIMP |
192 | | PlaceholderTransaction::StartSelectionEquals(SelectionState* aSelState, |
193 | | bool* aResult) |
194 | 0 | { |
195 | 0 | // determine if starting selection matches the given selection state. |
196 | 0 | // note that we only care about collapsed selections. |
197 | 0 | NS_ENSURE_TRUE(aResult && aSelState, NS_ERROR_NULL_POINTER); |
198 | 0 | if (!mStartSel.IsCollapsed() || !aSelState->IsCollapsed()) { |
199 | 0 | *aResult = false; |
200 | 0 | return NS_OK; |
201 | 0 | } |
202 | 0 | *aResult = mStartSel.IsEqual(aSelState); |
203 | 0 | return NS_OK; |
204 | 0 | } |
205 | | |
206 | | NS_IMETHODIMP |
207 | | PlaceholderTransaction::EndPlaceHolderBatch() |
208 | 0 | { |
209 | 0 | mAbsorb = false; |
210 | 0 |
|
211 | 0 | if (mForwarding) { |
212 | 0 | nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mForwarding); |
213 | 0 | if (plcTxn) { |
214 | 0 | plcTxn->EndPlaceHolderBatch(); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | // remember our selection state. |
218 | 0 | return RememberEndingSelection(); |
219 | 0 | } |
220 | | |
221 | | NS_IMETHODIMP |
222 | | PlaceholderTransaction::ForwardEndBatchTo( |
223 | | nsIAbsorbingTransaction* aForwardingAddress) |
224 | 0 | { |
225 | 0 | mForwarding = do_GetWeakReference(aForwardingAddress); |
226 | 0 | return NS_OK; |
227 | 0 | } |
228 | | |
229 | | NS_IMETHODIMP |
230 | | PlaceholderTransaction::Commit() |
231 | 0 | { |
232 | 0 | mCommitted = true; |
233 | 0 | return NS_OK; |
234 | 0 | } |
235 | | |
236 | | nsresult |
237 | | PlaceholderTransaction::RememberEndingSelection() |
238 | 0 | { |
239 | 0 | if (NS_WARN_IF(!mEditorBase)) { |
240 | 0 | return NS_ERROR_NOT_INITIALIZED; |
241 | 0 | } |
242 | 0 | |
243 | 0 | RefPtr<Selection> selection = mEditorBase->GetSelection(); |
244 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
245 | 0 | mEndSel.SaveSelection(selection); |
246 | 0 | return NS_OK; |
247 | 0 | } |
248 | | |
249 | | } // namespace mozilla |