1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "DeleteRangeTxn.h"
40 : #include "nsIDOMRange.h"
41 : #include "nsIDOMCharacterData.h"
42 : #include "nsIDOMNodeList.h"
43 : #include "nsISelection.h"
44 : #include "DeleteTextTxn.h"
45 : #include "DeleteElementTxn.h"
46 : #include "nsIContentIterator.h"
47 : #include "nsIContent.h"
48 : #include "nsComponentManagerUtils.h"
49 :
50 : #ifdef NS_DEBUG
51 : static bool gNoisy = false;
52 : #endif
53 :
54 : // note that aEditor is not refcounted
55 0 : DeleteRangeTxn::DeleteRangeTxn()
56 : : EditAggregateTxn()
57 : ,mRange()
58 : ,mStartParent()
59 : ,mStartOffset(0)
60 : ,mEndParent()
61 : ,mCommonParent()
62 : ,mEndOffset(0)
63 : ,mEditor(nsnull)
64 0 : ,mRangeUpdater(nsnull)
65 : {
66 0 : }
67 :
68 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(DeleteRangeTxn)
69 :
70 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DeleteRangeTxn,
71 : EditAggregateTxn)
72 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRange)
73 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStartParent)
74 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEndParent)
75 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCommonParent)
76 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
77 :
78 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DeleteRangeTxn,
79 : EditAggregateTxn)
80 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRange)
81 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartParent)
82 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndParent)
83 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCommonParent)
84 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
85 :
86 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn)
87 0 : NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn)
88 :
89 0 : NS_IMETHODIMP DeleteRangeTxn::Init(nsIEditor *aEditor,
90 : nsIDOMRange *aRange,
91 : nsRangeUpdater *aRangeUpdater)
92 : {
93 0 : NS_ASSERTION(aEditor && aRange, "bad state");
94 0 : if (!aEditor || !aRange) { return NS_ERROR_NOT_INITIALIZED; }
95 :
96 0 : mEditor = aEditor;
97 0 : mRange = do_QueryInterface(aRange);
98 0 : mRangeUpdater = aRangeUpdater;
99 :
100 0 : nsresult result = aRange->GetStartContainer(getter_AddRefs(mStartParent));
101 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed.");
102 0 : result = aRange->GetEndContainer(getter_AddRefs(mEndParent));
103 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
104 0 : result = aRange->GetStartOffset(&mStartOffset);
105 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
106 0 : result = aRange->GetEndOffset(&mEndOffset);
107 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
108 0 : result = aRange->GetCommonAncestorContainer(getter_AddRefs(mCommonParent));
109 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
110 :
111 0 : if (!mEditor->IsModifiableNode(mStartParent)) {
112 0 : return NS_ERROR_FAILURE;
113 : }
114 :
115 0 : if (mStartParent!=mEndParent &&
116 0 : (!mEditor->IsModifiableNode(mEndParent) ||
117 0 : !mEditor->IsModifiableNode(mCommonParent)))
118 : {
119 0 : return NS_ERROR_FAILURE;
120 : }
121 :
122 : #ifdef NS_DEBUG
123 : {
124 : PRUint32 count;
125 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(mStartParent);
126 0 : if (textNode)
127 0 : textNode->GetLength(&count);
128 : else
129 : {
130 0 : nsCOMPtr<nsIDOMNodeList> children;
131 0 : result = mStartParent->GetChildNodes(getter_AddRefs(children));
132 0 : NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list");
133 0 : children->GetLength(&count);
134 : }
135 0 : NS_ASSERTION(mStartOffset<=(PRInt32)count, "bad start offset");
136 :
137 0 : textNode = do_QueryInterface(mEndParent);
138 0 : if (textNode)
139 0 : textNode->GetLength(&count);
140 : else
141 : {
142 0 : nsCOMPtr<nsIDOMNodeList> children;
143 0 : result = mEndParent->GetChildNodes(getter_AddRefs(children));
144 0 : NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list");
145 0 : children->GetLength(&count);
146 : }
147 0 : NS_ASSERTION(mEndOffset<=(PRInt32)count, "bad end offset");
148 :
149 : #ifdef NS_DEBUG
150 0 : if (gNoisy)
151 : {
152 : printf ("DeleteRange: %d of %p to %d of %p\n",
153 0 : mStartOffset, (void *)mStartParent, mEndOffset, (void *)mEndParent);
154 : }
155 : #endif
156 : }
157 : #endif
158 0 : return result;
159 :
160 : }
161 :
162 0 : NS_IMETHODIMP DeleteRangeTxn::DoTransaction(void)
163 : {
164 : #ifdef NS_DEBUG
165 0 : if (gNoisy) { printf("Do Delete Range\n"); }
166 : #endif
167 :
168 0 : NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
169 :
170 : nsresult result;
171 : // build the child transactions
172 :
173 0 : if (mStartParent==mEndParent)
174 : { // the selection begins and ends in the same node
175 0 : result = CreateTxnsToDeleteBetween(mStartParent, mStartOffset, mEndOffset);
176 : }
177 : else
178 : { // the selection ends in a different node from where it started
179 : // delete the relevant content in the start node
180 0 : result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eNext);
181 0 : if (NS_SUCCEEDED(result))
182 : {
183 : // delete the intervening nodes
184 0 : result = CreateTxnsToDeleteNodesBetween();
185 0 : if (NS_SUCCEEDED(result))
186 : {
187 : // delete the relevant content in the end node
188 0 : result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::ePrevious);
189 : }
190 : }
191 : }
192 :
193 : // if we've successfully built this aggregate transaction, then do it.
194 0 : if (NS_SUCCEEDED(result)) {
195 0 : result = EditAggregateTxn::DoTransaction();
196 : }
197 :
198 0 : NS_ENSURE_SUCCESS(result, result);
199 :
200 : // only set selection to deletion point if editor gives permission
201 : bool bAdjustSelection;
202 0 : mEditor->ShouldTxnSetSelection(&bAdjustSelection);
203 0 : if (bAdjustSelection)
204 : {
205 0 : nsCOMPtr<nsISelection> selection;
206 0 : result = mEditor->GetSelection(getter_AddRefs(selection));
207 : // At this point, it is possible that the frame for our root element
208 : // might have been destroyed, in which case, the above call returns
209 : // an error. We eat that error here intentionally. See bug 574558
210 : // for a sample case where this happens.
211 0 : NS_ENSURE_SUCCESS(result, NS_OK);
212 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
213 0 : result = selection->Collapse(mStartParent, mStartOffset);
214 : }
215 : else
216 : {
217 : // do nothing - dom range gravity will adjust selection
218 : }
219 :
220 0 : return result;
221 : }
222 :
223 0 : NS_IMETHODIMP DeleteRangeTxn::UndoTransaction(void)
224 : {
225 : #ifdef NS_DEBUG
226 0 : if (gNoisy) { printf("Undo Delete Range\n"); }
227 : #endif
228 :
229 0 : NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
230 :
231 0 : return EditAggregateTxn::UndoTransaction();
232 : }
233 :
234 0 : NS_IMETHODIMP DeleteRangeTxn::RedoTransaction(void)
235 : {
236 : #ifdef NS_DEBUG
237 0 : if (gNoisy) { printf("Redo Delete Range\n"); }
238 : #endif
239 :
240 0 : NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
241 :
242 0 : return EditAggregateTxn::RedoTransaction();
243 : }
244 :
245 0 : NS_IMETHODIMP DeleteRangeTxn::GetTxnDescription(nsAString& aString)
246 : {
247 0 : aString.AssignLiteral("DeleteRangeTxn");
248 0 : return NS_OK;
249 : }
250 :
251 : NS_IMETHODIMP
252 0 : DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
253 : PRUint32 aStartOffset,
254 : PRUint32 aEndOffset)
255 : {
256 0 : nsresult result = NS_OK;
257 : // see what kind of node we have
258 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aStartParent);
259 0 : if (textNode)
260 : { // if the node is a text node, then delete text content
261 0 : nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
262 0 : NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY);
263 :
264 : PRInt32 numToDel;
265 0 : if (aStartOffset==aEndOffset)
266 0 : numToDel = 1;
267 : else
268 0 : numToDel = aEndOffset-aStartOffset;
269 0 : result = txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
270 0 : if (NS_SUCCEEDED(result))
271 0 : AppendChild(txn);
272 : }
273 : else
274 : {
275 0 : nsCOMPtr<nsIDOMNodeList> children;
276 0 : result = aStartParent->GetChildNodes(getter_AddRefs(children));
277 0 : NS_ENSURE_SUCCESS(result, result);
278 0 : NS_ENSURE_TRUE(children, NS_ERROR_NULL_POINTER);
279 :
280 : #ifdef DEBUG
281 : PRUint32 childCount;
282 0 : children->GetLength(&childCount);
283 0 : NS_ASSERTION(aEndOffset<=childCount, "bad aEndOffset");
284 : #endif
285 : PRUint32 i;
286 0 : for (i=aStartOffset; i<aEndOffset; i++)
287 : {
288 0 : nsCOMPtr<nsIDOMNode> child;
289 0 : result = children->Item(i, getter_AddRefs(child));
290 0 : NS_ENSURE_SUCCESS(result, result);
291 0 : NS_ENSURE_TRUE(child, NS_ERROR_NULL_POINTER);
292 :
293 0 : nsRefPtr<DeleteElementTxn> txn = new DeleteElementTxn();
294 0 : NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY);
295 :
296 0 : result = txn->Init(mEditor, child, mRangeUpdater);
297 0 : if (NS_SUCCEEDED(result))
298 0 : AppendChild(txn);
299 : }
300 : }
301 0 : return result;
302 : }
303 :
304 0 : NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
305 : PRUint32 aOffset,
306 : nsIEditor::EDirection aAction)
307 : {
308 0 : nsresult result = NS_OK;
309 : // see what kind of node we have
310 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aParent);
311 0 : if (textNode)
312 : { // if the node is a text node, then delete text content
313 : PRUint32 start, numToDelete;
314 0 : if (nsIEditor::eNext == aAction)
315 : {
316 0 : start=aOffset;
317 0 : textNode->GetLength(&numToDelete);
318 0 : numToDelete -= aOffset;
319 : }
320 : else
321 : {
322 0 : start=0;
323 0 : numToDelete=aOffset;
324 : }
325 :
326 0 : if (numToDelete)
327 : {
328 0 : nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
329 0 : NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY);
330 :
331 0 : result = txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
332 0 : if (NS_SUCCEEDED(result))
333 0 : AppendChild(txn);
334 : }
335 : }
336 :
337 0 : return result;
338 : }
339 :
340 0 : NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
341 : {
342 0 : nsCOMPtr<nsIContentIterator> iter = do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1");
343 0 : NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
344 :
345 0 : nsresult result = iter->Init(mRange);
346 0 : NS_ENSURE_SUCCESS(result, result);
347 :
348 0 : while (!iter->IsDone() && NS_SUCCEEDED(result))
349 : {
350 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(iter->GetCurrentNode());
351 0 : NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
352 :
353 0 : nsRefPtr<DeleteElementTxn> txn = new DeleteElementTxn();
354 0 : NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY);
355 :
356 0 : result = txn->Init(mEditor, node, mRangeUpdater);
357 0 : if (NS_SUCCEEDED(result))
358 0 : AppendChild(txn);
359 0 : iter->Next();
360 : }
361 0 : return result;
362 4392 : }
363 :
|