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
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
);
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()) {
62 aTransaction
.mContentToInsert
->AsText()->GetData(data
);
63 aStream
<< " (#text \"" << NS_ConvertUTF16toUTF8(data
).get() << "\")";
65 aStream
<< " (" << *aTransaction
.mContentToInsert
<< ")";
68 aStream
<< ", mPointToInsert=" << aTransaction
.mPointToInsert
69 << ", mEditorBase=" << aTransaction
.mEditorBase
.get() << " }";
73 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction
, EditTransactionBase
,
74 mEditorBase
, mContentToInsert
,
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
;
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();
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
;
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");
172 if (!mEditorBase
->AllowsTransactionsToChangeSelection()) {
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");
184 } // namespace mozilla