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_TextServicesDocument_h 7 #define mozilla_TextServicesDocument_h 8 9 #include "nsCOMPtr.h" 10 #include "nsCycleCollectionParticipant.h" 11 #include "nsIEditActionListener.h" 12 #include "nsISupportsImpl.h" 13 #include "nsStringFwd.h" 14 #include "nsTArray.h" 15 #include "nscore.h" 16 17 class nsIContent; 18 class nsIContentIterator; 19 class nsIDOMCharacterData; 20 class nsIDOMDocument; 21 class nsIDOMNode; 22 class nsIEditor; 23 class nsINode; 24 class nsISelection; 25 class nsISelectionController; 26 class nsITextServicesFilter; 27 class nsRange; 28 29 namespace mozilla { 30 31 class OffsetEntry; 32 class TextEditor; 33 34 /** 35 * The TextServicesDocument presents the document in as a bunch of flattened 36 * text blocks. Each text block can be retrieved as an nsString. 37 */ 38 class TextServicesDocument final : public nsIEditActionListener { 39 private: 40 enum class IteratorStatus : uint8_t { 41 // No iterator (I), or iterator doesn't point to anything valid. 42 eDone = 0, 43 // I points to first text node (TN) in current block (CB). 44 eValid, 45 // No TN in CB, I points to first TN in prev block. 46 ePrev, 47 // No TN in CB, I points to first TN in next block. 48 eNext, 49 }; 50 51 nsCOMPtr<nsIDOMDocument> mDOMDocument; 52 nsCOMPtr<nsISelectionController> mSelCon; 53 RefPtr<TextEditor> mTextEditor; 54 nsCOMPtr<nsIContentIterator> mIterator; 55 nsCOMPtr<nsIContent> mPrevTextBlock; 56 nsCOMPtr<nsIContent> mNextTextBlock; 57 nsTArray<OffsetEntry*> mOffsetTable; 58 RefPtr<nsRange> mExtent; 59 nsCOMPtr<nsITextServicesFilter> mTxtSvcFilter; 60 61 int32_t mSelStartIndex; 62 int32_t mSelStartOffset; 63 int32_t mSelEndIndex; 64 int32_t mSelEndOffset; 65 66 IteratorStatus mIteratorStatus; 67 68 protected: 69 virtual ~TextServicesDocument(); 70 71 public: 72 TextServicesDocument(); 73 74 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 75 NS_DECL_CYCLE_COLLECTION_CLASS(TextServicesDocument) 76 77 /** 78 * Initializes the text services document to use a particular editor. The 79 * text services document will use the DOM document and presentation shell 80 * used by the editor. 81 * 82 * @param aEditor The editor to use. 83 */ 84 nsresult InitWithEditor(nsIEditor* aEditor); 85 86 /** 87 * Get the DOM document for the document in use. 88 * 89 * @return aDOMDocument The dom document. 90 */ 91 nsresult GetDocument(nsIDOMDocument** aDOMDocument); 92 93 /** 94 * Sets the range/extent over which the text services document will iterate. 95 * Note that InitWithEditor() should have been called prior to calling this 96 * method. If this method is never called, the text services defaults to 97 * iterating over the entire document. 98 * 99 * @param aDOMRange The range to use. aDOMRange must point to a 100 * valid range object. 101 */ 102 nsresult SetExtent(nsRange* aRange); 103 104 /** 105 * Expands the end points of the range so that it spans complete words. This 106 * call does not change any internal state of the text services document. 107 * 108 * @param aDOMRange The range to be expanded/adjusted. 109 */ 110 nsresult ExpandRangeToWordBoundaries(nsRange* aRange); 111 112 /** 113 * Sets the filter to be used while iterating over content. 114 * 115 * @param aFilter The filter to be used while iterating over 116 * content. 117 */ 118 nsresult SetFilter(nsITextServicesFilter* aFilter); 119 120 /** 121 * Returns the text in the current text block. 122 * 123 * @param aStr [OUT] This will contain the text. 124 */ 125 nsresult GetCurrentTextBlock(nsString* aStr); 126 127 /** 128 * Tells the document to point to the first text block in the document. This 129 * method does not adjust the current cursor position or selection. 130 */ 131 nsresult FirstBlock(); 132 133 enum class BlockSelectionStatus { 134 // There is no text block (TB) in or before the selection (S). 135 eBlockNotFound = 0, 136 // No TB in S, but found one before/after S. 137 eBlockOutside, 138 // S extends beyond the start and end of TB. 139 eBlockInside, 140 // TB contains entire S. 141 eBlockContains, 142 // S begins or ends in TB but extends outside of TB. 143 eBlockPartial, 144 }; 145 146 /** 147 * Tells the document to point to the last text block that contains the 148 * current selection or caret. 149 * 150 * @param aSelectionStatus [OUT] This will contain the text block 151 * selection status. 152 * @param aSelectionOffset [OUT] This will contain the offset into the 153 * string returned by GetCurrentTextBlock() where 154 * the selection begins. 155 * @param aLength [OUT] This will contain the number of 156 * characters that are selected in the string. 157 */ 158 nsresult LastSelectedBlock(BlockSelectionStatus* aSelStatus, 159 int32_t* aSelOffset, int32_t* aSelLength); 160 161 /** 162 * Tells the document to point to the text block before the current one. 163 * This method will return NS_OK, even if there is no previous block. 164 * Callers should call IsDone() to check if we have gone beyond the first 165 * text block in the document. 166 */ 167 nsresult PrevBlock(); 168 169 /** 170 * Tells the document to point to the text block after the current one. 171 * This method will return NS_OK, even if there is no next block. Callers 172 * should call IsDone() to check if we have gone beyond the last text block 173 * in the document. 174 */ 175 nsresult NextBlock(); 176 177 /** 178 * IsDone() will always set aIsDone == false unless the document contains 179 * no text, PrevBlock() was called while the document was already pointing 180 * to the first text block in the document, or NextBlock() was called while 181 * the document was already pointing to the last text block in the document. 182 * 183 * @param aIsDone [OUT] This will contain the result. 184 */ 185 nsresult IsDone(bool* aIsDone); 186 187 /** 188 * SetSelection() allows the caller to set the selection based on an offset 189 * into the string returned by GetCurrentTextBlock(). A length of zero 190 * places the cursor at that offset. A positive non-zero length "n" selects 191 * n characters in the string. 192 * 193 * @param aOffset Offset into string returned by 194 * GetCurrentTextBlock(). 195 * @param aLength Number of characters selected. 196 */ 197 nsresult SetSelection(int32_t aOffset, int32_t aLength); 198 199 /** 200 * Scrolls the document so that the current selection is visible. 201 */ 202 nsresult ScrollSelectionIntoView(); 203 204 /** 205 * Deletes the text selected by SetSelection(). Calling DeleteSelection() 206 * with nothing selected, or with a collapsed selection (cursor) does 207 * nothing and returns NS_OK. 208 */ 209 nsresult DeleteSelection(); 210 211 /** 212 * Inserts the given text at the current cursor position. If there is a 213 * selection, it will be deleted before the text is inserted. 214 */ 215 nsresult InsertText(const nsString* aText); 216 217 /** 218 * nsIEditActionListener method implementations. 219 */ 220 NS_DECL_NSIEDITACTIONLISTENER 221 222 /** 223 * Actual edit action listeners. When you add new method here for listening 224 * to new edit action, you need to make it called by EditorBase. 225 * Additionally, you need to call it from proper method of 226 * nsIEditActionListener too because if this is created not for inline 227 * spell checker of the editor, edit actions will be notified via 228 * nsIEditActionListener (slow path, though). 229 */ 230 void DidDeleteNode(nsINode* aChild); 231 void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode); 232 233 static nsresult GetRangeEndPoints(nsRange* aRange, nsINode** aStartContainer, 234 int32_t* aStartOffset, 235 nsINode** aEndContainer, 236 int32_t* aEndOffset); 237 238 private: 239 nsresult CreateContentIterator(nsRange* aRange, 240 nsIContentIterator** aIterator); 241 242 already_AddRefed<nsINode> GetDocumentContentRootNode(); 243 already_AddRefed<nsRange> CreateDocumentContentRange(); 244 already_AddRefed<nsRange> CreateDocumentContentRootToNodeOffsetRange( 245 nsINode* aParent, uint32_t aOffset, bool aToStart); 246 nsresult CreateDocumentContentIterator(nsIContentIterator** aIterator); 247 248 nsresult AdjustContentIterator(); 249 250 static nsresult FirstTextNode(nsIContentIterator* aIterator, 251 IteratorStatus* aIteratorStatus); 252 static nsresult LastTextNode(nsIContentIterator* aIterator, 253 IteratorStatus* aIteratorStatus); 254 255 static nsresult FirstTextNodeInCurrentBlock(nsIContentIterator* aIterator); 256 static nsresult FirstTextNodeInPrevBlock(nsIContentIterator* aIterator); 257 static nsresult FirstTextNodeInNextBlock(nsIContentIterator* aIterator); 258 259 nsresult GetFirstTextNodeInPrevBlock(nsIContent** aContent); 260 nsresult GetFirstTextNodeInNextBlock(nsIContent** aContent); 261 262 static bool IsBlockNode(nsIContent* aContent); 263 static bool IsTextNode(nsIContent* aContent); 264 static bool IsTextNode(nsIDOMNode* aNode); 265 266 static bool DidSkip(nsIContentIterator* aFilteredIter); 267 static void ClearDidSkip(nsIContentIterator* aFilteredIter); 268 269 static bool HasSameBlockNodeParent(nsIContent* aContent1, 270 nsIContent* aContent2); 271 272 nsresult SetSelectionInternal(int32_t aOffset, int32_t aLength, 273 bool aDoUpdate); 274 nsresult GetSelection(BlockSelectionStatus* aSelStatus, int32_t* aSelOffset, 275 int32_t* aSelLength); 276 nsresult GetCollapsedSelection(BlockSelectionStatus* aSelStatus, 277 int32_t* aSelOffset, int32_t* aSelLength); 278 nsresult GetUncollapsedSelection(BlockSelectionStatus* aSelStatus, 279 int32_t* aSelOffset, int32_t* aSelLength); 280 281 bool SelectionIsCollapsed(); 282 bool SelectionIsValid(); 283 284 static nsresult CreateOffsetTable(nsTArray<OffsetEntry*>* aOffsetTable, 285 nsIContentIterator* aIterator, 286 IteratorStatus* aIteratorStatus, 287 nsRange* aIterRange, nsString* aStr); 288 static nsresult ClearOffsetTable(nsTArray<OffsetEntry*>* aOffsetTable); 289 290 static nsresult NodeHasOffsetEntry(nsTArray<OffsetEntry*>* aOffsetTable, 291 nsINode* aNode, bool* aHasEntry, 292 int32_t* aEntryIndex); 293 294 nsresult RemoveInvalidOffsetEntries(); 295 nsresult SplitOffsetEntry(int32_t aTableIndex, int32_t aOffsetIntoEntry); 296 297 static nsresult FindWordBounds(nsTArray<OffsetEntry*>* aOffsetTable, 298 nsString* aBlockStr, nsINode* aNode, 299 int32_t aNodeOffset, nsINode** aWordStartNode, 300 int32_t* aWordStartOffset, 301 nsINode** aWordEndNode, 302 int32_t* aWordEndOffset); 303 }; 304 305 } // namespace mozilla 306 307 #endif // #ifndef mozilla_TextServicesDocument_h 308