InsertNodeTransaction.cpp (7507B)
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 "InsertNodeTransaction.h" 7 8 #include "EditorBase.h" // for EditorBase 9 #include "EditorDOMAPIWrapper.h" // For AutoNodeAPIWrapper 10 #include "EditorDOMPoint.h" // for EditorDOMPoint 11 #include "HTMLEditor.h" // for HTMLEditor 12 #include "TextEditor.h" // for TextEditor 13 14 #include "mozilla/Logging.h" 15 #include "mozilla/ToString.h" 16 17 #include "nsAString.h" 18 #include "nsDebug.h" // for NS_WARNING, etc. 19 #include "nsError.h" // for NS_ERROR_NULL_POINTER, etc. 20 #include "nsIContent.h" // for nsIContent 21 #include "nsReadableUtils.h" // for ToNewCString 22 #include "nsString.h" // for nsString 23 24 namespace mozilla { 25 26 using namespace dom; 27 28 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create( 29 EditorBase& aEditorBase, nsIContent& aContentToInsert, 30 const EditorDOMPoint& aPointToInsert); 31 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create( 32 EditorBase& aEditorBase, nsIContent& aContentToInsert, 33 const EditorRawDOMPoint& aPointToInsert); 34 35 // static 36 template <typename PT, typename CT> 37 already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create( 38 EditorBase& aEditorBase, nsIContent& aContentToInsert, 39 const EditorDOMPointBase<PT, CT>& aPointToInsert) { 40 RefPtr<InsertNodeTransaction> transaction = 41 new InsertNodeTransaction(aEditorBase, aContentToInsert, aPointToInsert); 42 return transaction.forget(); 43 } 44 45 template <typename PT, typename CT> 46 InsertNodeTransaction::InsertNodeTransaction( 47 EditorBase& aEditorBase, nsIContent& aContentToInsert, 48 const EditorDOMPointBase<PT, CT>& aPointToInsert) 49 : mContentToInsert(&aContentToInsert), 50 mPointToInsert(aPointToInsert.template To<EditorDOMPoint>()), 51 mEditorBase(&aEditorBase) { 52 MOZ_ASSERT(mPointToInsert.IsSetAndValid()); 53 // Ensure mPointToInsert stores child at offset. 54 (void)mPointToInsert.GetChild(); 55 } 56 57 std::ostream& operator<<(std::ostream& aStream, 58 const InsertNodeTransaction& aTransaction) { 59 aStream << "{ mContentToInsert=" << aTransaction.mContentToInsert.get(); 60 if (aTransaction.mContentToInsert) { 61 if (aTransaction.mContentToInsert->IsText()) { 62 nsAutoString data; 63 aTransaction.mContentToInsert->AsText()->GetData(data); 64 aStream << " (#text \"" << NS_ConvertUTF16toUTF8(data).get() << "\")"; 65 } else { 66 aStream << " (" << *aTransaction.mContentToInsert << ")"; 67 } 68 } 69 aStream << ", mPointToInsert=" << aTransaction.mPointToInsert 70 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }"; 71 return aStream; 72 } 73 74 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase, 75 mEditorBase, mContentToInsert, 76 mPointToInsert) 77 78 NS_IMPL_ADDREF_INHERITED(InsertNodeTransaction, EditTransactionBase) 79 NS_IMPL_RELEASE_INHERITED(InsertNodeTransaction, EditTransactionBase) 80 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTransaction) 81 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) 82 83 NS_IMETHODIMP InsertNodeTransaction::DoTransaction() { 84 MOZ_LOG(GetLogModule(), LogLevel::Info, 85 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__, 86 ToString(*this).c_str())); 87 88 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) || 89 NS_WARN_IF(!mPointToInsert.IsSet())) { 90 return NS_ERROR_NOT_AVAILABLE; 91 } 92 93 MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToInsert->IsText()); 94 95 if (!mPointToInsert.IsSetAndValid()) { 96 // It seems that DOM tree has been changed after first DoTransaction() 97 // and current RedoTranaction() call. 98 if (mPointToInsert.GetChild()) { 99 EditorDOMPoint newPointToInsert(mPointToInsert.GetChild()); 100 if (!newPointToInsert.IsSet()) { 101 // The insertion point has been removed from the DOM tree. 102 // In this case, we should append the node to the container instead. 103 newPointToInsert.SetToEndOf(mPointToInsert.GetContainer()); 104 if (NS_WARN_IF(!newPointToInsert.IsSet())) { 105 return NS_ERROR_FAILURE; 106 } 107 } 108 mPointToInsert = newPointToInsert; 109 } else { 110 mPointToInsert.SetToEndOf(mPointToInsert.GetContainer()); 111 if (NS_WARN_IF(!mPointToInsert.IsSet())) { 112 return NS_ERROR_FAILURE; 113 } 114 } 115 } 116 117 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 118 const OwningNonNull<nsIContent> contentToInsert = *mContentToInsert; 119 const OwningNonNull<nsINode> container = *mPointToInsert.GetContainer(); 120 const nsCOMPtr<nsIContent> refChild = mPointToInsert.GetChild(); 121 if (contentToInsert->IsElement()) { 122 nsresult rv = editorBase->MarkElementDirty( 123 MOZ_KnownLive(*contentToInsert->AsElement())); 124 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 125 return EditorBase::ToGenericNSResult(rv); 126 } 127 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 128 "EditorBase::MarkElementDirty() failed, but ignored"); 129 } 130 131 AutoNodeAPIWrapper nodeWrapper(editorBase, container); 132 nsresult rv = nodeWrapper.InsertBefore(contentToInsert, refChild); 133 if (NS_FAILED(rv)) { 134 NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed"); 135 return rv; 136 } 137 NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(), 138 "Inserting a node caused other mutations, but ignored"); 139 return NS_OK; 140 } 141 142 NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() { 143 MOZ_LOG(GetLogModule(), LogLevel::Info, 144 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__, 145 ToString(*this).c_str())); 146 147 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) || 148 NS_WARN_IF(!mPointToInsert.IsSet())) { 149 return NS_ERROR_NOT_INITIALIZED; 150 } 151 // XXX If the inserted node has been moved to different container node or 152 // just removed from the DOM tree, this always fails. 153 const OwningNonNull<EditorBase> editorBase = *mEditorBase; 154 const OwningNonNull<nsINode> container = *mPointToInsert.GetContainer(); 155 const OwningNonNull<nsIContent> contentToInsert = *mContentToInsert; 156 AutoNodeAPIWrapper nodeWrapper(editorBase, container); 157 nsresult rv = nodeWrapper.RemoveChild(contentToInsert); 158 if (NS_FAILED(rv)) { 159 NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed"); 160 return rv; 161 } 162 NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(), 163 "Removing a node caused other mutations, but ignored"); 164 return NS_OK; 165 } 166 167 NS_IMETHODIMP InsertNodeTransaction::RedoTransaction() { 168 MOZ_LOG(GetLogModule(), LogLevel::Info, 169 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__, 170 ToString(*this).c_str())); 171 nsresult rv = DoTransaction(); 172 if (MOZ_UNLIKELY(NS_FAILED(rv))) { 173 NS_WARNING("InsertNodeTransaction::RedoTransaction() failed"); 174 return rv; 175 } 176 177 if (!mEditorBase->AllowsTransactionsToChangeSelection()) { 178 return NS_OK; 179 } 180 181 OwningNonNull<EditorBase> editorBase(*mEditorBase); 182 rv = editorBase->CollapseSelectionTo( 183 SuggestPointToPutCaret<EditorRawDOMPoint>()); 184 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 185 "EditorBase::CollapseSelectionTo() failed, but ignored"); 186 return NS_OK; 187 } 188 189 } // namespace mozilla