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 "mozilla/EditorDOMPoint.h" // for RangeBoundary, EditorRawDOMPoint
9 #include "mozilla/HTMLEditor.h" // for HTMLEditor
10 #include "mozilla/dom/Selection.h"
11 #include "nsAString.h"
12 #include "nsDebug.h" // for NS_ASSERTION, etc.
13 #include "nsError.h" // for NS_ERROR_NOT_INITIALIZED, etc.
14 #include "nsIContent.h" // for nsIContent
20 template already_AddRefed
<SplitNodeTransaction
> SplitNodeTransaction::Create(
21 HTMLEditor
& aHTMLEditor
, const EditorDOMPoint
& aStartOfRightContent
);
22 template already_AddRefed
<SplitNodeTransaction
> SplitNodeTransaction::Create(
23 HTMLEditor
& aHTMLEditor
, const EditorRawDOMPoint
& aStartOfRightContent
);
26 template <typename PT
, typename CT
>
27 already_AddRefed
<SplitNodeTransaction
> SplitNodeTransaction::Create(
28 HTMLEditor
& aHTMLEditor
,
29 const EditorDOMPointBase
<PT
, CT
>& aStartOfRightContent
) {
30 RefPtr
<SplitNodeTransaction
> transaction
=
31 new SplitNodeTransaction(aHTMLEditor
, aStartOfRightContent
);
32 return transaction
.forget();
35 template <typename PT
, typename CT
>
36 SplitNodeTransaction::SplitNodeTransaction(
37 HTMLEditor
& aHTMLEditor
,
38 const EditorDOMPointBase
<PT
, CT
>& aStartOfRightContent
)
39 : mHTMLEditor(&aHTMLEditor
), mStartOfRightContent(aStartOfRightContent
) {
40 MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent
.IsInContentNode());
43 NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction
, EditTransactionBase
,
44 mHTMLEditor
, mStartOfRightContent
,
45 mContainerParentNode
, mNewLeftContent
)
47 NS_IMPL_ADDREF_INHERITED(SplitNodeTransaction
, EditTransactionBase
)
48 NS_IMPL_RELEASE_INHERITED(SplitNodeTransaction
, EditTransactionBase
)
49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitNodeTransaction
)
50 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase
)
52 NS_IMETHODIMP
SplitNodeTransaction::DoTransaction() {
53 if (NS_WARN_IF(!mHTMLEditor
) ||
54 NS_WARN_IF(!mStartOfRightContent
.IsInContentNode())) {
55 return NS_ERROR_NOT_AVAILABLE
;
57 MOZ_ASSERT(mStartOfRightContent
.IsSetAndValid());
61 // Don't use .downcast directly because AsContent has an assertion we want
62 nsCOMPtr
<nsINode
> cloneOfRightContainer
=
63 mStartOfRightContent
.GetContainer()->CloneNode(false, error
);
65 NS_WARNING("nsINode::CloneNode() failed");
66 return error
.StealNSResult();
68 if (NS_WARN_IF(!cloneOfRightContainer
)) {
69 return NS_ERROR_UNEXPECTED
;
72 mNewLeftContent
= cloneOfRightContainer
->AsContent();
74 mContainerParentNode
= mStartOfRightContent
.GetContainerParent();
75 if (!mContainerParentNode
) {
76 NS_WARNING("Right container was an orphan node");
77 return NS_ERROR_NOT_AVAILABLE
;
80 OwningNonNull
<HTMLEditor
> htmlEditor
= *mHTMLEditor
;
81 OwningNonNull
<nsIContent
> newLeftContent
= *mNewLeftContent
;
82 OwningNonNull
<nsINode
> containerParentNode
= *mContainerParentNode
;
83 EditorDOMPoint
startOfRightContent(mStartOfRightContent
);
85 if (RefPtr
<Element
> startOfRightNode
=
86 startOfRightContent
.GetContainerAsElement()) {
87 nsresult rv
= htmlEditor
->MarkElementDirty(*startOfRightNode
);
88 if (NS_WARN_IF(rv
== NS_ERROR_EDITOR_DESTROYED
)) {
89 return EditorBase::ToGenericNSResult(rv
);
91 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
92 "EditorBase::MarkElementDirty() failed, but ignored");
95 // Insert the new node
96 htmlEditor
->DoSplitNode(startOfRightContent
, newLeftContent
, error
);
98 NS_WARNING("HTMLEditor::DoSplitNode() failed");
99 return error
.StealNSResult();
102 if (!htmlEditor
->AllowsTransactionsToChangeSelection()) {
106 NS_WARNING_ASSERTION(
107 !htmlEditor
->Destroyed(),
108 "The editor has gone but SplitNodeTransaction keeps trying to modify "
110 RefPtr
<Selection
> selection
= htmlEditor
->GetSelection();
111 if (NS_WARN_IF(!selection
)) {
112 return NS_ERROR_FAILURE
;
114 EditorRawDOMPoint
atEndOfLeftNode(EditorRawDOMPoint::AtEndOf(newLeftContent
));
115 selection
->CollapseInLimiter(atEndOfLeftNode
, error
);
116 NS_WARNING_ASSERTION(!error
.Failed(),
117 "Selection::CollapseInLimiter() failed");
118 return error
.StealNSResult();
121 NS_IMETHODIMP
SplitNodeTransaction::UndoTransaction() {
122 if (NS_WARN_IF(!mHTMLEditor
) || NS_WARN_IF(!mNewLeftContent
) ||
123 NS_WARN_IF(!mContainerParentNode
) ||
124 NS_WARN_IF(!mStartOfRightContent
.IsInContentNode())) {
125 return NS_ERROR_NOT_AVAILABLE
;
128 // This assumes Do inserted the new node in front of the prior existing node
129 // XXX Perhaps, we should reset mStartOfRightNode with current first child
130 // of the right node.
131 OwningNonNull
<HTMLEditor
> htmlEditor
= *mHTMLEditor
;
132 OwningNonNull
<nsIContent
> containerContent
=
133 *mStartOfRightContent
.ContainerAsContent();
134 OwningNonNull
<nsIContent
> newLeftContent
= *mNewLeftContent
;
135 nsresult rv
= htmlEditor
->DoJoinNodes(containerContent
, newLeftContent
);
136 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "HTMLEditor::DoJoinNodes() failed");
140 /* Redo cannot simply resplit the right node, because subsequent transactions
141 * on the redo stack may depend on the left node existing in its previous
144 NS_IMETHODIMP
SplitNodeTransaction::RedoTransaction() {
145 if (NS_WARN_IF(!mNewLeftContent
) || NS_WARN_IF(!mContainerParentNode
) ||
146 NS_WARN_IF(!mStartOfRightContent
.IsInContentNode()) ||
147 NS_WARN_IF(!mHTMLEditor
)) {
148 return NS_ERROR_NOT_AVAILABLE
;
151 OwningNonNull
<HTMLEditor
> htmlEditor
= *mHTMLEditor
;
152 OwningNonNull
<nsINode
> newLeftContent
= *mNewLeftContent
;
153 OwningNonNull
<nsINode
> containerParentNode
= *mContainerParentNode
;
154 EditorDOMPoint
startOfRightContent(mStartOfRightContent
);
156 // First, massage the existing node so it is in its post-split state
158 if (startOfRightContent
.IsInTextNode()) {
159 Text
* rightTextNode
= startOfRightContent
.ContainerAsText();
160 htmlEditor
->DoDeleteText(MOZ_KnownLive(*rightTextNode
), 0,
161 startOfRightContent
.Offset(), error
);
162 if (error
.Failed()) {
163 NS_WARNING("EditorBase::DoDeleteText() failed");
164 return error
.StealNSResult();
167 AutoTArray
<OwningNonNull
<nsIContent
>, 24> movingChildren
;
168 if (nsIContent
* child
=
169 startOfRightContent
.GetContainer()->GetFirstChild()) {
170 movingChildren
.AppendElement(*child
);
171 for (uint32_t i
= 0; i
< startOfRightContent
.Offset(); i
++) {
172 child
= child
->GetNextSibling();
176 movingChildren
.AppendElement(*child
);
180 for (OwningNonNull
<nsIContent
>& child
: movingChildren
) {
181 newLeftContent
->AppendChild(child
, error
);
182 if (error
.Failed()) {
183 NS_WARNING("nsINode::AppendChild() failed");
184 return error
.StealNSResult();
188 MOZ_ASSERT(!error
.Failed());
189 // Second, re-insert the left node into the tree
190 containerParentNode
->InsertBefore(newLeftContent
,
191 startOfRightContent
.GetContainer(), error
);
192 // InsertBefore() may call MightThrowJSException() even if there is no
193 // error. We don't need the flag here.
194 error
.WouldReportJSException();
195 NS_WARNING_ASSERTION(!error
.Failed(), "nsINode::InsertBefore() failed");
196 return error
.StealNSResult();
199 } // namespace mozilla