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/. */
5
6 #include "InsertNodeTransaction.h"
7
8 #include "mozilla/EditorBase.h" // for EditorBase
9 #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
10
11 #include "mozilla/dom/Selection.h" // for Selection
12
13 #include "nsAString.h"
14 #include "nsDebug.h" // for NS_ENSURE_TRUE, etc.
15 #include "nsError.h" // for NS_ERROR_NULL_POINTER, etc.
16 #include "nsIContent.h" // for nsIContent
17 #include "nsMemory.h" // for nsMemory
18 #include "nsReadableUtils.h" // for ToNewCString
19 #include "nsString.h" // for nsString
20
21 namespace mozilla {
22
23 using namespace dom;
24
25 // static
Create(EditorBase & aEditorBase,nsIContent & aContentToInsert,const EditorRawDOMPoint & aPointToInsert)26 already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
27 EditorBase& aEditorBase, nsIContent& aContentToInsert,
28 const EditorRawDOMPoint& aPointToInsert) {
29 RefPtr<InsertNodeTransaction> transaction =
30 new InsertNodeTransaction(aEditorBase, aContentToInsert, aPointToInsert);
31 return transaction.forget();
32 }
33
InsertNodeTransaction(EditorBase & aEditorBase,nsIContent & aContentToInsert,const EditorRawDOMPoint & aPointToInsert)34 InsertNodeTransaction::InsertNodeTransaction(
35 EditorBase& aEditorBase, nsIContent& aContentToInsert,
36 const EditorRawDOMPoint& aPointToInsert)
37 : mContentToInsert(&aContentToInsert),
38 mPointToInsert(aPointToInsert),
39 mEditorBase(&aEditorBase) {
40 MOZ_ASSERT(mPointToInsert.IsSetAndValid());
41 // Ensure mPointToInsert stores child at offset.
42 Unused << mPointToInsert.GetChild();
43 }
44
~InsertNodeTransaction()45 InsertNodeTransaction::~InsertNodeTransaction() {}
46
NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction,EditTransactionBase,mEditorBase,mContentToInsert,mPointToInsert)47 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase,
48 mEditorBase, mContentToInsert,
49 mPointToInsert)
50
51 NS_IMPL_ADDREF_INHERITED(InsertNodeTransaction, EditTransactionBase)
52 NS_IMPL_RELEASE_INHERITED(InsertNodeTransaction, EditTransactionBase)
53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTransaction)
54 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
55
56 NS_IMETHODIMP
57 InsertNodeTransaction::DoTransaction() {
58 if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) ||
59 NS_WARN_IF(!mPointToInsert.IsSet())) {
60 return NS_ERROR_NOT_INITIALIZED;
61 }
62
63 if (!mPointToInsert.IsSetAndValid()) {
64 // It seems that DOM tree has been changed after first DoTransaction()
65 // and current RedoTranaction() call.
66 if (mPointToInsert.GetChild()) {
67 EditorDOMPoint newPointToInsert(mPointToInsert.GetChild());
68 if (!newPointToInsert.IsSet()) {
69 // The insertion point has been removed from the DOM tree.
70 // In this case, we should append the node to the container instead.
71 newPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
72 if (NS_WARN_IF(!newPointToInsert.IsSet())) {
73 return NS_ERROR_FAILURE;
74 }
75 }
76 mPointToInsert = newPointToInsert;
77 } else {
78 mPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
79 if (NS_WARN_IF(!mPointToInsert.IsSet())) {
80 return NS_ERROR_FAILURE;
81 }
82 }
83 }
84
85 mEditorBase->MarkNodeDirty(GetAsDOMNode(mContentToInsert));
86
87 ErrorResult error;
88 mPointToInsert.GetContainer()->InsertBefore(*mContentToInsert,
89 mPointToInsert.GetChild(), error);
90 error.WouldReportJSException();
91 if (NS_WARN_IF(error.Failed())) {
92 return error.StealNSResult();
93 }
94
95 // Only set selection to insertion point if editor gives permission
96 if (mEditorBase->GetShouldTxnSetSelection()) {
97 RefPtr<Selection> selection = mEditorBase->GetSelection();
98 if (NS_WARN_IF(!selection)) {
99 return NS_ERROR_FAILURE;
100 }
101 // Place the selection just after the inserted element
102 EditorRawDOMPoint afterInsertedNode(mContentToInsert);
103 DebugOnly<bool> advanced = afterInsertedNode.AdvanceOffset();
104 NS_WARNING_ASSERTION(advanced,
105 "Failed to advance offset after the inserted node");
106 selection->Collapse(afterInsertedNode, error);
107 if (NS_WARN_IF(error.Failed())) {
108 error.SuppressException();
109 }
110 }
111 return NS_OK;
112 }
113
114 NS_IMETHODIMP
UndoTransaction()115 InsertNodeTransaction::UndoTransaction() {
116 if (NS_WARN_IF(!mContentToInsert) || NS_WARN_IF(!mPointToInsert.IsSet())) {
117 return NS_ERROR_NOT_INITIALIZED;
118 }
119 // XXX If the inserted node has been moved to different container node or
120 // just removed from the DOM tree, this always fails.
121 ErrorResult error;
122 mPointToInsert.GetContainer()->RemoveChild(*mContentToInsert, error);
123 if (NS_WARN_IF(error.Failed())) {
124 return error.StealNSResult();
125 }
126 return NS_OK;
127 }
128
129 } // namespace mozilla
130