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 "nsMemory.h" // for nsMemory
21 #include "nsReadableUtils.h" // for ToNewCString
22 #include "nsString.h" // for nsString
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
);
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();
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 Unused
<< mPointToInsert
.GetChild();
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()) {
63 aTransaction
.mContentToInsert
->AsText()->GetData(data
);
64 aStream
<< " (#text \"" << NS_ConvertUTF16toUTF8(data
).get() << "\")";
66 aStream
<< " (" << *aTransaction
.mContentToInsert
<< ")";
69 aStream
<< ", mPointToInsert=" << aTransaction
.mPointToInsert
70 << ", mEditorBase=" << aTransaction
.mEditorBase
.get() << " }";
74 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction
, EditTransactionBase
,
75 mEditorBase
, mContentToInsert
,
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
)
83 NS_IMETHODIMP
InsertNodeTransaction::DoTransaction() {
84 MOZ_LOG(GetLogModule(), LogLevel::Info
,
85 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__
,
86 ToString(*this).c_str()));
88 if (NS_WARN_IF(!mEditorBase
) || NS_WARN_IF(!mContentToInsert
) ||
89 NS_WARN_IF(!mPointToInsert
.IsSet())) {
90 return NS_ERROR_NOT_AVAILABLE
;
93 MOZ_ASSERT_IF(mEditorBase
->IsTextEditor(), !mContentToInsert
->IsText());
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
;
108 mPointToInsert
= newPointToInsert
;
110 mPointToInsert
.SetToEndOf(mPointToInsert
.GetContainer());
111 if (NS_WARN_IF(!mPointToInsert
.IsSet())) {
112 return NS_ERROR_FAILURE
;
117 OwningNonNull
<EditorBase
> editorBase
= *mEditorBase
;
118 OwningNonNull
<nsIContent
> contentToInsert
= *mContentToInsert
;
119 OwningNonNull
<nsINode
> container
= *mPointToInsert
.GetContainer();
120 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
);
127 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
128 "EditorBase::MarkElementDirty() failed, but ignored");
131 IgnoredErrorResult error
;
132 container
->InsertBefore(contentToInsert
, refChild
, error
);
133 // InsertBefore() may call MightThrowJSException() even if there is no
134 // error. We don't need the flag here.
135 error
.WouldReportJSException();
136 if (error
.Failed()) {
137 NS_WARNING("nsINode::InsertBefore() failed");
138 return error
.StealNSResult();
144 NS_IMETHODIMP
InsertNodeTransaction::UndoTransaction() {
145 MOZ_LOG(GetLogModule(), LogLevel::Info
,
146 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__
,
147 ToString(*this).c_str()));
149 if (NS_WARN_IF(!mEditorBase
) || NS_WARN_IF(!mContentToInsert
) ||
150 NS_WARN_IF(!mPointToInsert
.IsSet())) {
151 return NS_ERROR_NOT_INITIALIZED
;
153 // XXX If the inserted node has been moved to different container node or
154 // just removed from the DOM tree, this always fails.
155 OwningNonNull
<nsINode
> container
= *mPointToInsert
.GetContainer();
156 OwningNonNull
<nsIContent
> contentToInsert
= *mContentToInsert
;
158 container
->RemoveChild(contentToInsert
, error
);
159 NS_WARNING_ASSERTION(!error
.Failed(), "nsINode::RemoveChild() failed");
160 return error
.StealNSResult();
163 NS_IMETHODIMP
InsertNodeTransaction::RedoTransaction() {
164 MOZ_LOG(GetLogModule(), LogLevel::Info
,
165 ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__
,
166 ToString(*this).c_str()));
167 nsresult rv
= DoTransaction();
168 if (MOZ_UNLIKELY(NS_FAILED(rv
))) {
169 NS_WARNING("InsertNodeTransaction::RedoTransaction() failed");
173 if (!mEditorBase
->AllowsTransactionsToChangeSelection()) {
177 OwningNonNull
<EditorBase
> editorBase(*mEditorBase
);
178 rv
= editorBase
->CollapseSelectionTo(
179 SuggestPointToPutCaret
<EditorRawDOMPoint
>());
180 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
181 "EditorBase::CollapseSelectionTo() failed, but ignored");
185 } // namespace mozilla