Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / editor / libeditor / JoinNodesTransaction.cpp
blob094a41a5c90c8b4de7492787157d88574da65819
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.
24 namespace mozilla {
26 using namespace dom;
28 // static
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())) {
35 return nullptr;
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() << " }";
68 return aStream;
71 NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodesTransaction, EditTransactionBase,
72 mHTMLEditor, mParentNode, mRemovedContent,
73 mKeepingContent)
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()) {
83 return false;
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
125 // below.
126 mJoinedOffset = mKeepingContent->Length();
128 const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
129 const OwningNonNull<nsIContent> removingContent = *mRemovedContent;
130 const OwningNonNull<nsIContent> keepingContent = *mKeepingContent;
131 nsresult rv;
132 // Let's try to get actual joined point with the tacker.
133 auto joinNodesPoint = EditorDOMPoint::AtEndOf(keepingContent);
135 AutoTrackDOMPoint trackJoinNodePoint(htmlEditor->RangeUpdaterRef(),
136 &joinNodesPoint);
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);
148 return 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();
175 return NS_OK;
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