tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TextServicesDocument.h (15528B)


      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 "mozilla/Maybe.h"
     10 #include "mozilla/UniquePtr.h"
     11 #include "nsCOMPtr.h"
     12 #include "nsCycleCollectionParticipant.h"
     13 #include "nsIEditActionListener.h"
     14 #include "nsISupportsImpl.h"
     15 #include "nsStringFwd.h"
     16 #include "nsTArray.h"
     17 #include "nscore.h"
     18 
     19 class nsIContent;
     20 class nsIEditor;
     21 class nsINode;
     22 class nsISelectionController;
     23 class nsRange;
     24 
     25 namespace mozilla {
     26 
     27 class EditorBase;
     28 class FilteredContentIterator;
     29 class OffsetEntry;
     30 
     31 namespace dom {
     32 class AbstractRange;
     33 class Document;
     34 class Element;
     35 class StaticRange;
     36 };  // namespace dom
     37 
     38 /**
     39 * The TextServicesDocument presents the document in as a bunch of flattened
     40 * text blocks. Each text block can be retrieved as an nsString.
     41 */
     42 class TextServicesDocument final : public nsIEditActionListener {
     43 private:
     44  enum class IteratorStatus : uint8_t {
     45    // No iterator (I), or iterator doesn't point to anything valid.
     46    eDone = 0,
     47    // I points to first text node (TN) in current block (CB).
     48    eValid,
     49    // No TN in CB, I points to first TN in prev block.
     50    ePrev,
     51    // No TN in CB, I points to first TN in next block.
     52    eNext,
     53  };
     54 
     55  class OffsetEntryArray final : public nsTArray<UniquePtr<OffsetEntry>> {
     56   public:
     57    /**
     58     * Init() initializes this array with aFilteredIter.
     59     *
     60     * @param[in]  aIterRange   Can be nullptr.
     61     * @param[out] aAllTextInBlock
     62     *                          Returns all text in the block.
     63     */
     64    Result<IteratorStatus, nsresult> Init(
     65        FilteredContentIterator& aFilteredIter, IteratorStatus aIteratorStatus,
     66        nsRange* aIterRange, nsAString* aAllTextInBlock = nullptr);
     67 
     68    /**
     69     * Returns index of first `OffsetEntry` which manages aTextNode.
     70     */
     71    Maybe<size_t> FirstIndexOf(const dom::Text& aTextNode) const;
     72 
     73    /**
     74     * FindWordRange() returns a word range starting from aStartPointToScan
     75     * in aAllTextInBlock.
     76     */
     77    Result<EditorDOMRangeInTexts, nsresult> FindWordRange(
     78        nsAString& aAllTextInBlock, const EditorRawDOMPoint& aStartPointToScan);
     79 
     80    /**
     81     * SplitElementAt() splits an `OffsetEntry` at aIndex if aOffsetInTextNode
     82     * is middle of the range in the text node.
     83     *
     84     * @param aIndex    Index of the entry which you want to split.
     85     * @param aOffsetInTextNode
     86     *                  Offset in the text node.  I.e., the offset should be
     87     *                  greater than 0 and less than `mLength`.
     88     */
     89    nsresult SplitElementAt(size_t aIndex, uint32_t aOffsetInTextNode);
     90 
     91    /**
     92     * Remove all `OffsetEntry` elements whose `mIsValid` is set to false.
     93     */
     94    void RemoveInvalidElements();
     95 
     96    /**
     97     * Called when non-collapsed selection will be deleted.
     98     */
     99    nsresult WillDeleteSelection();
    100 
    101    /**
    102     * Called when non-collapsed selection is deleteded.
    103     */
    104    OffsetEntry* DidDeleteSelection();
    105 
    106    /**
    107     * Called when aInsertedText is inserted.
    108     */
    109    MOZ_CAN_RUN_SCRIPT nsresult DidInsertText(dom::Selection* aSelection,
    110                                              const nsAString& aInsertedString);
    111 
    112    /**
    113     * Called when selection range will be applied to the DOM Selection.
    114     */
    115    Result<EditorRawDOMRangeInTexts, nsresult> WillSetSelection(
    116        uint32_t aOffsetInTextInBlock, uint32_t aLength);
    117 
    118    class Selection final {
    119     public:
    120      size_t StartIndex() const {
    121        MOZ_ASSERT(IsIndexesSet());
    122        return *mStartIndex;
    123      }
    124      size_t EndIndex() const {
    125        MOZ_ASSERT(IsIndexesSet());
    126        return *mEndIndex;
    127      }
    128 
    129      uint32_t StartOffsetInTextInBlock() const {
    130        MOZ_ASSERT(IsSet());
    131        return *mStartOffsetInTextInBlock;
    132      }
    133      uint32_t EndOffsetInTextInBlock() const {
    134        MOZ_ASSERT(IsSet());
    135        return *mEndOffsetInTextInBlock;
    136      }
    137      uint32_t LengthInTextInBlock() const {
    138        MOZ_ASSERT(IsSet());
    139        return *mEndOffsetInTextInBlock - *mStartOffsetInTextInBlock;
    140      }
    141 
    142      bool IsCollapsed() {
    143        return !IsSet() || (IsInSameElement() && StartOffsetInTextInBlock() ==
    144                                                     EndOffsetInTextInBlock());
    145      }
    146 
    147      bool IsIndexesSet() const {
    148        return mStartIndex.isSome() && mEndIndex.isSome();
    149      }
    150      bool IsSet() const {
    151        return IsIndexesSet() && mStartOffsetInTextInBlock.isSome() &&
    152               mEndOffsetInTextInBlock.isSome();
    153      }
    154      bool IsInSameElement() const {
    155        return IsIndexesSet() && StartIndex() == EndIndex();
    156      }
    157 
    158      void Reset() {
    159        mStartIndex.reset();
    160        mEndIndex.reset();
    161        mStartOffsetInTextInBlock.reset();
    162        mEndOffsetInTextInBlock.reset();
    163      }
    164      void SetIndex(size_t aIndex) { mEndIndex = mStartIndex = Some(aIndex); }
    165      void Set(size_t aIndex, uint32_t aOffsetInTextInBlock) {
    166        mEndIndex = mStartIndex = Some(aIndex);
    167        mStartOffsetInTextInBlock = mEndOffsetInTextInBlock =
    168            Some(aOffsetInTextInBlock);
    169      }
    170      void SetIndexes(size_t aStartIndex, size_t aEndIndex) {
    171        MOZ_DIAGNOSTIC_ASSERT(aStartIndex <= aEndIndex);
    172        mStartIndex = Some(aStartIndex);
    173        mEndIndex = Some(aEndIndex);
    174      }
    175      void Set(size_t aStartIndex, size_t aEndIndex,
    176               uint32_t aStartOffsetInTextInBlock,
    177               uint32_t aEndOffsetInTextInBlock) {
    178        MOZ_DIAGNOSTIC_ASSERT(aStartIndex <= aEndIndex);
    179        mStartIndex = Some(aStartIndex);
    180        mEndIndex = Some(aEndIndex);
    181        mStartOffsetInTextInBlock = Some(aStartOffsetInTextInBlock);
    182        mEndOffsetInTextInBlock = Some(aEndOffsetInTextInBlock);
    183      }
    184 
    185      void CollapseToStart() {
    186        MOZ_ASSERT(mStartIndex.isSome());
    187        MOZ_ASSERT(mStartOffsetInTextInBlock.isSome());
    188        mEndIndex = mStartIndex;
    189        mEndOffsetInTextInBlock = mStartOffsetInTextInBlock;
    190      }
    191 
    192     private:
    193      Maybe<size_t> mStartIndex;
    194      Maybe<size_t> mEndIndex;
    195      // Selected start and end offset in all text in a block element.
    196      Maybe<uint32_t> mStartOffsetInTextInBlock;
    197      Maybe<uint32_t> mEndOffsetInTextInBlock;
    198    };
    199    Selection mSelection;
    200  };
    201 
    202  RefPtr<dom::Document> mDocument;
    203  nsCOMPtr<nsISelectionController> mSelCon;
    204  RefPtr<EditorBase> mEditorBase;
    205  RefPtr<FilteredContentIterator> mFilteredIter;
    206  nsCOMPtr<nsIContent> mPrevTextBlock;
    207  nsCOMPtr<nsIContent> mNextTextBlock;
    208  OffsetEntryArray mOffsetTable;
    209  RefPtr<nsRange> mExtent;
    210 
    211  uint32_t mTxtSvcFilterType;
    212  IteratorStatus mIteratorStatus;
    213 
    214 protected:
    215  virtual ~TextServicesDocument() = default;
    216 
    217 public:
    218  TextServicesDocument();
    219 
    220  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    221  NS_DECL_CYCLE_COLLECTION_CLASS(TextServicesDocument)
    222 
    223  /**
    224   * Initializes the text services document to use a particular editor. The
    225   * text services document will use the DOM document and presentation shell
    226   * used by the editor.
    227   *
    228   * @param aEditor             The editor to use.
    229   */
    230  nsresult InitWithEditor(nsIEditor* aEditor);
    231 
    232  /**
    233   * Sets the range/extent over which the text services document will iterate.
    234   * Note that InitWithEditor() should have been called prior to calling this
    235   * method.  If this method is never called, the text services defaults to
    236   * iterating over the entire document.
    237   *
    238   * @param aAbstractRange      The range to use. aAbstractRange must point to a
    239   *                            valid range object.
    240   */
    241  nsresult SetExtent(const dom::AbstractRange* aAbstractRange);
    242 
    243  /**
    244   * Expands the end points of the range so that it spans complete words.  This
    245   * call does not change any internal state of the text services document.
    246   *
    247   * @param aStaticRange        [in/out] The range to be expanded/adjusted.
    248   */
    249  nsresult ExpandRangeToWordBoundaries(dom::StaticRange* aStaticRange);
    250 
    251  /**
    252   * Sets the filter type to be used while iterating over content.
    253   * This will clear the current filter type if it's not either
    254   * FILTERTYPE_NORMAL or FILTERTYPE_MAIL.
    255   *
    256   * @param aFilterType         The filter type to be used while iterating over
    257   *                            content.
    258   */
    259  nsresult SetFilterType(uint32_t aFilterType);
    260 
    261  /**
    262   * Returns the text in the current text block.
    263   *
    264   * @param aStr                [OUT] This will contain the text.
    265   */
    266  nsresult GetCurrentTextBlock(nsAString& aStr);
    267 
    268  /**
    269   * Tells the document to point to the first text block in the document.  This
    270   * method does not adjust the current cursor position or selection.
    271   */
    272  nsresult FirstBlock();
    273 
    274  enum class BlockSelectionStatus {
    275    // There is no text block (TB) in or before the selection (S).
    276    eBlockNotFound = 0,
    277    // No TB in S, but found one before/after S.
    278    eBlockOutside,
    279    // S extends beyond the start and end of TB.
    280    eBlockInside,
    281    // TB contains entire S.
    282    eBlockContains,
    283    // S begins or ends in TB but extends outside of TB.
    284    eBlockPartial,
    285  };
    286 
    287  /**
    288   * Tells the document to point to the last text block that contains the
    289   * current selection or caret.
    290   *
    291   * @param aSelectionStatus    [OUT] This will contain the text block
    292   *                            selection status.
    293   * @param aSelectionOffset    [OUT] This will contain the offset into the
    294   *                            string returned by GetCurrentTextBlock() where
    295   *                            the selection begins.
    296   * @param aLength             [OUT] This will contain the number of
    297   *                            characters that are selected in the string.
    298   */
    299  MOZ_CAN_RUN_SCRIPT
    300  nsresult LastSelectedBlock(BlockSelectionStatus* aSelStatus,
    301                             uint32_t* aSelOffset, uint32_t* aSelLength);
    302 
    303  /**
    304   * Tells the document to point to the text block before the current one.
    305   * This method will return NS_OK, even if there is no previous block.
    306   * Callers should call IsDone() to check if we have gone beyond the first
    307   * text block in the document.
    308   */
    309  nsresult PrevBlock();
    310 
    311  /**
    312   * Tells the document to point to the text block after the current one.
    313   * This method will return NS_OK, even if there is no next block. Callers
    314   * should call IsDone() to check if we have gone beyond the last text block
    315   * in the document.
    316   */
    317  nsresult NextBlock();
    318 
    319  /**
    320   * IsDone() will always set aIsDone == false unless the document contains
    321   * no text, PrevBlock() was called while the document was already pointing
    322   * to the first text block in the document, or NextBlock() was called while
    323   * the document was already pointing to the last text block in the document.
    324   *
    325   * @param aIsDone             [OUT] This will contain the result.
    326   */
    327  nsresult IsDone(bool* aIsDone);
    328 
    329  /**
    330   * SetSelection() allows the caller to set the selection based on an offset
    331   * into the string returned by GetCurrentTextBlock().  A length of zero
    332   * places the cursor at that offset. A positive non-zero length "n" selects
    333   * n characters in the string.
    334   *
    335   * @param aOffset             Offset into string returned by
    336   *                            GetCurrentTextBlock().
    337   * @param aLength             Number of characters selected.
    338   */
    339  MOZ_CAN_RUN_SCRIPT nsresult SetSelection(uint32_t aOffset, uint32_t aLength);
    340 
    341  /**
    342   * Scrolls the document so that the current selection is visible.
    343   */
    344  nsresult ScrollSelectionIntoView();
    345 
    346  /**
    347   * Deletes the text selected by SetSelection(). Calling DeleteSelection()
    348   * with nothing selected, or with a collapsed selection (cursor) does
    349   * nothing and returns NS_OK.
    350   */
    351  MOZ_CAN_RUN_SCRIPT
    352  nsresult DeleteSelection();
    353 
    354  /**
    355   * Inserts the given text at the current cursor position.  If there is a
    356   * selection, it will be deleted before the text is inserted.
    357   */
    358  MOZ_CAN_RUN_SCRIPT
    359  nsresult InsertText(const nsAString& aText);
    360 
    361  /**
    362   * nsIEditActionListener method implementations.
    363   */
    364  NS_DECL_NSIEDITACTIONLISTENER
    365 
    366  /**
    367   * Actual edit action listeners.  When you add new method here for listening
    368   * to new edit action, you need to make it called by EditorBase.
    369   * Additionally, you need to call it from proper method of
    370   * nsIEditActionListener too because if this is created not for inline
    371   * spell checker of the editor, edit actions will be notified via
    372   * nsIEditActionListener (slow path, though).
    373   */
    374  void DidDeleteContent(const nsIContent& aChildContent);
    375  void DidJoinContents(const EditorRawDOMPoint& aJoinedPoint,
    376                       const nsIContent& aRemovedContent);
    377 
    378 private:
    379  // TODO: We should get rid of this method since `aAbstractRange` has
    380  //       enough simple API to get them.
    381  static nsresult GetRangeEndPoints(const dom::AbstractRange* aAbstractRange,
    382                                    nsINode** aStartContainer,
    383                                    uint32_t* aStartOffset,
    384                                    nsINode** aEndContainer,
    385                                    uint32_t* aEndOffset);
    386 
    387  nsresult CreateFilteredContentIterator(
    388      const dom::AbstractRange* aAbstractRange,
    389      FilteredContentIterator** aFilteredIter);
    390 
    391  dom::Element* GetDocumentContentRootNode() const;
    392  already_AddRefed<nsRange> CreateDocumentContentRange();
    393  already_AddRefed<nsRange> CreateDocumentContentRootToNodeOffsetRange(
    394      nsINode* aParent, uint32_t aOffset, bool aToStart);
    395  nsresult CreateDocumentContentIterator(
    396      FilteredContentIterator** aFilteredIter);
    397 
    398  nsresult AdjustContentIterator();
    399 
    400  static nsresult FirstTextNode(FilteredContentIterator* aFilteredIter,
    401                                IteratorStatus* aIteratorStatus);
    402  static nsresult LastTextNode(FilteredContentIterator* aFilteredIter,
    403                               IteratorStatus* aIteratorStatus);
    404 
    405  static nsresult FirstTextNodeInCurrentBlock(
    406      FilteredContentIterator* aFilteredIter);
    407  static nsresult FirstTextNodeInPrevBlock(
    408      FilteredContentIterator* aFilteredIter);
    409  static nsresult FirstTextNodeInNextBlock(
    410      FilteredContentIterator* aFilteredIter);
    411 
    412  nsresult GetFirstTextNodeInPrevBlock(nsIContent** aContent);
    413  nsresult GetFirstTextNodeInNextBlock(nsIContent** aContent);
    414 
    415  static bool DidSkip(FilteredContentIterator* aFilteredIter);
    416  static void ClearDidSkip(FilteredContentIterator* aFilteredIter);
    417 
    418  static bool HasSameBlockNodeParent(dom::Text& aTextNode1,
    419                                     dom::Text& aTextNode2);
    420 
    421  MOZ_CAN_RUN_SCRIPT nsresult SetSelectionInternal(uint32_t aOffset,
    422                                                   uint32_t aLength,
    423                                                   bool aDoUpdate);
    424  MOZ_CAN_RUN_SCRIPT nsresult GetSelection(BlockSelectionStatus* aSelStatus,
    425                                           uint32_t* aSelOffset,
    426                                           uint32_t* aSelLength);
    427  MOZ_CAN_RUN_SCRIPT nsresult
    428  GetCollapsedSelection(BlockSelectionStatus* aSelStatus, uint32_t* aSelOffset,
    429                        uint32_t* aSelLength);
    430  nsresult GetUncollapsedSelection(BlockSelectionStatus* aSelStatus,
    431                                   uint32_t* aSelOffset, uint32_t* aSelLength);
    432 };
    433 
    434 }  // namespace mozilla
    435 
    436 #endif  // #ifndef mozilla_TextServicesDocument_h