DeleteNodeTransaction.cpp (6789B)
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 "DeleteNodeTransaction.h" 7 8 #include "EditorBase.h" 9 #include "EditorDOMAPIWrapper.h" 10 #include "EditorDOMPoint.h" 11 #include "HTMLEditUtils.h" 12 #include "SelectionState.h" // RangeUpdater 13 #include "TextEditor.h" 14 15 #include "mozilla/Logging.h" 16 #include "mozilla/ToString.h" 17 18 #include "nsDebug.h" 19 #include "nsError.h" 20 #include "nsAString.h" 21 22 namespace mozilla { 23 24 // static 25 already_AddRefed<DeleteNodeTransaction> DeleteNodeTransaction::MaybeCreate( 26 EditorBase& aEditorBase, nsIContent& aContentToDelete) { 27 RefPtr<DeleteNodeTransaction> transaction = 28 new DeleteNodeTransaction(aEditorBase, aContentToDelete); 29 if (NS_WARN_IF(!transaction->CanDoIt())) { 30 return nullptr; 31 } 32 return transaction.forget(); 33 } 34 35 DeleteNodeTransaction::DeleteNodeTransaction(EditorBase& aEditorBase, 36 nsIContent& aContentToDelete) 37 : DeleteContentTransactionBase(aEditorBase), 38 mContentToDelete(&aContentToDelete), 39 mParentNode(aContentToDelete.GetParentNode()) { 40 MOZ_DIAGNOSTIC_ASSERT_IF( 41 aEditorBase.IsHTMLEditor(), 42 HTMLEditUtils::IsRemovableNode(aContentToDelete) || 43 // It's okay to delete text node if it's added by `HTMLEditor` since 44 // remaining it may be noisy for the users. 45 (aContentToDelete.IsText() && 46 aContentToDelete.HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY))); 47 NS_ASSERTION( 48 !aEditorBase.IsHTMLEditor() || 49 HTMLEditUtils::IsRemovableNode(aContentToDelete), 50 "Deleting non-editable text node, please write a test for this!!"); 51 } 52 53 std::ostream& operator<<(std::ostream& aStream, 54 const DeleteNodeTransaction& aTransaction) { 55 aStream << "{ mContentToDelete=" << aTransaction.mContentToDelete.get(); 56 if (aTransaction.mContentToDelete) { 57 aStream << " (" << *aTransaction.mContentToDelete << ")"; 58 } 59 aStream << ", mParentNode=" << aTransaction.mParentNode.get(); 60 if (aTransaction.mParentNode) { 61 aStream << " (" << *aTransaction.mParentNode << ")"; 62 } 63 aStream << ", mRefContent=" << aTransaction.mRefContent.get(); 64 if (aTransaction.mRefContent) { 65 aStream << " (" << *aTransaction.mRefContent << ")"; 66 } 67 aStream << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 68 return aStream; 69 } 70 71 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction, 72 DeleteContentTransactionBase, 73 mContentToDelete, mParentNode, mRefContent) 74 75 NS_IMPL_ADDREF_INHERITED(DeleteNodeTransaction, DeleteContentTransactionBase) 76 NS_IMPL_RELEASE_INHERITED(DeleteNodeTransaction, DeleteContentTransactionBase) 77 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteNodeTransaction) 78 NS_INTERFACE_MAP_END_INHERITING(DeleteContentTransactionBase) 79 80 bool DeleteNodeTransaction::CanDoIt() const { 81 if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) || 82 !mParentNode) { 83 return false; 84 } 85 return mEditorBase->IsTextEditor() || 86 HTMLEditUtils::IsSimplyEditableNode(*mParentNode); 87 } 88 89 NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() { 90 MOZ_LOG(GetLogModule(), LogLevel::Info, 91 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__, 92 ToString(*this).c_str())); 93 94 if (NS_WARN_IF(!CanDoIt())) { 95 return NS_OK; 96 } 97 98 MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToDelete->IsText()); 99 100 // Remember which child mContentToDelete was (by remembering which child was 101 // next). Note that mRefContent can be nullptr. 102 mRefContent = mContentToDelete->GetNextSibling(); 103 104 // give range updater a chance. SelAdjDeleteNode() needs to be called 105 // *before* we do the action, unlike some of the other RangeItem update 106 // methods. 107 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 108 editorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete); 109 110 const OwningNonNull<nsINode> parentNode = *mParentNode; 111 const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete; 112 AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode); 113 nsresult rv = nodeWrapper.RemoveChild(contentToDelete); 114 if (NS_FAILED(rv)) { 115 NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed"); 116 return rv; 117 } 118 NS_WARNING_ASSERTION( 119 nodeWrapper.IsExpectedResult(), 120 "Removing a content node caused other mutations, but ignored"); 121 return NS_OK; 122 } 123 124 EditorDOMPoint DeleteNodeTransaction::SuggestPointToPutCaret() const { 125 return EditorDOMPoint(); 126 } 127 128 NS_IMETHODIMP DeleteNodeTransaction::UndoTransaction() { 129 MOZ_LOG(GetLogModule(), LogLevel::Info, 130 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__, 131 ToString(*this).c_str())); 132 133 if (NS_WARN_IF(!CanDoIt())) { 134 // This is a legal state, the transaction is a no-op. 135 return NS_OK; 136 } 137 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 138 const OwningNonNull<nsINode> parentNode = *mParentNode; 139 const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete; 140 const nsCOMPtr<nsIContent> refContent = mRefContent; 141 // XXX Perhaps, we should check `refContent` is a child of `parentNode`, 142 // and if it's not, we should stop undoing or something. 143 AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode); 144 nsresult rv = nodeWrapper.InsertBefore(contentToDelete, refContent); 145 if (NS_FAILED(rv)) { 146 NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed"); 147 return rv; 148 } 149 NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(), 150 "Inserting a node caused other mutations, but ignored"); 151 return NS_OK; 152 } 153 154 NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() { 155 MOZ_LOG(GetLogModule(), LogLevel::Info, 156 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__, 157 ToString(*this).c_str())); 158 159 if (NS_WARN_IF(!CanDoIt())) { 160 // This is a legal state, the transaction is a no-op. 161 return NS_OK; 162 } 163 164 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 165 editorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete); 166 167 const OwningNonNull<nsINode> parentNode = *mParentNode; 168 const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete; 169 AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode); 170 nsresult rv = nodeWrapper.RemoveChild(contentToDelete); 171 if (NS_FAILED(rv)) { 172 NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed"); 173 return rv; 174 } 175 NS_WARNING_ASSERTION( 176 nodeWrapper.IsExpectedResult(), 177 "Removing a content node caused other mutations, but ignored"); 178 return NS_OK; 179 } 180 181 } // namespace mozilla