Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/SelectionState.h
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
#ifndef mozilla_SelectionState_h
7
#define mozilla_SelectionState_h
8
9
#include "mozilla/EditorDOMPoint.h"
10
#include "nsCOMPtr.h"
11
#include "nsDirection.h"
12
#include "nsINode.h"
13
#include "nsTArray.h"
14
#include "nscore.h"
15
16
class nsCycleCollectionTraversalCallback;
17
class nsRange;
18
namespace mozilla {
19
class RangeUpdater;
20
namespace dom {
21
class Selection;
22
class Text;
23
} // namespace dom
24
25
/**
26
 * A helper struct for saving/setting ranges.
27
 */
28
struct RangeItem final
29
{
30
  RangeItem();
31
32
private:
33
  // Private destructor, to discourage deletion outside of Release():
34
  ~RangeItem();
35
36
public:
37
  void StoreRange(nsRange* aRange);
38
  already_AddRefed<nsRange> GetRange();
39
40
  NS_INLINE_DECL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_NATIVE_REFCOUNTING(RangeItem)
41
  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(RangeItem)
42
43
  nsCOMPtr<nsINode> mStartContainer;
44
  int32_t mStartOffset;
45
  nsCOMPtr<nsINode> mEndContainer;
46
  int32_t mEndOffset;
47
};
48
49
/**
50
 * mozilla::SelectionState
51
 *
52
 * Class for recording selection info.  Stores selection as collection of
53
 * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Can't store
54
 * ranges since dom gravity will possibly change the ranges.
55
 */
56
57
class SelectionState final
58
{
59
public:
60
  SelectionState();
61
  ~SelectionState();
62
63
  void SaveSelection(dom::Selection *aSel);
64
  nsresult RestoreSelection(dom::Selection* aSel);
65
  bool IsCollapsed();
66
  bool IsEqual(SelectionState *aSelState);
67
  void MakeEmpty();
68
  bool IsEmpty();
69
private:
70
  AutoTArray<RefPtr<RangeItem>, 1> mArray;
71
  nsDirection mDirection;
72
73
  friend class RangeUpdater;
74
  friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
75
                                          SelectionState&,
76
                                          const char*,
77
                                          uint32_t);
78
  friend void ImplCycleCollectionUnlink(SelectionState&);
79
};
80
81
inline void
82
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
83
                            SelectionState& aField,
84
                            const char* aName,
85
                            uint32_t aFlags = 0)
86
0
{
87
0
  ImplCycleCollectionTraverse(aCallback, aField.mArray, aName, aFlags);
88
0
}
89
90
inline void
91
ImplCycleCollectionUnlink(SelectionState& aField)
92
0
{
93
0
  ImplCycleCollectionUnlink(aField.mArray);
94
0
}
95
96
class RangeUpdater final
97
{
98
public:
99
  RangeUpdater();
100
  ~RangeUpdater();
101
102
  void RegisterRangeItem(RangeItem* aRangeItem);
103
  void DropRangeItem(RangeItem* aRangeItem);
104
  nsresult RegisterSelectionState(SelectionState& aSelState);
105
  nsresult DropSelectionState(SelectionState& aSelState);
106
107
  // editor selection gravity routines.  Note that we can't always depend on
108
  // DOM Range gravity to do what we want to the "real" selection.  For instance,
109
  // if you move a node, that corresponds to deleting it and reinserting it.
110
  // DOM Range gravity will promote the selection out of the node on deletion,
111
  // which is not what you want if you know you are reinserting it.
112
  template<typename PT, typename CT>
113
  nsresult SelAdjCreateNode(const EditorDOMPointBase<PT, CT>& aPoint);
114
  template<typename PT, typename CT>
115
  nsresult SelAdjInsertNode(const EditorDOMPointBase<PT, CT>& aPoint);
116
  void SelAdjDeleteNode(nsINode* aNode);
117
  nsresult SelAdjSplitNode(nsIContent& aRightNode, nsIContent* aNewLeftNode);
118
  nsresult SelAdjJoinNodes(nsINode& aLeftNode,
119
                           nsINode& aRightNode,
120
                           nsINode& aParent,
121
                           int32_t aOffset,
122
                           int32_t aOldLeftNodeLength);
123
  void SelAdjInsertText(dom::Text& aTextNode, int32_t aOffset,
124
                        const nsAString &aString);
125
  nsresult SelAdjDeleteText(nsIContent* aTextNode, int32_t aOffset,
126
                            int32_t aLength);
127
  // the following gravity routines need will/did sandwiches, because the other
128
  // gravity routines will be called inside of these sandwiches, but should be
129
  // ignored.
130
  nsresult WillReplaceContainer();
131
  nsresult DidReplaceContainer(dom::Element* aOriginalNode,
132
                               dom::Element* aNewNode);
133
  nsresult WillRemoveContainer();
134
  nsresult DidRemoveContainer(nsINode* aNode, nsINode* aParent,
135
                              int32_t aOffset, uint32_t aNodeOrigLen);
136
  nsresult WillInsertContainer();
137
  nsresult DidInsertContainer();
138
  void WillMoveNode();
139
  void DidMoveNode(nsINode* aOldParent, int32_t aOldOffset,
140
                   nsINode* aNewParent, int32_t aNewOffset);
141
142
private:
143
  friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
144
                                          RangeUpdater&,
145
                                          const char*,
146
                                          uint32_t);
147
  friend void ImplCycleCollectionUnlink(RangeUpdater& aField);
148
149
  nsTArray<RefPtr<RangeItem>> mArray;
150
  bool mLock;
151
};
152
153
inline void
154
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
155
                            RangeUpdater& aField,
156
                            const char* aName,
157
                            uint32_t aFlags = 0)
158
0
{
159
0
  ImplCycleCollectionTraverse(aCallback, aField.mArray, aName, aFlags);
160
0
}
161
162
inline void
163
ImplCycleCollectionUnlink(RangeUpdater& aField)
164
0
{
165
0
  ImplCycleCollectionUnlink(aField.mArray);
166
0
}
167
168
/**
169
 * Helper class for using SelectionState.  Stack based class for doing
170
 * preservation of dom points across editor actions.
171
 */
172
173
class MOZ_STACK_CLASS AutoTrackDOMPoint final
174
{
175
private:
176
  RangeUpdater& mRangeUpdater;
177
  // Allow tracking nsINode until nsNode is gone
178
  nsCOMPtr<nsINode>* mNode;
179
  int32_t* mOffset;
180
  EditorDOMPoint* mPoint;
181
  RefPtr<RangeItem> mRangeItem;
182
183
public:
184
  AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
185
                    nsCOMPtr<nsINode>* aNode, int32_t* aOffset)
186
    : mRangeUpdater(aRangeUpdater)
187
    , mNode(aNode)
188
    , mOffset(aOffset)
189
    , mPoint(nullptr)
190
0
  {
191
0
    mRangeItem = new RangeItem();
192
0
    mRangeItem->mStartContainer = *mNode;
193
0
    mRangeItem->mEndContainer = *mNode;
194
0
    mRangeItem->mStartOffset = *mOffset;
195
0
    mRangeItem->mEndOffset = *mOffset;
196
0
    mRangeUpdater.RegisterRangeItem(mRangeItem);
197
0
  }
198
199
  AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
200
                    EditorDOMPoint* aPoint)
201
    : mRangeUpdater(aRangeUpdater)
202
    , mNode(nullptr)
203
    , mOffset(nullptr)
204
    , mPoint(aPoint)
205
0
  {
206
0
    mRangeItem = new RangeItem();
207
0
    mRangeItem->mStartContainer = mPoint->GetContainer();
208
0
    mRangeItem->mEndContainer = mPoint->GetContainer();
209
0
    mRangeItem->mStartOffset = mPoint->Offset();
210
0
    mRangeItem->mEndOffset = mPoint->Offset();
211
0
    mRangeUpdater.RegisterRangeItem(mRangeItem);
212
0
  }
213
214
  ~AutoTrackDOMPoint()
215
0
  {
216
0
    mRangeUpdater.DropRangeItem(mRangeItem);
217
0
    if (mPoint) {
218
0
      mPoint->Set(mRangeItem->mStartContainer, mRangeItem->mStartOffset);
219
0
      return;
220
0
    }
221
0
    *mNode = mRangeItem->mStartContainer;
222
0
    *mOffset = mRangeItem->mStartOffset;
223
0
  }
224
};
225
226
/**
227
 * Another helper class for SelectionState.  Stack based class for doing
228
 * Will/DidReplaceContainer()
229
 */
230
231
class MOZ_STACK_CLASS AutoReplaceContainerSelNotify final
232
{
233
private:
234
  RangeUpdater& mRangeUpdater;
235
  dom::Element* mOriginalElement;
236
  dom::Element* mNewElement;
237
238
public:
239
  AutoReplaceContainerSelNotify(RangeUpdater& aRangeUpdater,
240
                                dom::Element* aOriginalElement,
241
                                dom::Element* aNewElement)
242
    : mRangeUpdater(aRangeUpdater)
243
    , mOriginalElement(aOriginalElement)
244
    , mNewElement(aNewElement)
245
0
  {
246
0
    mRangeUpdater.WillReplaceContainer();
247
0
  }
248
249
  ~AutoReplaceContainerSelNotify()
250
0
  {
251
0
    mRangeUpdater.DidReplaceContainer(mOriginalElement, mNewElement);
252
0
  }
253
};
254
255
/**
256
 * Another helper class for SelectionState.  Stack based class for doing
257
 * Will/DidRemoveContainer()
258
 */
259
260
class MOZ_STACK_CLASS AutoRemoveContainerSelNotify final
261
{
262
private:
263
  RangeUpdater& mRangeUpdater;
264
  nsINode* mNode;
265
  nsINode* mParent;
266
  int32_t mOffset;
267
  uint32_t mNodeOrigLen;
268
269
public:
270
  AutoRemoveContainerSelNotify(RangeUpdater& aRangeUpdater,
271
                               nsINode* aNode,
272
                               nsINode* aParent,
273
                               int32_t aOffset,
274
                               uint32_t aNodeOrigLen)
275
    : mRangeUpdater(aRangeUpdater)
276
    , mNode(aNode)
277
    , mParent(aParent)
278
    , mOffset(aOffset)
279
    , mNodeOrigLen(aNodeOrigLen)
280
0
  {
281
0
    mRangeUpdater.WillRemoveContainer();
282
0
  }
283
284
  ~AutoRemoveContainerSelNotify()
285
0
  {
286
0
    mRangeUpdater.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
287
0
  }
288
};
289
290
/**
291
 * Another helper class for SelectionState.  Stack based class for doing
292
 * Will/DidInsertContainer()
293
 */
294
295
class MOZ_STACK_CLASS AutoInsertContainerSelNotify final
296
{
297
private:
298
  RangeUpdater& mRangeUpdater;
299
300
public:
301
  explicit AutoInsertContainerSelNotify(RangeUpdater& aRangeUpdater)
302
    : mRangeUpdater(aRangeUpdater)
303
0
  {
304
0
    mRangeUpdater.WillInsertContainer();
305
0
  }
306
307
  ~AutoInsertContainerSelNotify()
308
0
  {
309
0
    mRangeUpdater.DidInsertContainer();
310
0
  }
311
};
312
313
/**
314
 * Another helper class for SelectionState.  Stack based class for doing
315
 * Will/DidMoveNode()
316
 */
317
318
class MOZ_STACK_CLASS AutoMoveNodeSelNotify final
319
{
320
public:
321
  AutoMoveNodeSelNotify(RangeUpdater& aRangeUpdater,
322
                        const EditorDOMPoint& aOldPoint,
323
                        const EditorDOMPoint& aNewPoint)
324
    : mRangeUpdater(aRangeUpdater)
325
    , mOldParent(aOldPoint.GetContainer())
326
    , mNewParent(aNewPoint.GetContainer())
327
    , mOldOffset(aOldPoint.Offset())
328
    , mNewOffset(aNewPoint.Offset())
329
0
  {
330
0
    MOZ_ASSERT(mOldParent);
331
0
    MOZ_ASSERT(mNewParent);
332
0
    mRangeUpdater.WillMoveNode();
333
0
  }
334
335
  ~AutoMoveNodeSelNotify()
336
0
  {
337
0
    mRangeUpdater.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
338
0
  }
339
340
  EditorRawDOMPoint ComputeInsertionPoint() const
341
0
  {
342
0
    if (mOldParent == mNewParent &&
343
0
        mOldOffset < mNewOffset) {
344
0
      return EditorRawDOMPoint(mNewParent, mNewOffset - 1);
345
0
    }
346
0
    return EditorRawDOMPoint(mNewParent, mNewOffset);
347
0
  }
348
349
private:
350
  RangeUpdater& mRangeUpdater;
351
  nsINode* mOldParent;
352
  nsINode* mNewParent;
353
  uint32_t mOldOffset;
354
  uint32_t mNewOffset;
355
};
356
357
} // namespace mozilla
358
359
#endif // #ifndef mozilla_SelectionState_h