Bug 1890689 apply drift correction to input rate instead of output rate r=pehrsons
[gecko.git] / editor / libeditor / InsertNodeTransaction.cpp
blob50e6b32653fa2e60ce767dfc045089ca33b85919
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/. */
6 #include "InsertNodeTransaction.h"
8 #include "EditorBase.h" // for EditorBase
9 #include "EditorDOMPoint.h" // for EditorDOMPoint
10 #include "HTMLEditor.h" // for HTMLEditor
11 #include "TextEditor.h" // for TextEditor
13 #include "mozilla/Logging.h"
14 #include "mozilla/ToString.h"
16 #include "nsAString.h"
17 #include "nsDebug.h" // for NS_WARNING, etc.
18 #include "nsError.h" // for NS_ERROR_NULL_POINTER, etc.
19 #include "nsIContent.h" // for nsIContent
20 #include "nsReadableUtils.h" // for ToNewCString
21 #include "nsString.h" // for nsString
23 namespace mozilla {
25 using namespace dom;
27 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
28 EditorBase& aEditorBase, nsIContent& aContentToInsert,
29 const EditorDOMPoint& aPointToInsert);
30 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
31 EditorBase& aEditorBase, nsIContent& aContentToInsert,
32 const EditorRawDOMPoint& aPointToInsert);
34 // static
35 template <typename PT, typename CT>
36 already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
37 EditorBase& aEditorBase, nsIContent& aContentToInsert,
38 const EditorDOMPointBase<PT, CT>& aPointToInsert) {
39 RefPtr<InsertNodeTransaction> transaction =
40 new InsertNodeTransaction(aEditorBase, aContentToInsert, aPointToInsert);
41 return transaction.forget();
44 template <typename PT, typename CT>
45 InsertNodeTransaction::InsertNodeTransaction(
46 EditorBase& aEditorBase, nsIContent& aContentToInsert,
47 const EditorDOMPointBase<PT, CT>& aPointToInsert)
48 : mContentToInsert(&aContentToInsert),
49 mPointToInsert(aPointToInsert.template To<EditorDOMPoint>()),
50 mEditorBase(&aEditorBase) {
51 MOZ_ASSERT(mPointToInsert.IsSetAndValid());
52 // Ensure mPointToInsert stores child at offset.
53 Unused << mPointToInsert.GetChild();
56 std::ostream& operator<<(std::ostream& aStream,
57 const InsertNodeTransaction& aTransaction) {
58 aStream << "{ mContentToInsert=" << aTransaction.mContentToInsert.get();
59 if (aTransaction.mContentToInsert) {
60 if (aTransaction.mContentToInsert->IsText()) {
61 nsAutoString data;
62 aTransaction.mContentToInsert->AsText()->GetData(data);
63 aStream << " (#text \"" << NS_ConvertUTF16toUTF8(data).get() << "\")";
64 } else {
65 aStream << " (" << *aTransaction.mContentToInsert << ")";
68 aStream << ", mPointToInsert=" << aTransaction.mPointToInsert
69 << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
70 return aStream;
73 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase,
74 mEditorBase, mContentToInsert,
75 mPointToInsert)
77 NS_IMPL_ADDREF_INHERITED(InsertNodeTransaction, EditTransactionBase)
78 NS_IMPL_RELEASE_INHERITED(InsertNodeTransaction, EditTransactionBase)
79 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTransaction)
80 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
82 NS_IMETHODIMP InsertNodeTransaction::DoTransaction() {
83 MOZ_LOG(GetLogModule(), LogLevel::Info,
84 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
85 ToString(*this).c_str()));
87 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) ||
88 NS_WARN_IF(!mPointToInsert.IsSet())) {
89 return NS_ERROR_NOT_AVAILABLE;
92 MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToInsert->IsText());
94 if (!mPointToInsert.IsSetAndValid()) {
95 // It seems that DOM tree has been changed after first DoTransaction()
96 // and current RedoTranaction() call.
97 if (mPointToInsert.GetChild()) {
98 EditorDOMPoint newPointToInsert(mPointToInsert.GetChild());
99 if (!newPointToInsert.IsSet()) {
100 // The insertion point has been removed from the DOM tree.
101 // In this case, we should append the node to the container instead.
102 newPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
103 if (NS_WARN_IF(!newPointToInsert.IsSet())) {
104 return NS_ERROR_FAILURE;
107 mPointToInsert = newPointToInsert;
108 } else {
109 mPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
110 if (NS_WARN_IF(!mPointToInsert.IsSet())) {
111 return NS_ERROR_FAILURE;
116 OwningNonNull<EditorBase> editorBase = *mEditorBase;
117 OwningNonNull<nsIContent> contentToInsert = *mContentToInsert;
118 OwningNonNull<nsINode> container = *mPointToInsert.GetContainer();
119 nsCOMPtr<nsIContent> refChild = mPointToInsert.GetChild();
120 if (contentToInsert->IsElement()) {
121 nsresult rv = editorBase->MarkElementDirty(
122 MOZ_KnownLive(*contentToInsert->AsElement()));
123 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
124 return EditorBase::ToGenericNSResult(rv);
126 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
127 "EditorBase::MarkElementDirty() failed, but ignored");
130 IgnoredErrorResult error;
131 container->InsertBefore(contentToInsert, refChild, error);
132 // InsertBefore() may call MightThrowJSException() even if there is no
133 // error. We don't need the flag here.
134 error.WouldReportJSException();
135 if (error.Failed()) {
136 NS_WARNING("nsINode::InsertBefore() failed");
137 return error.StealNSResult();
140 return NS_OK;
143 NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() {
144 MOZ_LOG(GetLogModule(), LogLevel::Info,
145 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
146 ToString(*this).c_str()));
148 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) ||
149 NS_WARN_IF(!mPointToInsert.IsSet())) {
150 return NS_ERROR_NOT_INITIALIZED;
152 // XXX If the inserted node has been moved to different container node or
153 // just removed from the DOM tree, this always fails.
154 OwningNonNull<nsINode> container = *mPointToInsert.GetContainer();
155 OwningNonNull<nsIContent> contentToInsert = *mContentToInsert;
156 ErrorResult error;
157 container->RemoveChild(contentToInsert, error);
158 NS_WARNING_ASSERTION(!error.Failed(), "nsINode::RemoveChild() failed");
159 return error.StealNSResult();
162 NS_IMETHODIMP InsertNodeTransaction::RedoTransaction() {
163 MOZ_LOG(GetLogModule(), LogLevel::Info,
164 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
165 ToString(*this).c_str()));
166 nsresult rv = DoTransaction();
167 if (MOZ_UNLIKELY(NS_FAILED(rv))) {
168 NS_WARNING("InsertNodeTransaction::RedoTransaction() failed");
169 return rv;
172 if (!mEditorBase->AllowsTransactionsToChangeSelection()) {
173 return NS_OK;
176 OwningNonNull<EditorBase> editorBase(*mEditorBase);
177 rv = editorBase->CollapseSelectionTo(
178 SuggestPointToPutCaret<EditorRawDOMPoint>());
179 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
180 "EditorBase::CollapseSelectionTo() failed, but ignored");
181 return NS_OK;
184 } // namespace mozilla