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 "JoinNodesTransaction.h"
8 #include "EditorDOMPoint.h" // for EditorDOMPoint, etc.
9 #include "HTMLEditHelpers.h" // for SplitNodeResult
10 #include "HTMLEditor.h" // for HTMLEditor
11 #include "HTMLEditorInlines.h"
12 #include "HTMLEditUtils.h"
14 #include "mozilla/Logging.h"
15 #include "mozilla/ToString.h"
16 #include "mozilla/dom/Text.h"
18 #include "nsAString.h"
19 #include "nsDebug.h" // for NS_ASSERTION, etc.
20 #include "nsError.h" // for NS_ERROR_NULL_POINTER, etc.
21 #include "nsIContent.h" // for nsIContent
22 #include "nsISupportsImpl.h" // for QueryInterface, etc.
29 already_AddRefed
<JoinNodesTransaction
> JoinNodesTransaction::MaybeCreate(
30 HTMLEditor
& aHTMLEditor
, nsIContent
& aLeftContent
,
31 nsIContent
& aRightContent
) {
32 RefPtr
<JoinNodesTransaction
> transaction
=
33 new JoinNodesTransaction(aHTMLEditor
, aLeftContent
, aRightContent
);
34 if (NS_WARN_IF(!transaction
->CanDoIt())) {
37 return transaction
.forget();
40 JoinNodesTransaction::JoinNodesTransaction(HTMLEditor
& aHTMLEditor
,
41 nsIContent
& aLeftContent
,
42 nsIContent
& aRightContent
)
43 : mHTMLEditor(&aHTMLEditor
),
44 mRemovedContent(&aRightContent
),
45 mKeepingContent(&aLeftContent
) {
46 // printf("JoinNodesTransaction size: %zu\n", sizeof(JoinNodesTransaction));
47 static_assert(sizeof(JoinNodesTransaction
) <= 64,
48 "Transaction classes may be created a lot and may be alive "
49 "long so that keep the foot print smaller as far as possible");
52 std::ostream
& operator<<(std::ostream
& aStream
,
53 const JoinNodesTransaction
& aTransaction
) {
54 aStream
<< "{ mParentNode=" << aTransaction
.mParentNode
.get();
55 if (aTransaction
.mParentNode
) {
56 aStream
<< " (" << *aTransaction
.mParentNode
<< ")";
58 aStream
<< ", mRemovedContent=" << aTransaction
.mRemovedContent
.get();
59 if (aTransaction
.mRemovedContent
) {
60 aStream
<< " (" << *aTransaction
.mRemovedContent
<< ")";
62 aStream
<< ", mKeepingContent=" << aTransaction
.mKeepingContent
.get();
63 if (aTransaction
.mKeepingContent
) {
64 aStream
<< " (" << *aTransaction
.mKeepingContent
<< ")";
66 aStream
<< ", mJoinedOffset=" << aTransaction
.mJoinedOffset
67 << ", mHTMLEditor=" << aTransaction
.mHTMLEditor
.get() << " }";
71 NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodesTransaction
, EditTransactionBase
,
72 mHTMLEditor
, mParentNode
, mRemovedContent
,
75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JoinNodesTransaction
)
76 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase
)
78 bool JoinNodesTransaction::CanDoIt() const {
79 if (NS_WARN_IF(!mKeepingContent
) || NS_WARN_IF(!mRemovedContent
) ||
80 NS_WARN_IF(!mHTMLEditor
) ||
81 NS_WARN_IF(mRemovedContent
->IsBeingRemoved()) ||
82 !mKeepingContent
->IsInComposedDoc()) {
85 return HTMLEditUtils::IsRemovableFromParentNode(*mRemovedContent
);
88 // After DoTransaction() and RedoTransaction(), the left node is removed from
89 // the content tree and right node remains.
90 NS_IMETHODIMP
JoinNodesTransaction::DoTransaction() {
91 MOZ_LOG(GetLogModule(), LogLevel::Info
,
92 ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__
,
93 ToString(*this).c_str()));
95 return DoTransactionInternal(RedoingTransaction::No
);
98 nsresult
JoinNodesTransaction::DoTransactionInternal(
99 RedoingTransaction aRedoingTransaction
) {
100 if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor
) || NS_WARN_IF(!mKeepingContent
) ||
101 NS_WARN_IF(!mRemovedContent
) ||
102 NS_WARN_IF(mRemovedContent
->IsBeingRemoved()))) {
103 return NS_ERROR_NOT_AVAILABLE
;
106 nsINode
* removingContentParentNode
= mRemovedContent
->GetParentNode();
107 if (MOZ_UNLIKELY(NS_WARN_IF(!removingContentParentNode
))) {
108 return NS_ERROR_NOT_AVAILABLE
;
111 // Verify that the joining content nodes have the same parent
112 if (MOZ_UNLIKELY(removingContentParentNode
!=
113 mKeepingContent
->GetParentNode())) {
114 NS_ASSERTION(false, "Nodes do not have same parent");
115 return NS_ERROR_NOT_AVAILABLE
;
118 // Set this instance's mParentNode. Other methods will see a non-null
119 // mParentNode and know all is well
120 mParentNode
= removingContentParentNode
;
121 // For now, setting mJoinedOffset to removed content length so that
122 // CreateJoinedPoint returns a point in mKeepingContent whose offset is
123 // the result if all content in mRemovedContent are moved to start or end of
124 // mKeepingContent without any intervention. The offset will be adjusted
126 mJoinedOffset
= mKeepingContent
->Length();
128 const OwningNonNull
<HTMLEditor
> htmlEditor
= *mHTMLEditor
;
129 const OwningNonNull
<nsIContent
> removingContent
= *mRemovedContent
;
130 const OwningNonNull
<nsIContent
> keepingContent
= *mKeepingContent
;
132 // Let's try to get actual joined point with the tacker.
133 auto joinNodesPoint
= EditorDOMPoint::AtEndOf(keepingContent
);
135 AutoTrackDOMPoint
trackJoinNodePoint(htmlEditor
->RangeUpdaterRef(),
137 rv
= htmlEditor
->DoJoinNodes(keepingContent
, removingContent
);
138 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "HTMLEditor::DoJoinNodes() failed");
140 // Adjust join node offset to the actual offset where the original first
141 // content of the right node is.
142 mJoinedOffset
= joinNodesPoint
.Offset();
144 if (aRedoingTransaction
== RedoingTransaction::No
) {
145 htmlEditor
->DidJoinNodesTransaction(*this, rv
);
151 // XXX: What if instead of split, we just deleted the unneeded children of
152 // mRight and re-inserted mLeft?
153 NS_IMETHODIMP
JoinNodesTransaction::UndoTransaction() {
154 MOZ_LOG(GetLogModule(), LogLevel::Info
,
155 ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__
,
156 ToString(*this).c_str()));
158 if (NS_WARN_IF(!mParentNode
) || NS_WARN_IF(!mKeepingContent
) ||
159 NS_WARN_IF(!mRemovedContent
) || NS_WARN_IF(!mHTMLEditor
)) {
160 return NS_ERROR_NOT_AVAILABLE
;
163 const OwningNonNull
<HTMLEditor
> htmlEditor
= *mHTMLEditor
;
164 const OwningNonNull
<nsIContent
> removedContent
= *mRemovedContent
;
166 Result
<SplitNodeResult
, nsresult
> splitNodeResult
= htmlEditor
->DoSplitNode(
167 CreateJoinedPoint
<EditorDOMPoint
>(), removedContent
);
168 if (MOZ_UNLIKELY(splitNodeResult
.isErr())) {
169 NS_WARNING("HTMLEditor::DoSplitNode() failed");
170 return splitNodeResult
.unwrapErr();
172 // When adding caret suggestion to SplitNodeResult, here didn't change
173 // selection so that just ignore it.
174 splitNodeResult
.inspect().IgnoreCaretPointSuggestion();
178 NS_IMETHODIMP
JoinNodesTransaction::RedoTransaction() {
179 MOZ_LOG(GetLogModule(), LogLevel::Info
,
180 ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__
,
181 ToString(*this).c_str()));
182 return DoTransactionInternal(RedoingTransaction::Yes
);
185 } // namespace mozilla