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