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 "DeleteNodeTxn.h"
7 #include "DeleteRangeTxn.h"
8 #include "DeleteTextTxn.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/dom/Selection.h"
11 #include "mozilla/mozalloc.h"
16 #include "nsIContent.h"
17 #include "nsIContentIterator.h"
18 #include "nsIDOMCharacterData.h"
20 #include "nsAString.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 // note that aEditor is not refcounted
28 DeleteRangeTxn::DeleteRangeTxn()
32 mRangeUpdater(nullptr)
36 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTxn
, EditAggregateTxn
,
39 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn
)
40 NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn
)
43 DeleteRangeTxn::Init(nsEditor
* aEditor
,
45 nsRangeUpdater
* aRangeUpdater
)
47 MOZ_ASSERT(aEditor
&& aRange
);
50 mRange
= aRange
->CloneRange();
51 mRangeUpdater
= aRangeUpdater
;
53 NS_ENSURE_TRUE(mEditor
->IsModifiableNode(mRange
->GetStartParent()),
55 NS_ENSURE_TRUE(mEditor
->IsModifiableNode(mRange
->GetEndParent()),
57 NS_ENSURE_TRUE(mEditor
->IsModifiableNode(mRange
->GetCommonAncestor()),
64 DeleteRangeTxn::DoTransaction()
66 MOZ_ASSERT(mRange
&& mEditor
);
69 // build the child transactions
70 nsCOMPtr
<nsINode
> startParent
= mRange
->GetStartParent();
71 int32_t startOffset
= mRange
->StartOffset();
72 nsCOMPtr
<nsINode
> endParent
= mRange
->GetEndParent();
73 int32_t endOffset
= mRange
->EndOffset();
74 MOZ_ASSERT(startParent
&& endParent
);
76 if (startParent
== endParent
) {
77 // the selection begins and ends in the same node
78 res
= CreateTxnsToDeleteBetween(startParent
, startOffset
, endOffset
);
79 NS_ENSURE_SUCCESS(res
, res
);
81 // the selection ends in a different node from where it started. delete
82 // the relevant content in the start node
83 res
= CreateTxnsToDeleteContent(startParent
, startOffset
, nsIEditor::eNext
);
84 NS_ENSURE_SUCCESS(res
, res
);
85 // delete the intervening nodes
86 res
= CreateTxnsToDeleteNodesBetween();
87 NS_ENSURE_SUCCESS(res
, res
);
88 // delete the relevant content in the end node
89 res
= CreateTxnsToDeleteContent(endParent
, endOffset
, nsIEditor::ePrevious
);
90 NS_ENSURE_SUCCESS(res
, res
);
93 // if we've successfully built this aggregate transaction, then do it.
94 res
= EditAggregateTxn::DoTransaction();
95 NS_ENSURE_SUCCESS(res
, res
);
97 // only set selection to deletion point if editor gives permission
98 bool bAdjustSelection
;
99 mEditor
->ShouldTxnSetSelection(&bAdjustSelection
);
100 if (bAdjustSelection
) {
101 nsRefPtr
<Selection
> selection
= mEditor
->GetSelection();
102 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
103 res
= selection
->Collapse(startParent
, startOffset
);
104 NS_ENSURE_SUCCESS(res
, res
);
106 // else do nothing - dom range gravity will adjust selection
112 DeleteRangeTxn::UndoTransaction()
114 MOZ_ASSERT(mRange
&& mEditor
);
116 return EditAggregateTxn::UndoTransaction();
120 DeleteRangeTxn::RedoTransaction()
122 MOZ_ASSERT(mRange
&& mEditor
);
124 return EditAggregateTxn::RedoTransaction();
128 DeleteRangeTxn::GetTxnDescription(nsAString
& aString
)
130 aString
.AssignLiteral("DeleteRangeTxn");
135 DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode
* aNode
,
136 int32_t aStartOffset
,
139 // see what kind of node we have
140 if (aNode
->IsNodeOfType(nsINode::eDATA_NODE
)) {
141 // if the node is a chardata node, then delete chardata content
143 if (aStartOffset
== aEndOffset
) {
146 numToDel
= aEndOffset
- aStartOffset
;
149 nsRefPtr
<nsGenericDOMDataNode
> charDataNode
=
150 static_cast<nsGenericDOMDataNode
*>(aNode
);
152 nsRefPtr
<DeleteTextTxn
> txn
=
153 new DeleteTextTxn(*mEditor
, *charDataNode
, aStartOffset
, numToDel
,
156 nsresult res
= txn
->Init();
157 NS_ENSURE_SUCCESS(res
, res
);
163 nsCOMPtr
<nsIContent
> child
= aNode
->GetChildAt(aStartOffset
);
164 NS_ENSURE_STATE(child
);
166 nsresult res
= NS_OK
;
167 for (int32_t i
= aStartOffset
; i
< aEndOffset
; ++i
) {
168 nsRefPtr
<DeleteNodeTxn
> txn
= new DeleteNodeTxn();
169 res
= txn
->Init(mEditor
, child
, mRangeUpdater
);
170 if (NS_SUCCEEDED(res
)) {
174 child
= child
->GetNextSibling();
177 NS_ENSURE_SUCCESS(res
, res
);
182 DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode
* aNode
,
184 nsIEditor::EDirection aAction
)
186 // see what kind of node we have
187 if (aNode
->IsNodeOfType(nsINode::eDATA_NODE
)) {
188 // if the node is a chardata node, then delete chardata content
189 uint32_t start
, numToDelete
;
190 if (nsIEditor::eNext
== aAction
) {
192 numToDelete
= aNode
->Length() - aOffset
;
195 numToDelete
= aOffset
;
199 nsRefPtr
<nsGenericDOMDataNode
> dataNode
=
200 static_cast<nsGenericDOMDataNode
*>(aNode
);
201 nsRefPtr
<DeleteTextTxn
> txn
= new DeleteTextTxn(*mEditor
, *dataNode
,
202 start
, numToDelete
, mRangeUpdater
);
204 nsresult res
= txn
->Init();
205 NS_ENSURE_SUCCESS(res
, res
);
215 DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
217 nsCOMPtr
<nsIContentIterator
> iter
= NS_NewContentSubtreeIterator();
219 nsresult res
= iter
->Init(mRange
);
220 NS_ENSURE_SUCCESS(res
, res
);
222 while (!iter
->IsDone()) {
223 nsCOMPtr
<nsINode
> node
= iter
->GetCurrentNode();
224 NS_ENSURE_TRUE(node
, NS_ERROR_NULL_POINTER
);
226 nsRefPtr
<DeleteNodeTxn
> txn
= new DeleteNodeTxn();
228 res
= txn
->Init(mEditor
, node
, mRangeUpdater
);
229 NS_ENSURE_SUCCESS(res
, res
);