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 "DeleteRangeTransaction.h"
7 :
8 : #include "DeleteNodeTransaction.h"
9 : #include "DeleteTextTransaction.h"
10 : #include "mozilla/Assertions.h"
11 : #include "mozilla/EditorBase.h"
12 : #include "mozilla/dom/Selection.h"
13 : #include "mozilla/mozalloc.h"
14 : #include "nsCOMPtr.h"
15 : #include "nsDebug.h"
16 : #include "nsError.h"
17 : #include "nsIContent.h"
18 : #include "nsIContentIterator.h"
19 : #include "nsINode.h"
20 : #include "nsAString.h"
21 :
22 : namespace mozilla {
23 :
24 : using namespace dom;
25 :
26 : // note that aEditorBase is not refcounted
27 0 : DeleteRangeTransaction::DeleteRangeTransaction(EditorBase& aEditorBase,
28 : nsRange& aRangeToDelete,
29 0 : RangeUpdater* aRangeUpdater)
30 : : mEditorBase(&aEditorBase)
31 0 : , mRangeToDelete(aRangeToDelete.CloneRange())
32 0 : , mRangeUpdater(aRangeUpdater)
33 : {
34 0 : }
35 :
36 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTransaction,
37 : EditAggregateTransaction,
38 : mEditorBase,
39 : mRangeToDelete)
40 :
41 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTransaction)
42 0 : NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
43 :
44 : NS_IMETHODIMP
45 0 : DeleteRangeTransaction::DoTransaction()
46 : {
47 0 : if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mRangeToDelete)) {
48 0 : return NS_ERROR_NOT_AVAILABLE;
49 : }
50 :
51 : // Swap mRangeToDelete out into a stack variable, so we make sure to null it
52 : // out on return from this function. Once this function returns, we no longer
53 : // need mRangeToDelete, and keeping it alive in the long term slows down all
54 : // DOM mutations because it's observing them.
55 0 : RefPtr<nsRange> rangeToDelete;
56 0 : rangeToDelete.swap(mRangeToDelete);
57 :
58 : // build the child transactions
59 0 : nsCOMPtr<nsINode> startContainer = rangeToDelete->GetStartContainer();
60 0 : int32_t startOffset = rangeToDelete->StartOffset();
61 0 : nsCOMPtr<nsINode> endContainer = rangeToDelete->GetEndContainer();
62 0 : int32_t endOffset = rangeToDelete->EndOffset();
63 0 : MOZ_ASSERT(startContainer && endContainer);
64 :
65 0 : if (startContainer == endContainer) {
66 : // the selection begins and ends in the same node
67 : nsresult rv =
68 0 : CreateTxnsToDeleteBetween(startContainer, startOffset, endOffset);
69 0 : NS_ENSURE_SUCCESS(rv, rv);
70 : } else {
71 : // the selection ends in a different node from where it started. delete
72 : // the relevant content in the start node
73 : nsresult rv =
74 0 : CreateTxnsToDeleteContent(startContainer, startOffset, nsIEditor::eNext);
75 0 : NS_ENSURE_SUCCESS(rv, rv);
76 : // delete the intervening nodes
77 0 : rv = CreateTxnsToDeleteNodesBetween(rangeToDelete);
78 0 : NS_ENSURE_SUCCESS(rv, rv);
79 : // delete the relevant content in the end node
80 0 : rv = CreateTxnsToDeleteContent(endContainer, endOffset,
81 0 : nsIEditor::ePrevious);
82 0 : NS_ENSURE_SUCCESS(rv, rv);
83 : }
84 :
85 : // if we've successfully built this aggregate transaction, then do it.
86 0 : nsresult rv = EditAggregateTransaction::DoTransaction();
87 0 : NS_ENSURE_SUCCESS(rv, rv);
88 :
89 : // only set selection to deletion point if editor gives permission
90 : bool bAdjustSelection;
91 0 : mEditorBase->ShouldTxnSetSelection(&bAdjustSelection);
92 0 : if (bAdjustSelection) {
93 0 : RefPtr<Selection> selection = mEditorBase->GetSelection();
94 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
95 0 : rv = selection->Collapse(startContainer, startOffset);
96 0 : NS_ENSURE_SUCCESS(rv, rv);
97 : }
98 : // else do nothing - dom range gravity will adjust selection
99 :
100 0 : return NS_OK;
101 : }
102 :
103 : NS_IMETHODIMP
104 0 : DeleteRangeTransaction::UndoTransaction()
105 : {
106 0 : return EditAggregateTransaction::UndoTransaction();
107 : }
108 :
109 : NS_IMETHODIMP
110 0 : DeleteRangeTransaction::RedoTransaction()
111 : {
112 0 : return EditAggregateTransaction::RedoTransaction();
113 : }
114 :
115 : NS_IMETHODIMP
116 0 : DeleteRangeTransaction::GetTxnDescription(nsAString& aString)
117 : {
118 0 : aString.AssignLiteral("DeleteRangeTransaction");
119 0 : return NS_OK;
120 : }
121 :
122 : nsresult
123 0 : DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
124 : int32_t aStartOffset,
125 : int32_t aEndOffset)
126 : {
127 0 : if (NS_WARN_IF(!mEditorBase)) {
128 0 : return NS_ERROR_NOT_AVAILABLE;
129 : }
130 :
131 : // see what kind of node we have
132 0 : if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
133 : // if the node is a chardata node, then delete chardata content
134 : int32_t numToDel;
135 0 : if (aStartOffset == aEndOffset) {
136 0 : numToDel = 1;
137 : } else {
138 0 : numToDel = aEndOffset - aStartOffset;
139 : }
140 :
141 : RefPtr<nsGenericDOMDataNode> charDataNode =
142 0 : static_cast<nsGenericDOMDataNode*>(aNode);
143 :
144 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
145 0 : new DeleteTextTransaction(*mEditorBase, *charDataNode, aStartOffset,
146 0 : numToDel, mRangeUpdater);
147 : // If the text node isn't editable, it should be never undone/redone.
148 : // So, the transaction shouldn't be recorded.
149 0 : if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) {
150 0 : return NS_ERROR_FAILURE;
151 : }
152 0 : AppendChild(deleteTextTransaction);
153 0 : return NS_OK;
154 : }
155 :
156 0 : nsCOMPtr<nsIContent> child = aNode->GetChildAt(aStartOffset);
157 0 : for (int32_t i = aStartOffset; i < aEndOffset; ++i) {
158 : // Even if we detect invalid range, we should ignore it for removing
159 : // specified range's nodes as far as possible.
160 0 : if (NS_WARN_IF(!child)) {
161 0 : break;
162 : }
163 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
164 0 : new DeleteNodeTransaction(*mEditorBase, *child, mRangeUpdater);
165 : // XXX This is odd handling. Even if some children are not editable,
166 : // editor should append transactions because they could be editable
167 : // at undoing/redoing. Additionally, if the transaction needs to
168 : // delete/restore all nodes, it should at undoing/redoing.
169 0 : if (deleteNodeTransaction->CanDoIt()) {
170 0 : AppendChild(deleteNodeTransaction);
171 : }
172 0 : child = child->GetNextSibling();
173 : }
174 :
175 0 : return NS_OK;
176 : }
177 :
178 : nsresult
179 0 : DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode,
180 : int32_t aOffset,
181 : nsIEditor::EDirection aAction)
182 : {
183 0 : if (NS_WARN_IF(!mEditorBase)) {
184 0 : return NS_ERROR_NOT_AVAILABLE;
185 : }
186 :
187 : // see what kind of node we have
188 0 : if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
189 : // if the node is a chardata node, then delete chardata content
190 : uint32_t start, numToDelete;
191 0 : if (nsIEditor::eNext == aAction) {
192 0 : start = aOffset;
193 0 : numToDelete = aNode->Length() - aOffset;
194 : } else {
195 0 : start = 0;
196 0 : numToDelete = aOffset;
197 : }
198 :
199 0 : if (numToDelete) {
200 : RefPtr<nsGenericDOMDataNode> dataNode =
201 0 : static_cast<nsGenericDOMDataNode*>(aNode);
202 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
203 0 : new DeleteTextTransaction(*mEditorBase, *dataNode, start, numToDelete,
204 0 : mRangeUpdater);
205 : // If the text node isn't editable, it should be never undone/redone.
206 : // So, the transaction shouldn't be recorded.
207 0 : if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) {
208 0 : return NS_ERROR_FAILURE;
209 : }
210 0 : AppendChild(deleteTextTransaction);
211 : }
212 : }
213 :
214 0 : return NS_OK;
215 : }
216 :
217 : nsresult
218 0 : DeleteRangeTransaction::CreateTxnsToDeleteNodesBetween(nsRange* aRangeToDelete)
219 : {
220 0 : if (NS_WARN_IF(!mEditorBase)) {
221 0 : return NS_ERROR_NOT_AVAILABLE;
222 : }
223 :
224 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
225 :
226 0 : nsresult rv = iter->Init(aRangeToDelete);
227 0 : NS_ENSURE_SUCCESS(rv, rv);
228 :
229 0 : while (!iter->IsDone()) {
230 0 : nsCOMPtr<nsINode> node = iter->GetCurrentNode();
231 0 : if (NS_WARN_IF(!node)) {
232 0 : return NS_ERROR_NULL_POINTER;
233 : }
234 :
235 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
236 0 : new DeleteNodeTransaction(*mEditorBase, *node, mRangeUpdater);
237 : // XXX This is odd handling. Even if some nodes in the range are not
238 : // editable, editor should append transactions because they could
239 : // at undoing/redoing. Additionally, if the transaction needs to
240 : // delete/restore all nodes, it should at undoing/redoing.
241 0 : if (NS_WARN_IF(!deleteNodeTransaction->CanDoIt())) {
242 0 : return NS_ERROR_FAILURE;
243 : }
244 0 : AppendChild(deleteNodeTransaction);
245 :
246 0 : iter->Next();
247 : }
248 0 : return NS_OK;
249 : }
250 :
251 : } // namespace mozilla
|