/src/mozilla-central/editor/libeditor/CreateElementTransaction.cpp
Line | Count | Source (jump to first uncovered line) |
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 "CreateElementTransaction.h" |
7 | | |
8 | | #include <algorithm> |
9 | | #include <stdio.h> |
10 | | |
11 | | #include "mozilla/dom/Element.h" |
12 | | #include "mozilla/dom/Selection.h" |
13 | | |
14 | | #include "mozilla/Casting.h" |
15 | | #include "mozilla/EditorBase.h" |
16 | | #include "mozilla/EditorDOMPoint.h" |
17 | | |
18 | | #include "nsAlgorithm.h" |
19 | | #include "nsAString.h" |
20 | | #include "nsDebug.h" |
21 | | #include "nsError.h" |
22 | | #include "nsIContent.h" |
23 | | #include "nsIEditor.h" |
24 | | #include "nsINode.h" |
25 | | #include "nsISupportsUtils.h" |
26 | | #include "nsMemory.h" |
27 | | #include "nsReadableUtils.h" |
28 | | #include "nsStringFwd.h" |
29 | | #include "nsString.h" |
30 | | |
31 | | namespace mozilla { |
32 | | |
33 | | using namespace dom; |
34 | | |
35 | | template already_AddRefed<CreateElementTransaction> |
36 | | CreateElementTransaction::Create( |
37 | | EditorBase& aEditorBase, |
38 | | nsAtom& aTag, |
39 | | const EditorDOMPoint& aPointToInsert); |
40 | | template already_AddRefed<CreateElementTransaction> |
41 | | CreateElementTransaction::Create( |
42 | | EditorBase& aEditorBase, |
43 | | nsAtom& aTag, |
44 | | const EditorRawDOMPoint& aPointToInsert); |
45 | | |
46 | | template<typename PT, typename CT> |
47 | | already_AddRefed<CreateElementTransaction> |
48 | | CreateElementTransaction::Create( |
49 | | EditorBase& aEditorBase, |
50 | | nsAtom& aTag, |
51 | | const EditorDOMPointBase<PT, CT>& aPointToInsert) |
52 | 0 | { |
53 | 0 | RefPtr<CreateElementTransaction> transaction = |
54 | 0 | new CreateElementTransaction(aEditorBase, aTag, aPointToInsert); |
55 | 0 | return transaction.forget(); |
56 | 0 | } Unexecuted instantiation: already_AddRefed<mozilla::CreateElementTransaction> mozilla::CreateElementTransaction::Create<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorBase&, nsAtom&, mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) Unexecuted instantiation: already_AddRefed<mozilla::CreateElementTransaction> mozilla::CreateElementTransaction::Create<nsINode*, nsIContent*>(mozilla::EditorBase&, nsAtom&, mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) |
57 | | |
58 | | template<typename PT, typename CT> |
59 | | CreateElementTransaction::CreateElementTransaction( |
60 | | EditorBase& aEditorBase, |
61 | | nsAtom& aTag, |
62 | | const EditorDOMPointBase<PT, CT>& aPointToInsert) |
63 | | : EditTransactionBase() |
64 | | , mEditorBase(&aEditorBase) |
65 | | , mTag(&aTag) |
66 | | , mPointToInsert(aPointToInsert) |
67 | 0 | { |
68 | 0 | } Unexecuted instantiation: mozilla::CreateElementTransaction::CreateElementTransaction<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorBase&, nsAtom&, mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) Unexecuted instantiation: mozilla::CreateElementTransaction::CreateElementTransaction<nsINode*, nsIContent*>(mozilla::EditorBase&, nsAtom&, mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) |
69 | | |
70 | | CreateElementTransaction::~CreateElementTransaction() |
71 | 0 | { |
72 | 0 | } |
73 | | |
74 | | NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction, |
75 | | EditTransactionBase, |
76 | | mEditorBase, |
77 | | mPointToInsert, |
78 | | mNewNode) |
79 | | |
80 | | NS_IMPL_ADDREF_INHERITED(CreateElementTransaction, EditTransactionBase) |
81 | | NS_IMPL_RELEASE_INHERITED(CreateElementTransaction, EditTransactionBase) |
82 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTransaction) |
83 | 0 | NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) |
84 | | |
85 | | |
86 | | NS_IMETHODIMP |
87 | | CreateElementTransaction::DoTransaction() |
88 | 0 | { |
89 | 0 | if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || |
90 | 0 | NS_WARN_IF(!mPointToInsert.IsSet())) { |
91 | 0 | return NS_ERROR_NOT_INITIALIZED; |
92 | 0 | } |
93 | 0 | |
94 | 0 | mNewNode = mEditorBase->CreateHTMLContent(mTag); |
95 | 0 | NS_ENSURE_STATE(mNewNode); |
96 | 0 |
|
97 | 0 | // Try to insert formatting whitespace for the new node: |
98 | 0 | mEditorBase->MarkNodeDirty(mNewNode); |
99 | 0 |
|
100 | 0 | // Insert the new node |
101 | 0 | ErrorResult error; |
102 | 0 | InsertNewNode(error); |
103 | 0 | if (NS_WARN_IF(error.Failed())) { |
104 | 0 | return error.StealNSResult(); |
105 | 0 | } |
106 | 0 | |
107 | 0 | // Only set selection to insertion point if editor gives permission |
108 | 0 | if (!mEditorBase->AllowsTransactionsToChangeSelection()) { |
109 | 0 | // Do nothing - DOM range gravity will adjust selection |
110 | 0 | return NS_OK; |
111 | 0 | } |
112 | 0 | |
113 | 0 | RefPtr<Selection> selection = mEditorBase->GetSelection(); |
114 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
115 | 0 |
|
116 | 0 | EditorRawDOMPoint afterNewNode(mNewNode); |
117 | 0 | if (NS_WARN_IF(!afterNewNode.AdvanceOffset())) { |
118 | 0 | // If mutation observer or mutation event listener moved or removed the |
119 | 0 | // new node, we hit this case. Should we use script blocker while we're |
120 | 0 | // in this method? |
121 | 0 | return NS_ERROR_FAILURE; |
122 | 0 | } |
123 | 0 | selection->Collapse(afterNewNode, error); |
124 | 0 | if (error.Failed()) { |
125 | 0 | NS_WARNING("selection could not be collapsed after insert"); |
126 | 0 | error.SuppressException(); |
127 | 0 | } |
128 | 0 | return NS_OK; |
129 | 0 | } |
130 | | |
131 | | void |
132 | | CreateElementTransaction::InsertNewNode(ErrorResult& aError) |
133 | 0 | { |
134 | 0 | if (mPointToInsert.IsSetAndValid()) { |
135 | 0 | if (mPointToInsert.IsEndOfContainer()) { |
136 | 0 | mPointToInsert.GetContainer()->AppendChild(*mNewNode, aError); |
137 | 0 | NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node"); |
138 | 0 | return; |
139 | 0 | } |
140 | 0 | mPointToInsert.GetContainer()-> |
141 | 0 | InsertBefore(*mNewNode, |
142 | 0 | mPointToInsert.GetChild(), |
143 | 0 | aError); |
144 | 0 | NS_WARNING_ASSERTION(!aError.Failed(), "Failed to insert the new node"); |
145 | 0 | return; |
146 | 0 | } |
147 | 0 |
|
148 | 0 | if (NS_WARN_IF(mPointToInsert.GetChild() && |
149 | 0 | mPointToInsert.GetContainer() != |
150 | 0 | mPointToInsert.GetChild()->GetParentNode())) { |
151 | 0 | aError.Throw(NS_ERROR_FAILURE); |
152 | 0 | return; |
153 | 0 | } |
154 | 0 | |
155 | 0 | // If mPointToInsert has only offset and it's not valid, we need to treat |
156 | 0 | // it as pointing end of the container. |
157 | 0 | mPointToInsert.GetContainer()->AppendChild(*mNewNode, aError); |
158 | 0 | NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node"); |
159 | 0 | } |
160 | | |
161 | | NS_IMETHODIMP |
162 | | CreateElementTransaction::UndoTransaction() |
163 | 0 | { |
164 | 0 | if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) { |
165 | 0 | return NS_ERROR_NOT_INITIALIZED; |
166 | 0 | } |
167 | 0 | |
168 | 0 | ErrorResult error; |
169 | 0 | mPointToInsert.GetContainer()->RemoveChild(*mNewNode, error); |
170 | 0 | if (NS_WARN_IF(error.Failed())) { |
171 | 0 | return error.StealNSResult(); |
172 | 0 | } |
173 | 0 | return NS_OK; |
174 | 0 | } |
175 | | |
176 | | NS_IMETHODIMP |
177 | | CreateElementTransaction::RedoTransaction() |
178 | 0 | { |
179 | 0 | if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) { |
180 | 0 | return NS_ERROR_NOT_INITIALIZED; |
181 | 0 | } |
182 | 0 | |
183 | 0 | // First, reset mNewNode so it has no attributes or content |
184 | 0 | // XXX We never actually did this, we only cleared mNewNode's contents if it |
185 | 0 | // was a CharacterData node (which it's not, it's an Element) |
186 | 0 | // XXX Don't we need to set selection like DoTransaction()? |
187 | 0 | |
188 | 0 | // Now, reinsert mNewNode |
189 | 0 | ErrorResult error; |
190 | 0 | InsertNewNode(error); |
191 | 0 | if (NS_WARN_IF(error.Failed())) { |
192 | 0 | return error.StealNSResult(); |
193 | 0 | } |
194 | 0 | return NS_OK; |
195 | 0 | } |
196 | | |
197 | | already_AddRefed<Element> |
198 | | CreateElementTransaction::GetNewNode() |
199 | 0 | { |
200 | 0 | return nsCOMPtr<Element>(mNewNode).forget(); |
201 | 0 | } |
202 | | |
203 | | } // namespace mozilla |