Bug 1769033 - Add OpenBSD sandboxing support r=gaston
[gecko.git] / editor / libeditor / PlaceholderTransaction.cpp
blob1cfdfd17d4fb9e48c07c659a5309f950cde8276d
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 "PlaceholderTransaction.h"
8 #include <utility>
10 #include "CompositionTransaction.h"
11 #include "mozilla/EditorBase.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/ToString.h"
14 #include "mozilla/dom/Selection.h"
15 #include "nsGkAtoms.h"
16 #include "nsQueryObject.h"
18 namespace mozilla {
20 using namespace dom;
22 PlaceholderTransaction::PlaceholderTransaction(
23 EditorBase& aEditorBase, nsStaticAtom& aName,
24 Maybe<SelectionState>&& aSelState)
25 : mEditorBase(&aEditorBase),
26 mCompositionTransaction(nullptr),
27 mStartSel(*std::move(aSelState)),
28 mAbsorb(true),
29 mCommitted(false) {
30 mName = &aName;
33 NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTransaction)
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction,
36 EditAggregateTransaction)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase);
38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSel);
39 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel);
40 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction,
43 EditAggregateTransaction)
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase);
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSel);
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel);
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTransaction)
50 NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
52 NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
53 NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
55 NS_IMETHODIMP PlaceholderTransaction::DoTransaction() {
56 MOZ_LOG(
57 GetLogModule(), LogLevel::Info,
58 ("%p PlaceholderTransaction::%s this={ mName=%s }", this, __FUNCTION__,
59 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
60 return NS_OK;
63 NS_IMETHODIMP PlaceholderTransaction::UndoTransaction() {
64 MOZ_LOG(GetLogModule(), LogLevel::Info,
65 ("%p PlaceholderTransaction::%s this={ mName=%s } "
66 "Start==============================",
67 this, __FUNCTION__,
68 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
70 if (NS_WARN_IF(!mEditorBase)) {
71 return NS_ERROR_NOT_INITIALIZED;
74 // Undo transactions.
75 nsresult rv = EditAggregateTransaction::UndoTransaction();
76 if (NS_FAILED(rv)) {
77 NS_WARNING("EditAggregateTransaction::UndoTransaction() failed");
78 return rv;
81 // now restore selection
82 RefPtr<Selection> selection = mEditorBase->GetSelection();
83 if (NS_WARN_IF(!selection)) {
84 return NS_ERROR_FAILURE;
86 rv = mStartSel.RestoreSelection(*selection);
87 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
88 "SelectionState::RestoreSelection() failed");
90 MOZ_LOG(GetLogModule(), LogLevel::Info,
91 ("%p PlaceholderTransaction::%s this={ mName=%s } "
92 "End==============================",
93 this, __FUNCTION__,
94 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
95 return rv;
98 NS_IMETHODIMP PlaceholderTransaction::RedoTransaction() {
99 MOZ_LOG(GetLogModule(), LogLevel::Info,
100 ("%p PlaceholderTransaction::%s this={ mName=%s } "
101 "Start==============================",
102 this, __FUNCTION__,
103 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
105 if (NS_WARN_IF(!mEditorBase)) {
106 return NS_ERROR_NOT_INITIALIZED;
109 // Redo transactions.
110 nsresult rv = EditAggregateTransaction::RedoTransaction();
111 if (NS_FAILED(rv)) {
112 NS_WARNING("EditAggregateTransaction::RedoTransaction() failed");
113 return rv;
116 // now restore selection
117 RefPtr<Selection> selection = mEditorBase->GetSelection();
118 if (NS_WARN_IF(!selection)) {
119 return NS_ERROR_FAILURE;
121 rv = mEndSel.RestoreSelection(*selection);
122 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
123 "SelectionState::RestoreSelection() failed");
124 MOZ_LOG(GetLogModule(), LogLevel::Info,
125 ("%p PlaceholderTransaction::%s this={ mName=%s } "
126 "End==============================",
127 this, __FUNCTION__,
128 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
129 return rv;
132 NS_IMETHODIMP PlaceholderTransaction::Merge(nsITransaction* aOtherTransaction,
133 bool* aDidMerge) {
134 if (NS_WARN_IF(!aDidMerge) || NS_WARN_IF(!aOtherTransaction)) {
135 return NS_ERROR_INVALID_ARG;
138 // set out param default value
139 *aDidMerge = false;
141 if (mForwardingTransaction) {
142 MOZ_ASSERT_UNREACHABLE(
143 "tried to merge into a placeholder that was in "
144 "forwarding mode!");
145 return NS_ERROR_FAILURE;
148 RefPtr<EditTransactionBase> otherTransactionBase =
149 aOtherTransaction->GetAsEditTransactionBase();
150 if (!otherTransactionBase) {
151 MOZ_LOG(GetLogModule(), LogLevel::Debug,
152 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
153 "mName=%s } returned false due to non edit transaction",
154 this, __FUNCTION__, aOtherTransaction,
155 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
156 return NS_OK;
159 // We are absorbing all transactions if mAbsorb is lit.
160 if (mAbsorb) {
161 if (CompositionTransaction* otherCompositionTransaction =
162 otherTransactionBase->GetAsCompositionTransaction()) {
163 // special handling for CompositionTransaction's: they need to merge with
164 // any previous CompositionTransaction in this placeholder, if possible.
165 if (!mCompositionTransaction) {
166 // this is the first IME txn in the placeholder
167 mCompositionTransaction = otherCompositionTransaction;
168 DebugOnly<nsresult> rvIgnored =
169 AppendChild(otherCompositionTransaction);
170 NS_WARNING_ASSERTION(
171 NS_SUCCEEDED(rvIgnored),
172 "EditAggregateTransaction::AppendChild() failed, but ignored");
173 } else {
174 bool didMerge;
175 mCompositionTransaction->Merge(otherCompositionTransaction, &didMerge);
176 if (!didMerge) {
177 // it wouldn't merge. Earlier IME txn is already committed and will
178 // not absorb further IME txns. So just stack this one after it
179 // and remember it as a candidate for further merges.
180 mCompositionTransaction = otherCompositionTransaction;
181 DebugOnly<nsresult> rvIgnored =
182 AppendChild(otherCompositionTransaction);
183 NS_WARNING_ASSERTION(
184 NS_SUCCEEDED(rvIgnored),
185 "EditAggregateTransaction::AppendChild() failed, but ignored");
188 } else {
189 PlaceholderTransaction* otherPlaceholderTransaction =
190 otherTransactionBase->GetAsPlaceholderTransaction();
191 if (!otherPlaceholderTransaction) {
192 // See bug 171243: just drop incoming placeholders on the floor.
193 // Their children will be swallowed by this preexisting one.
194 DebugOnly<nsresult> rvIgnored = AppendChild(otherTransactionBase);
195 NS_WARNING_ASSERTION(
196 NS_SUCCEEDED(rvIgnored),
197 "EditAggregateTransaction::AppendChild() failed, but ignored");
200 *aDidMerge = true;
201 // RememberEndingSelection();
202 // efficiency hack: no need to remember selection here, as we haven't yet
203 // finished the initial batch and we know we will be told when the batch
204 // ends. we can remeber the selection then.
205 return NS_OK;
208 // merge typing or IME or deletion transactions if the selection matches
209 if (mCommitted ||
210 (mName != nsGkAtoms::TypingTxnName && mName != nsGkAtoms::IMETxnName &&
211 mName != nsGkAtoms::DeleteTxnName)) {
212 MOZ_LOG(GetLogModule(), LogLevel::Debug,
213 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
214 "mName=%s } returned false due to non mergable transaction",
215 this, __FUNCTION__, aOtherTransaction,
216 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
217 return NS_OK;
220 PlaceholderTransaction* otherPlaceholderTransaction =
221 otherTransactionBase->GetAsPlaceholderTransaction();
222 if (!otherPlaceholderTransaction) {
223 MOZ_LOG(GetLogModule(), LogLevel::Debug,
224 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
225 "mName=%s } returned false due to non placeholder transaction",
226 this, __FUNCTION__, aOtherTransaction,
227 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
228 return NS_OK;
231 RefPtr<nsAtom> otherTransactionName;
232 DebugOnly<nsresult> rvIgnored = otherPlaceholderTransaction->GetName(
233 getter_AddRefs(otherTransactionName));
234 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
235 "PlaceholderTransaction::GetName() failed, but ignored");
236 if (!otherTransactionName || otherTransactionName == nsGkAtoms::_empty ||
237 otherTransactionName != mName) {
238 MOZ_LOG(GetLogModule(), LogLevel::Debug,
239 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
240 "mName=%s } returned false due to non mergable placeholder "
241 "transaction",
242 this, __FUNCTION__, aOtherTransaction,
243 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
244 return NS_OK;
247 // check if start selection of next placeholder matches
248 // end selection of this placeholder
249 // XXX Theese checks seem wrong. The ending selection is initialized with
250 // actual Selection rather than expected Selection. Therefore, even when
251 // web apps modifies Selection, we don't merge mergable transactions.
253 // If the new transaction's starting Selection is not a caret, we shouldn't be
254 // merged with it because it's probably caused deleting the selection.
255 if (!otherPlaceholderTransaction->mStartSel.HasOnlyCollapsedRange()) {
256 MOZ_LOG(GetLogModule(), LogLevel::Debug,
257 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
258 "mName=%s } returned false due to not collapsed selection at "
259 "start of new transactions",
260 this, __FUNCTION__, aOtherTransaction,
261 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
262 return NS_OK;
265 // If our ending Selection is not a caret, we should not be merged with it
266 // because we probably changed format of a block or style of text.
267 if (!mEndSel.HasOnlyCollapsedRange()) {
268 MOZ_LOG(GetLogModule(), LogLevel::Debug,
269 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
270 "mName=%s } returned false due to not collapsed selection at end "
271 "of previous transactions",
272 this, __FUNCTION__, aOtherTransaction,
273 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
274 return NS_OK;
277 // If the caret positions are now in different root nodes, e.g., the previous
278 // caret position was removed from the DOM tree, this merge should not be
279 // done.
280 const bool isPreviousCaretPointInSameRootOfNewCaretPoint = [&]() {
281 nsINode* previousRootInCurrentDOMTree = mEndSel.GetCommonRootNode();
282 return previousRootInCurrentDOMTree &&
283 previousRootInCurrentDOMTree ==
284 otherPlaceholderTransaction->mStartSel.GetCommonRootNode();
285 }();
286 if (!isPreviousCaretPointInSameRootOfNewCaretPoint) {
287 MOZ_LOG(GetLogModule(), LogLevel::Debug,
288 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
289 "mName=%s } returned false due to the caret points are in "
290 "different root nodes",
291 this, __FUNCTION__, aOtherTransaction,
292 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
293 return NS_OK;
296 // If the caret points of end of us and start of new transaction are not same,
297 // we shouldn't merge them.
298 if (!otherPlaceholderTransaction->mStartSel.Equals(mEndSel)) {
299 MOZ_LOG(GetLogModule(), LogLevel::Debug,
300 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
301 "mName=%s } returned false due to caret positions were different",
302 this, __FUNCTION__, aOtherTransaction,
303 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
304 return NS_OK;
307 mAbsorb = true; // we need to start absorbing again
308 otherPlaceholderTransaction->ForwardEndBatchTo(*this);
309 // AppendChild(editTransactionBase);
310 // see bug 171243: we don't need to merge placeholders
311 // into placeholders. We just reactivate merging in the
312 // pre-existing placeholder and drop the new one on the floor. The
313 // EndPlaceHolderBatch() call on the new placeholder will be
314 // forwarded to this older one.
315 rvIgnored = RememberEndingSelection();
316 NS_WARNING_ASSERTION(
317 NS_SUCCEEDED(rvIgnored),
318 "PlaceholderTransaction::RememberEndingSelection() failed, but "
319 "ignored");
320 *aDidMerge = true;
321 MOZ_LOG(GetLogModule(), LogLevel::Debug,
322 ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
323 "mName=%s } returned true",
324 this, __FUNCTION__, aOtherTransaction,
325 nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
326 return NS_OK;
329 nsresult PlaceholderTransaction::EndPlaceHolderBatch() {
330 mAbsorb = false;
332 if (mForwardingTransaction) {
333 if (mForwardingTransaction) {
334 DebugOnly<nsresult> rvIgnored =
335 mForwardingTransaction->EndPlaceHolderBatch();
336 NS_WARNING_ASSERTION(
337 NS_SUCCEEDED(rvIgnored),
338 "PlaceholderTransaction::EndPlaceHolderBatch() failed, but ignored");
341 // remember our selection state.
342 nsresult rv = RememberEndingSelection();
343 NS_WARNING_ASSERTION(
344 NS_SUCCEEDED(rv),
345 "PlaceholderTransaction::RememberEndingSelection() failed");
346 return rv;
349 nsresult PlaceholderTransaction::RememberEndingSelection() {
350 if (NS_WARN_IF(!mEditorBase)) {
351 return NS_ERROR_NOT_INITIALIZED;
354 RefPtr<Selection> selection = mEditorBase->GetSelection();
355 if (NS_WARN_IF(!selection)) {
356 return NS_ERROR_FAILURE;
358 mEndSel.SaveSelection(*selection);
359 return NS_OK;
362 } // namespace mozilla