Backed out changeset 4191b252db9b (bug 1886734) for causing build bustages @netwerk...
[gecko.git] / editor / libeditor / SplitNodeTransaction.cpp
blob01c61bd4d4b0d1745806114949c444db2a3c4ca2
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 "SplitNodeTransaction.h"
8 #include "EditorDOMPoint.h" // for EditorRawDOMPoint
9 #include "HTMLEditHelpers.h" // for SplitNodeResult
10 #include "HTMLEditor.h" // for HTMLEditor
11 #include "HTMLEditorInlines.h"
12 #include "HTMLEditUtils.h"
13 #include "SelectionState.h" // for AutoTrackDOMPoint and RangeUpdater
15 #include "mozilla/Logging.h"
16 #include "mozilla/Maybe.h"
17 #include "mozilla/ToString.h"
18 #include "nsAString.h"
19 #include "nsDebug.h" // for NS_ASSERTION, etc.
20 #include "nsError.h" // for NS_ERROR_NOT_INITIALIZED, etc.
21 #include "nsIContent.h" // for nsIContent
23 namespace mozilla {
25 using namespace dom;
27 template already_AddRefed<SplitNodeTransaction> SplitNodeTransaction::Create(
28 HTMLEditor& aHTMLEditor, const EditorDOMPoint& aStartOfRightContent);
29 template already_AddRefed<SplitNodeTransaction> SplitNodeTransaction::Create(
30 HTMLEditor& aHTMLEditor, const EditorRawDOMPoint& aStartOfRightContent);
32 // static
33 template <typename PT, typename CT>
34 already_AddRefed<SplitNodeTransaction> SplitNodeTransaction::Create(
35 HTMLEditor& aHTMLEditor,
36 const EditorDOMPointBase<PT, CT>& aStartOfRightContent) {
37 RefPtr<SplitNodeTransaction> transaction =
38 new SplitNodeTransaction(aHTMLEditor, aStartOfRightContent);
39 return transaction.forget();
42 template <typename PT, typename CT>
43 SplitNodeTransaction::SplitNodeTransaction(
44 HTMLEditor& aHTMLEditor,
45 const EditorDOMPointBase<PT, CT>& aStartOfRightContent)
46 : mHTMLEditor(&aHTMLEditor),
47 mSplitContent(aStartOfRightContent.template GetContainerAs<nsIContent>()),
48 mSplitOffset(aStartOfRightContent.Offset()) {
49 // printf("SplitNodeTransaction size: %zu\n", sizeof(SplitNodeTransaction));
50 static_assert(sizeof(SplitNodeTransaction) <= 64,
51 "Transaction classes may be created a lot and may be alive "
52 "long so that keep the foot print smaller as far as possible");
53 MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.IsInContentNode());
54 MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsSplittableNode(
55 *aStartOfRightContent.template ContainerAs<nsIContent>()));
58 std::ostream& operator<<(std::ostream& aStream,
59 const SplitNodeTransaction& aTransaction) {
60 aStream << "{ mParentNode=" << aTransaction.mParentNode.get();
61 if (aTransaction.mParentNode) {
62 aStream << " (" << *aTransaction.mParentNode << ")";
64 aStream << ", mNewContent=" << aTransaction.mNewContent.get();
65 if (aTransaction.mNewContent) {
66 aStream << " (" << *aTransaction.mNewContent << ")";
68 aStream << ", mSplitContent=" << aTransaction.mSplitContent.get();
69 if (aTransaction.mSplitContent) {
70 aStream << " (" << *aTransaction.mSplitContent << ")";
72 aStream << ", mSplitOffset=" << aTransaction.mSplitOffset
73 << ", mHTMLEditor=" << aTransaction.mHTMLEditor.get() << " }";
74 return aStream;
77 NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase,
78 mHTMLEditor, mParentNode, mSplitContent,
79 mNewContent)
81 NS_IMPL_ADDREF_INHERITED(SplitNodeTransaction, EditTransactionBase)
82 NS_IMPL_RELEASE_INHERITED(SplitNodeTransaction, EditTransactionBase)
83 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitNodeTransaction)
84 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
86 NS_IMETHODIMP SplitNodeTransaction::DoTransaction() {
87 MOZ_LOG(GetLogModule(), LogLevel::Info,
88 ("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
89 ToString(*this).c_str()));
91 if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mSplitContent))) {
92 return NS_ERROR_NOT_AVAILABLE;
94 MOZ_ASSERT(mSplitOffset <= mSplitContent->Length());
96 // Create a new node
97 IgnoredErrorResult error;
98 // Don't use .downcast directly because AsContent has an assertion we want
99 nsCOMPtr<nsINode> newNode = mSplitContent->CloneNode(false, error);
100 if (MOZ_UNLIKELY(error.Failed())) {
101 NS_WARNING("nsINode::CloneNode() failed");
102 return error.StealNSResult();
104 if (MOZ_UNLIKELY(NS_WARN_IF(!newNode))) {
105 return NS_ERROR_UNEXPECTED;
108 mNewContent = newNode->AsContent();
109 mParentNode = mSplitContent->GetParentNode();
110 if (!mParentNode) {
111 NS_WARNING("The splitting content was an orphan node");
112 return NS_ERROR_NOT_AVAILABLE;
115 const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
116 const OwningNonNull<nsIContent> splittingContent = *mSplitContent;
117 // MOZ_KnownLive(*mNewContent): it's grabbed by newNode
118 Result<SplitNodeResult, nsresult> splitNodeResult = DoTransactionInternal(
119 htmlEditor, splittingContent, MOZ_KnownLive(*mNewContent), mSplitOffset);
120 if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
121 NS_WARNING("SplitNodeTransaction::DoTransactionInternal() failed");
122 return EditorBase::ToGenericNSResult(splitNodeResult.unwrapErr());
124 // The user should handle selection rather here.
125 splitNodeResult.inspect().IgnoreCaretPointSuggestion();
126 return NS_OK;
129 Result<SplitNodeResult, nsresult> SplitNodeTransaction::DoTransactionInternal(
130 HTMLEditor& aHTMLEditor, nsIContent& aSplittingContent,
131 nsIContent& aNewContent, uint32_t aSplitOffset) {
132 if (Element* const splittingElement = Element::FromNode(aSplittingContent)) {
133 // MOZ_KnownLive(*splittingElement): aSplittingContent should be grabbed by
134 // the callers.
135 nsresult rv =
136 aHTMLEditor.MarkElementDirty(MOZ_KnownLive(*splittingElement));
137 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
138 return Err(NS_ERROR_EDITOR_DESTROYED);
140 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
141 "EditorBase::MarkElementDirty() failed, but ignored");
144 Result<SplitNodeResult, nsresult> splitNodeResult = aHTMLEditor.DoSplitNode(
145 EditorDOMPoint(&aSplittingContent,
146 std::min(aSplitOffset, aSplittingContent.Length())),
147 aNewContent);
148 if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
149 NS_WARNING("HTMLEditor::DoSplitNode() failed");
150 return splitNodeResult;
152 // When adding caret suggestion to SplitNodeResult, here didn't change
153 // selection so that just ignore it.
154 splitNodeResult.inspect().IgnoreCaretPointSuggestion();
155 return splitNodeResult;
158 NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() {
159 MOZ_LOG(GetLogModule(), LogLevel::Info,
160 ("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
161 ToString(*this).c_str()));
163 if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mNewContent) ||
164 NS_WARN_IF(!mParentNode) || NS_WARN_IF(!mSplitContent) ||
165 NS_WARN_IF(mNewContent->IsBeingRemoved()))) {
166 return NS_ERROR_NOT_AVAILABLE;
169 // This assumes Do inserted the new node in front of the prior existing node
170 const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
171 const OwningNonNull<nsIContent> keepingContent = *mSplitContent;
172 const OwningNonNull<nsIContent> removingContent = *mNewContent;
173 EditorDOMPoint joinedPoint;
174 // Unfortunately, we cannot track joining point if moving right node content
175 // into left node since it cannot track changes from web apps and HTMLEditor
176 // never removes the content of the left node. So it should be true that
177 // we don't need to track the point in this case.
178 nsresult rv = htmlEditor->DoJoinNodes(keepingContent, removingContent);
179 if (NS_SUCCEEDED(rv)) {
180 // Adjust split offset for redo here
181 if (joinedPoint.IsSet()) {
182 mSplitOffset = joinedPoint.Offset();
184 } else {
185 NS_WARNING("HTMLEditor::DoJoinNodes() failed");
187 return rv;
190 /* Redo cannot simply resplit the right node, because subsequent transactions
191 * on the redo stack may depend on the left node existing in its previous
192 * state.
194 NS_IMETHODIMP SplitNodeTransaction::RedoTransaction() {
195 MOZ_LOG(GetLogModule(), LogLevel::Info,
196 ("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
197 ToString(*this).c_str()));
199 if (MOZ_UNLIKELY(NS_WARN_IF(!mNewContent) || NS_WARN_IF(!mParentNode) ||
200 NS_WARN_IF(!mSplitContent) || NS_WARN_IF(!mHTMLEditor))) {
201 return NS_ERROR_NOT_AVAILABLE;
204 const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
205 const OwningNonNull<nsIContent> newContent = *mNewContent;
206 const OwningNonNull<nsIContent> splittingContent = *mSplitContent;
207 Result<SplitNodeResult, nsresult> splitNodeResult = DoTransactionInternal(
208 htmlEditor, splittingContent, newContent, mSplitOffset);
209 if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
210 NS_WARNING("SplitNodeTransaction::DoTransactionInternal() failed");
211 return EditorBase::ToGenericNSResult(splitNodeResult.unwrapErr());
213 // When adding caret suggestion to SplitNodeResult, here didn't change
214 // selection so that just ignore it.
215 splitNodeResult.inspect().IgnoreCaretPointSuggestion();
216 return NS_OK;
219 } // namespace mozilla