Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/libeditor/PlaceholderTransaction.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 "PlaceholderTransaction.h"
7
8
#include "CompositionTransaction.h"
9
#include "mozilla/EditorBase.h"
10
#include "mozilla/dom/Selection.h"
11
#include "mozilla/Move.h"
12
#include "nsGkAtoms.h"
13
#include "nsQueryObject.h"
14
15
namespace mozilla {
16
17
using namespace dom;
18
19
PlaceholderTransaction::PlaceholderTransaction(
20
                          EditorBase& aEditorBase,
21
                          nsAtom* aName,
22
                          Maybe<SelectionState>&& aSelState)
23
  : mEditorBase(&aEditorBase)
24
  , mForwarding(nullptr)
25
  , mCompositionTransaction(nullptr)
26
  , mStartSel(*std::move(aSelState))
27
  , mAbsorb(true)
28
  , mCommitted(false)
29
0
{
30
0
  mName = aName;
31
0
}
32
33
PlaceholderTransaction::~PlaceholderTransaction()
34
0
{
35
0
}
36
37
NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTransaction)
38
39
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction,
40
0
                                                EditAggregateTransaction)
41
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase);
42
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSel);
43
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel);
44
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
45
46
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction,
47
0
                                                  EditAggregateTransaction)
48
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase);
49
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSel);
50
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel);
51
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
52
53
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTransaction)
54
0
  NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction)
55
0
NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
56
57
NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
58
NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
59
60
NS_IMETHODIMP
61
PlaceholderTransaction::DoTransaction()
62
0
{
63
0
  return NS_OK;
64
0
}
65
66
NS_IMETHODIMP
67
PlaceholderTransaction::UndoTransaction()
68
0
{
69
0
  if (NS_WARN_IF(!mEditorBase)) {
70
0
    return NS_ERROR_NOT_INITIALIZED;
71
0
  }
72
0
73
0
  // Undo transactions.
74
0
  nsresult rv = EditAggregateTransaction::UndoTransaction();
75
0
  NS_ENSURE_SUCCESS(rv, rv);
76
0
77
0
  // now restore selection
78
0
  RefPtr<Selection> selection = mEditorBase->GetSelection();
79
0
  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
80
0
  return mStartSel.RestoreSelection(selection);
81
0
}
82
83
NS_IMETHODIMP
84
PlaceholderTransaction::RedoTransaction()
85
0
{
86
0
  if (NS_WARN_IF(!mEditorBase)) {
87
0
    return NS_ERROR_NOT_INITIALIZED;
88
0
  }
89
0
90
0
  // Redo transactions.
91
0
  nsresult rv = EditAggregateTransaction::RedoTransaction();
92
0
  NS_ENSURE_SUCCESS(rv, rv);
93
0
94
0
  // now restore selection
95
0
  RefPtr<Selection> selection = mEditorBase->GetSelection();
96
0
  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
97
0
  return mEndSel.RestoreSelection(selection);
98
0
}
99
100
101
NS_IMETHODIMP
102
PlaceholderTransaction::Merge(nsITransaction* aTransaction,
103
                              bool* aDidMerge)
104
0
{
105
0
  NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER);
106
0
107
0
  // set out param default value
108
0
  *aDidMerge=false;
109
0
110
0
  if (mForwarding) {
111
0
    MOZ_ASSERT_UNREACHABLE("tried to merge into a placeholder that was in "
112
0
                           "forwarding mode!");
113
0
    return NS_ERROR_FAILURE;
114
0
  }
115
0
116
0
  // XXX: hack, not safe!  need nsIEditTransaction!
117
0
  EditTransactionBase* editTransactionBase = (EditTransactionBase*)aTransaction;
118
0
  // determine if this incoming txn is a placeholder txn
119
0
  nsCOMPtr<nsIAbsorbingTransaction> absorbingTransaction =
120
0
    do_QueryObject(editTransactionBase);
121
0
122
0
  // We are absorbing all transactions if mAbsorb is lit.
123
0
  if (mAbsorb) {
124
0
    RefPtr<CompositionTransaction> otherTransaction =
125
0
      do_QueryObject(aTransaction);
126
0
    if (otherTransaction) {
127
0
      // special handling for CompositionTransaction's: they need to merge with
128
0
      // any previous CompositionTransaction in this placeholder, if possible.
129
0
      if (!mCompositionTransaction) {
130
0
        // this is the first IME txn in the placeholder
131
0
        mCompositionTransaction = otherTransaction;
132
0
        AppendChild(editTransactionBase);
133
0
      } else {
134
0
        bool didMerge;
135
0
        mCompositionTransaction->Merge(otherTransaction, &didMerge);
136
0
        if (!didMerge) {
137
0
          // it wouldn't merge.  Earlier IME txn is already committed and will
138
0
          // not absorb further IME txns.  So just stack this one after it
139
0
          // and remember it as a candidate for further merges.
140
0
          mCompositionTransaction = otherTransaction;
141
0
          AppendChild(editTransactionBase);
142
0
        }
143
0
      }
144
0
    } else if (!absorbingTransaction) {
145
0
      // See bug 171243: just drop incoming placeholders on the floor.
146
0
      // Their children will be swallowed by this preexisting one.
147
0
      AppendChild(editTransactionBase);
148
0
    }
149
0
    *aDidMerge = true;
150
0
//  RememberEndingSelection();
151
0
//  efficiency hack: no need to remember selection here, as we haven't yet
152
0
//  finished the initial batch and we know we will be told when the batch ends.
153
0
//  we can remeber the selection then.
154
0
  } else {
155
0
    // merge typing or IME or deletion transactions if the selection matches
156
0
    if ((mName.get() == nsGkAtoms::TypingTxnName ||
157
0
         mName.get() == nsGkAtoms::IMETxnName    ||
158
0
         mName.get() == nsGkAtoms::DeleteTxnName) && !mCommitted) {
159
0
      if (absorbingTransaction) {
160
0
        RefPtr<nsAtom> atom;
161
0
        absorbingTransaction->GetTxnName(getter_AddRefs(atom));
162
0
        if (atom && atom == mName) {
163
0
          // check if start selection of next placeholder matches
164
0
          // end selection of this placeholder
165
0
          bool isSame;
166
0
          absorbingTransaction->StartSelectionEquals(&mEndSel, &isSame);
167
0
          if (isSame) {
168
0
            mAbsorb = true;  // we need to start absorbing again
169
0
            absorbingTransaction->ForwardEndBatchTo(this);
170
0
            // AppendChild(editTransactionBase);
171
0
            // see bug 171243: we don't need to merge placeholders
172
0
            // into placeholders.  We just reactivate merging in the pre-existing
173
0
            // placeholder and drop the new one on the floor.  The EndPlaceHolderBatch()
174
0
            // call on the new placeholder will be forwarded to this older one.
175
0
            RememberEndingSelection();
176
0
            *aDidMerge = true;
177
0
          }
178
0
        }
179
0
      }
180
0
    }
181
0
  }
182
0
  return NS_OK;
183
0
}
184
185
NS_IMETHODIMP
186
PlaceholderTransaction::GetTxnName(nsAtom** aName)
187
0
{
188
0
  return GetName(aName);
189
0
}
190
191
NS_IMETHODIMP
192
PlaceholderTransaction::StartSelectionEquals(SelectionState* aSelState,
193
                                             bool* aResult)
194
0
{
195
0
  // determine if starting selection matches the given selection state.
196
0
  // note that we only care about collapsed selections.
197
0
  NS_ENSURE_TRUE(aResult && aSelState, NS_ERROR_NULL_POINTER);
198
0
  if (!mStartSel.IsCollapsed() || !aSelState->IsCollapsed()) {
199
0
    *aResult = false;
200
0
    return NS_OK;
201
0
  }
202
0
  *aResult = mStartSel.IsEqual(aSelState);
203
0
  return NS_OK;
204
0
}
205
206
NS_IMETHODIMP
207
PlaceholderTransaction::EndPlaceHolderBatch()
208
0
{
209
0
  mAbsorb = false;
210
0
211
0
  if (mForwarding) {
212
0
    nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mForwarding);
213
0
    if (plcTxn) {
214
0
      plcTxn->EndPlaceHolderBatch();
215
0
    }
216
0
  }
217
0
  // remember our selection state.
218
0
  return RememberEndingSelection();
219
0
}
220
221
NS_IMETHODIMP
222
PlaceholderTransaction::ForwardEndBatchTo(
223
                          nsIAbsorbingTransaction* aForwardingAddress)
224
0
{
225
0
  mForwarding = do_GetWeakReference(aForwardingAddress);
226
0
  return NS_OK;
227
0
}
228
229
NS_IMETHODIMP
230
PlaceholderTransaction::Commit()
231
0
{
232
0
  mCommitted = true;
233
0
  return NS_OK;
234
0
}
235
236
nsresult
237
PlaceholderTransaction::RememberEndingSelection()
238
0
{
239
0
  if (NS_WARN_IF(!mEditorBase)) {
240
0
    return NS_ERROR_NOT_INITIALIZED;
241
0
  }
242
0
243
0
  RefPtr<Selection> selection = mEditorBase->GetSelection();
244
0
  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
245
0
  mEndSel.SaveSelection(selection);
246
0
  return NS_OK;
247
0
}
248
249
} // namespace mozilla