Bug 1686495 [wpt PR 27132] - Add tests for proposed WebDriver Shadow DOM support...
[gecko.git] / editor / libeditor / SplitNodeTransaction.cpp
blob4d5f86e9ac7c652942f28be089bc831fa391d778
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
16 namespace mozilla {
18 using namespace dom;
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);
25 // static
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());
59 // Create a new node
60 ErrorResult error;
61 // Don't use .downcast directly because AsContent has an assertion we want
62 nsCOMPtr<nsINode> cloneOfRightContainer =
63 mStartOfRightContent.GetContainer()->CloneNode(false, error);
64 if (error.Failed()) {
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);
97 if (error.Failed()) {
98 NS_WARNING("HTMLEditor::DoSplitNode() failed");
99 return error.StealNSResult();
102 if (!htmlEditor->AllowsTransactionsToChangeSelection()) {
103 return NS_OK;
106 NS_WARNING_ASSERTION(
107 !htmlEditor->Destroyed(),
108 "The editor has gone but SplitNodeTransaction keeps trying to modify "
109 "Selection");
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");
137 return rv;
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
142 * state.
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
157 ErrorResult error;
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();
166 } else {
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();
173 if (!child) {
174 break;
176 movingChildren.AppendElement(*child);
179 ErrorResult error;
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