Line data Source code
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 "nsCOMPtr.h"
10 : #include "nsIDOMNode.h"
11 : #include "nsINode.h"
12 : #include "nsTArray.h"
13 : #include "nscore.h"
14 :
15 : class nsCycleCollectionTraversalCallback;
16 : class nsIDOMCharacterData;
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 2 : NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(RangeItem)
41 2 : 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 :
72 : friend class RangeUpdater;
73 : friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
74 : SelectionState&,
75 : const char*,
76 : uint32_t);
77 : friend void ImplCycleCollectionUnlink(SelectionState&);
78 : };
79 :
80 : inline void
81 0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
82 : SelectionState& aField,
83 : const char* aName,
84 : uint32_t aFlags = 0)
85 : {
86 0 : ImplCycleCollectionTraverse(aCallback, aField.mArray, aName, aFlags);
87 0 : }
88 :
89 : inline void
90 0 : ImplCycleCollectionUnlink(SelectionState& aField)
91 : {
92 0 : ImplCycleCollectionUnlink(aField.mArray);
93 0 : }
94 :
95 : class RangeUpdater final
96 : {
97 : public:
98 : RangeUpdater();
99 : ~RangeUpdater();
100 :
101 : void RegisterRangeItem(RangeItem* aRangeItem);
102 : void DropRangeItem(RangeItem* aRangeItem);
103 : nsresult RegisterSelectionState(SelectionState& aSelState);
104 : nsresult DropSelectionState(SelectionState& aSelState);
105 :
106 : // editor selection gravity routines. Note that we can't always depend on
107 : // DOM Range gravity to do what we want to the "real" selection. For instance,
108 : // if you move a node, that corresponds to deleting it and reinserting it.
109 : // DOM Range gravity will promote the selection out of the node on deletion,
110 : // which is not what you want if you know you are reinserting it.
111 : nsresult SelAdjCreateNode(nsINode* aParent, int32_t aPosition);
112 : nsresult SelAdjInsertNode(nsINode* aParent, int32_t aPosition);
113 : void SelAdjDeleteNode(nsINode* aNode);
114 : nsresult SelAdjSplitNode(nsIContent& aOldRightNode, int32_t aOffset,
115 : nsIContent* aNewLeftNode);
116 : nsresult SelAdjJoinNodes(nsINode& aLeftNode,
117 : nsINode& aRightNode,
118 : nsINode& aParent,
119 : int32_t aOffset,
120 : int32_t aOldLeftNodeLength);
121 : void SelAdjInsertText(dom::Text& aTextNode, int32_t aOffset,
122 : const nsAString &aString);
123 : nsresult SelAdjDeleteText(nsIContent* aTextNode, int32_t aOffset,
124 : int32_t aLength);
125 : nsresult SelAdjDeleteText(nsIDOMCharacterData* aTextNode,
126 : int32_t aOffset, 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 DidRemoveContainer(nsIDOMNode* aNode, nsIDOMNode* aParent,
137 : int32_t aOffset, uint32_t aNodeOrigLen);
138 : nsresult WillInsertContainer();
139 : nsresult DidInsertContainer();
140 : void WillMoveNode();
141 : void DidMoveNode(nsINode* aOldParent, int32_t aOldOffset,
142 : nsINode* aNewParent, int32_t aNewOffset);
143 :
144 : private:
145 : friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
146 : RangeUpdater&,
147 : const char*,
148 : uint32_t);
149 : friend void ImplCycleCollectionUnlink(RangeUpdater& aField);
150 :
151 : nsTArray<RefPtr<RangeItem>> mArray;
152 : bool mLock;
153 : };
154 :
155 : inline void
156 0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
157 : RangeUpdater& aField,
158 : const char* aName,
159 : uint32_t aFlags = 0)
160 : {
161 0 : ImplCycleCollectionTraverse(aCallback, aField.mArray, aName, aFlags);
162 0 : }
163 :
164 : inline void
165 0 : ImplCycleCollectionUnlink(RangeUpdater& aField)
166 : {
167 0 : ImplCycleCollectionUnlink(aField.mArray);
168 0 : }
169 :
170 : /**
171 : * Helper class for using SelectionState. Stack based class for doing
172 : * preservation of dom points across editor actions.
173 : */
174 :
175 : class MOZ_STACK_CLASS AutoTrackDOMPoint final
176 : {
177 : private:
178 : RangeUpdater& mRangeUpdater;
179 : // Allow tracking either nsIDOMNode or nsINode until nsIDOMNode is gone
180 : nsCOMPtr<nsINode>* mNode;
181 : nsCOMPtr<nsIDOMNode>* mDOMNode;
182 : int32_t* mOffset;
183 : RefPtr<RangeItem> mRangeItem;
184 :
185 : public:
186 0 : AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
187 : nsCOMPtr<nsINode>* aNode, int32_t* aOffset)
188 0 : : mRangeUpdater(aRangeUpdater)
189 : , mNode(aNode)
190 : , mDOMNode(nullptr)
191 0 : , mOffset(aOffset)
192 : {
193 0 : mRangeItem = new RangeItem();
194 0 : mRangeItem->mStartContainer = *mNode;
195 0 : mRangeItem->mEndContainer = *mNode;
196 0 : mRangeItem->mStartOffset = *mOffset;
197 0 : mRangeItem->mEndOffset = *mOffset;
198 0 : mRangeUpdater.RegisterRangeItem(mRangeItem);
199 0 : }
200 :
201 0 : AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
202 : nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset)
203 0 : : mRangeUpdater(aRangeUpdater)
204 : , mNode(nullptr)
205 : , mDOMNode(aNode)
206 0 : , mOffset(aOffset)
207 : {
208 0 : mRangeItem = new RangeItem();
209 0 : mRangeItem->mStartContainer = do_QueryInterface(*mDOMNode);
210 0 : mRangeItem->mEndContainer = do_QueryInterface(*mDOMNode);
211 0 : mRangeItem->mStartOffset = *mOffset;
212 0 : mRangeItem->mEndOffset = *mOffset;
213 0 : mRangeUpdater.RegisterRangeItem(mRangeItem);
214 0 : }
215 :
216 0 : ~AutoTrackDOMPoint()
217 0 : {
218 0 : mRangeUpdater.DropRangeItem(mRangeItem);
219 0 : if (mNode) {
220 0 : *mNode = mRangeItem->mStartContainer;
221 : } else {
222 0 : *mDOMNode = GetAsDOMNode(mRangeItem->mStartContainer);
223 : }
224 0 : *mOffset = mRangeItem->mStartOffset;
225 0 : }
226 : };
227 :
228 : /**
229 : * Another helper class for SelectionState. Stack based class for doing
230 : * Will/DidReplaceContainer()
231 : */
232 :
233 : class MOZ_STACK_CLASS AutoReplaceContainerSelNotify final
234 : {
235 : private:
236 : RangeUpdater& mRangeUpdater;
237 : dom::Element* mOriginalElement;
238 : dom::Element* mNewElement;
239 :
240 : public:
241 0 : AutoReplaceContainerSelNotify(RangeUpdater& aRangeUpdater,
242 : dom::Element* aOriginalElement,
243 : dom::Element* aNewElement)
244 0 : : mRangeUpdater(aRangeUpdater)
245 : , mOriginalElement(aOriginalElement)
246 0 : , mNewElement(aNewElement)
247 : {
248 0 : mRangeUpdater.WillReplaceContainer();
249 0 : }
250 :
251 0 : ~AutoReplaceContainerSelNotify()
252 0 : {
253 0 : mRangeUpdater.DidReplaceContainer(mOriginalElement, mNewElement);
254 0 : }
255 : };
256 :
257 : /**
258 : * Another helper class for SelectionState. Stack based class for doing
259 : * Will/DidRemoveContainer()
260 : */
261 :
262 : class MOZ_STACK_CLASS AutoRemoveContainerSelNotify final
263 : {
264 : private:
265 : RangeUpdater& mRangeUpdater;
266 : nsIDOMNode* mNode;
267 : nsIDOMNode* mParent;
268 : int32_t mOffset;
269 : uint32_t mNodeOrigLen;
270 :
271 : public:
272 0 : AutoRemoveContainerSelNotify(RangeUpdater& aRangeUpdater,
273 : nsINode* aNode,
274 : nsINode* aParent,
275 : int32_t aOffset,
276 : uint32_t aNodeOrigLen)
277 0 : : mRangeUpdater(aRangeUpdater)
278 0 : , mNode(aNode->AsDOMNode())
279 0 : , mParent(aParent->AsDOMNode())
280 : , mOffset(aOffset)
281 0 : , mNodeOrigLen(aNodeOrigLen)
282 : {
283 0 : mRangeUpdater.WillRemoveContainer();
284 0 : }
285 :
286 0 : ~AutoRemoveContainerSelNotify()
287 0 : {
288 0 : mRangeUpdater.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
289 0 : }
290 : };
291 :
292 : /**
293 : * Another helper class for SelectionState. Stack based class for doing
294 : * Will/DidInsertContainer()
295 : */
296 :
297 : class MOZ_STACK_CLASS AutoInsertContainerSelNotify final
298 : {
299 : private:
300 : RangeUpdater& mRangeUpdater;
301 :
302 : public:
303 0 : explicit AutoInsertContainerSelNotify(RangeUpdater& aRangeUpdater)
304 0 : : mRangeUpdater(aRangeUpdater)
305 : {
306 0 : mRangeUpdater.WillInsertContainer();
307 0 : }
308 :
309 0 : ~AutoInsertContainerSelNotify()
310 0 : {
311 0 : mRangeUpdater.DidInsertContainer();
312 0 : }
313 : };
314 :
315 : /**
316 : * Another helper class for SelectionState. Stack based class for doing
317 : * Will/DidMoveNode()
318 : */
319 :
320 : class MOZ_STACK_CLASS AutoMoveNodeSelNotify final
321 : {
322 : private:
323 : RangeUpdater& mRangeUpdater;
324 : nsINode* mOldParent;
325 : nsINode* mNewParent;
326 : int32_t mOldOffset;
327 : int32_t mNewOffset;
328 :
329 : public:
330 0 : AutoMoveNodeSelNotify(RangeUpdater& aRangeUpdater,
331 : nsINode* aOldParent,
332 : int32_t aOldOffset,
333 : nsINode* aNewParent,
334 : int32_t aNewOffset)
335 0 : : mRangeUpdater(aRangeUpdater)
336 : , mOldParent(aOldParent)
337 : , mNewParent(aNewParent)
338 : , mOldOffset(aOldOffset)
339 0 : , mNewOffset(aNewOffset)
340 : {
341 0 : MOZ_ASSERT(aOldParent);
342 0 : MOZ_ASSERT(aNewParent);
343 0 : mRangeUpdater.WillMoveNode();
344 0 : }
345 :
346 0 : ~AutoMoveNodeSelNotify()
347 0 : {
348 0 : mRangeUpdater.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
349 0 : }
350 : };
351 :
352 : } // namespace mozilla
353 :
354 : #endif // #ifndef mozilla_SelectionState_h
|