SelectionState.h 10 KB

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