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