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 "DeleteTextTransaction.h"
7
8 #include "mozilla/Assertions.h"
9 #include "mozilla/EditorBase.h"
10 #include "mozilla/EditorDOMPoint.h"
11 #include "mozilla/SelectionState.h"
12 #include "mozilla/dom/Selection.h"
13 #include "nsDebug.h"
14 #include "nsError.h"
15 #include "nsIEditor.h"
16 #include "nsISupportsImpl.h"
17 #include "nsAString.h"
18
19 namespace mozilla {
20
21 using namespace dom;
22
23 // static
MaybeCreate(EditorBase & aEditorBase,nsGenericDOMDataNode & aCharData,uint32_t aOffset,uint32_t aLengthToDelete)24 already_AddRefed<DeleteTextTransaction> DeleteTextTransaction::MaybeCreate(
25 EditorBase& aEditorBase, nsGenericDOMDataNode& aCharData, uint32_t aOffset,
26 uint32_t aLengthToDelete) {
27 RefPtr<DeleteTextTransaction> transaction = new DeleteTextTransaction(
28 aEditorBase, aCharData, aOffset, aLengthToDelete);
29 return transaction.forget();
30 }
31
32 // static
33 already_AddRefed<DeleteTextTransaction>
MaybeCreateForPreviousCharacter(EditorBase & aEditorBase,nsGenericDOMDataNode & aCharData,uint32_t aOffset)34 DeleteTextTransaction::MaybeCreateForPreviousCharacter(
35 EditorBase& aEditorBase, nsGenericDOMDataNode& aCharData,
36 uint32_t aOffset) {
37 if (NS_WARN_IF(!aOffset)) {
38 return nullptr;
39 }
40
41 nsAutoString data;
42 aCharData.GetData(data);
43 if (NS_WARN_IF(data.IsEmpty())) {
44 return nullptr;
45 }
46
47 uint32_t length = 1;
48 uint32_t offset = aOffset - 1;
49 if (offset && NS_IS_LOW_SURROGATE(data[offset]) &&
50 NS_IS_HIGH_SURROGATE(data[offset - 1])) {
51 ++length;
52 --offset;
53 }
54 return DeleteTextTransaction::MaybeCreate(aEditorBase, aCharData, offset,
55 length);
56 }
57
58 // static
59 already_AddRefed<DeleteTextTransaction>
MaybeCreateForNextCharacter(EditorBase & aEditorBase,nsGenericDOMDataNode & aCharData,uint32_t aOffset)60 DeleteTextTransaction::MaybeCreateForNextCharacter(
61 EditorBase& aEditorBase, nsGenericDOMDataNode& aCharData,
62 uint32_t aOffset) {
63 nsAutoString data;
64 aCharData.GetData(data);
65 if (NS_WARN_IF(aOffset >= data.Length()) || NS_WARN_IF(data.IsEmpty())) {
66 return nullptr;
67 }
68
69 uint32_t length = 1;
70 if (aOffset + 1 < data.Length() && NS_IS_HIGH_SURROGATE(data[aOffset]) &&
71 NS_IS_LOW_SURROGATE(data[aOffset + 1])) {
72 ++length;
73 }
74 return DeleteTextTransaction::MaybeCreate(aEditorBase, aCharData, aOffset,
75 length);
76 }
77
DeleteTextTransaction(EditorBase & aEditorBase,nsGenericDOMDataNode & aCharData,uint32_t aOffset,uint32_t aLengthToDelete)78 DeleteTextTransaction::DeleteTextTransaction(EditorBase& aEditorBase,
79 nsGenericDOMDataNode& aCharData,
80 uint32_t aOffset,
81 uint32_t aLengthToDelete)
82 : mEditorBase(&aEditorBase),
83 mCharData(&aCharData),
84 mOffset(aOffset),
85 mLengthToDelete(aLengthToDelete) {
86 NS_ASSERTION(mCharData->Length() >= aOffset + aLengthToDelete,
87 "Trying to delete more characters than in node");
88 }
89
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction,EditTransactionBase,mEditorBase,mCharData)90 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction, EditTransactionBase,
91 mEditorBase, mCharData)
92
93 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction)
94 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
95
96 bool DeleteTextTransaction::CanDoIt() const {
97 if (NS_WARN_IF(!mCharData) || NS_WARN_IF(!mEditorBase)) {
98 return false;
99 }
100 return mEditorBase->IsModifiableNode(mCharData);
101 }
102
103 NS_IMETHODIMP
DoTransaction()104 DeleteTextTransaction::DoTransaction() {
105 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mCharData)) {
106 return NS_ERROR_NOT_AVAILABLE;
107 }
108
109 // Get the text that we're about to delete
110 nsresult rv =
111 mCharData->SubstringData(mOffset, mLengthToDelete, mDeletedText);
112 MOZ_ASSERT(NS_SUCCEEDED(rv));
113 rv = mCharData->DeleteData(mOffset, mLengthToDelete);
114 if (NS_WARN_IF(NS_FAILED(rv))) {
115 return rv;
116 }
117
118 mEditorBase->RangeUpdaterRef().SelAdjDeleteText(mCharData, mOffset,
119 mLengthToDelete);
120
121 // Only set selection to deletion point if editor gives permission
122 if (mEditorBase->GetShouldTxnSetSelection()) {
123 RefPtr<Selection> selection = mEditorBase->GetSelection();
124 if (NS_WARN_IF(!selection)) {
125 return NS_ERROR_FAILURE;
126 }
127 ErrorResult error;
128 selection->Collapse(EditorRawDOMPoint(mCharData, mOffset), error);
129 if (NS_WARN_IF(error.Failed())) {
130 return error.StealNSResult();
131 }
132 }
133 // Else do nothing - DOM Range gravity will adjust selection
134 return NS_OK;
135 }
136
137 // XXX: We may want to store the selection state and restore it properly. Was
138 // it an insertion point or an extended selection?
139 NS_IMETHODIMP
UndoTransaction()140 DeleteTextTransaction::UndoTransaction() {
141 if (NS_WARN_IF(!mCharData)) {
142 return NS_ERROR_NOT_INITIALIZED;
143 }
144 return mCharData->InsertData(mOffset, mDeletedText);
145 }
146
147 } // namespace mozilla
148