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