tor-browser

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

IMEContentObserver.h (37452B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_IMEContentObserver_h
      8 #define mozilla_IMEContentObserver_h
      9 
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/EditorBase.h"
     12 #include "mozilla/dom/Element.h"
     13 #include "mozilla/dom/Selection.h"
     14 #include "mozilla/dom/Text.h"
     15 #include "nsCOMPtr.h"
     16 #include "nsCycleCollectionParticipant.h"
     17 #include "nsIDocShell.h"  // XXX Why does only this need to be included here?
     18 #include "nsIMutationObserver.h"
     19 #include "nsIReflowObserver.h"
     20 #include "nsIScrollObserver.h"
     21 #include "nsIWidget.h"
     22 #include "nsStubDocumentObserver.h"
     23 #include "nsStubMutationObserver.h"
     24 #include "nsThreadUtils.h"
     25 #include "nsWeakReference.h"
     26 
     27 class nsIContent;
     28 class nsINode;
     29 class nsPresContext;
     30 
     31 namespace mozilla {
     32 
     33 class EventStateManager;
     34 class TextComposition;
     35 
     36 namespace dom {
     37 class Selection;
     38 }  // namespace dom
     39 
     40 // IMEContentObserver notifies widget of any text and selection changes
     41 // in the currently focused editor
     42 class IMEContentObserver final : public nsStubMutationObserver,
     43                                 public nsIReflowObserver,
     44                                 public nsIScrollObserver,
     45                                 public nsSupportsWeakReference {
     46 public:
     47  using SelectionChangeData = widget::IMENotification::SelectionChangeData;
     48  using TextChangeData = widget::IMENotification::TextChangeData;
     49  using TextChangeDataBase = widget::IMENotification::TextChangeDataBase;
     50  using IMENotificationRequests = widget::IMENotificationRequests;
     51  using IMEMessage = widget::IMEMessage;
     52  enum class ForRemoval : bool { No, Yes };
     53 
     54  IMEContentObserver();
     55 
     56  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     57  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IMEContentObserver,
     58                                           nsIReflowObserver)
     59  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
     60  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
     61  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
     62  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
     63  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
     64  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
     65  NS_DECL_NSIREFLOWOBSERVER
     66 
     67  // nsIScrollObserver
     68  virtual void ScrollPositionChanged() override;
     69 
     70  /**
     71   * OnSelectionChange() is called when selection is changed in the editor.
     72   */
     73  void OnSelectionChange(dom::Selection& aSelection);
     74 
     75  MOZ_CAN_RUN_SCRIPT bool OnMouseButtonEvent(nsPresContext& aPresContext,
     76                                             WidgetMouseEvent& aMouseEvent);
     77 
     78  MOZ_CAN_RUN_SCRIPT nsresult
     79  HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
     80 
     81  /**
     82   * Handle eSetSelection event if and only if aEvent changes selection offset
     83   * or length.  Doing nothing when selection range is same is important to
     84   * honer users' intention or web app's intention because ContentEventHandler
     85   * does not support to put range boundaries to arbitrary side of element
     86   * boundaries.  E.g., `<b>bold[]</b> normal` vs. `<b>bold</b>[] normal`.
     87   * Note that this compares given range with selection cache which has been
     88   * notified IME via widget.  Therefore, the caller needs to guarantee that
     89   * pending notifications should've been flushed.  If you test this, you need
     90   * to wait 2 animation frames before sending eSetSelection event.
     91   */
     92  MOZ_CAN_RUN_SCRIPT nsresult MaybeHandleSelectionEvent(
     93      nsPresContext* aPresContext, WidgetSelectionEvent* aEvent);
     94 
     95  /**
     96   * Init() initializes the instance, i.e., retrieving necessary objects and
     97   * starts to observe something.
     98   * Be aware, callers of this method need to guarantee that the instance
     99   * won't be released during calling this.
    100   *
    101   * @param aWidget         The widget which can access native IME.
    102   * @param aPresContext    The PresContext which has aContent.
    103   * @param aElement        An editable element or nullptr if this will observe
    104   *                        design mode document.
    105   * @param aEditorBase     The editor which is associated with aContent.
    106   */
    107  MOZ_CAN_RUN_SCRIPT void Init(nsIWidget& aWidget, nsPresContext& aPresContext,
    108                               dom::Element* aElement, EditorBase& aEditorBase);
    109 
    110  /**
    111   * Destroy() finalizes the instance, i.e., stops observing contents and
    112   * clearing the members.
    113   * Be aware, callers of this method need to guarantee that the instance
    114   * won't be released during calling this.
    115   */
    116  void Destroy();
    117 
    118  /**
    119   * Returns false if the instance refers some objects and observing them.
    120   * Otherwise, true.
    121   */
    122  bool Destroyed() const;
    123 
    124  /**
    125   * IMEContentObserver is stored by EventStateManager during observing.
    126   * DisconnectFromEventStateManager() is called when EventStateManager stops
    127   * storing the instance.
    128   */
    129  void DisconnectFromEventStateManager();
    130 
    131  /**
    132   * MaybeReinitialize() tries to restart to observe the editor's root node.
    133   * This is useful when the editor is reframed and all children are replaced
    134   * with new node instances.
    135   * Be aware, callers of this method need to guarantee that the instance
    136   * won't be released during calling this.
    137   *
    138   * @return            Returns true if the instance is managing the content.
    139   *                    Otherwise, false.
    140   */
    141  MOZ_CAN_RUN_SCRIPT bool MaybeReinitialize(nsIWidget& aWidget,
    142                                            nsPresContext& aPresContext,
    143                                            dom::Element* aElement,
    144                                            EditorBase& aEditorBase);
    145 
    146  /**
    147   * Return true if this is observing editable content and aElement has focus.
    148   * If aElement is a text control, check if this is observing its anonymous
    149   * subtree.  Otherwise, check if this is observing the children of aElement in
    150   * the DOM tree.  If aElement is nullptr, this returns true if entire the
    151   * document is editable, e.g., in the designMode.
    152   */
    153  [[nodiscard]] bool IsObserving(const nsPresContext& aPresContext,
    154                                 const dom::Element* aElement) const;
    155 
    156  [[nodiscard]] bool IsBeingInitializedFor(const nsPresContext& aPresContext,
    157                                           const dom::Element* aElement,
    158                                           const EditorBase& aEditorBase) const;
    159  bool IsObserving(const TextComposition& aTextComposition) const;
    160  bool WasInitializedWith(const EditorBase& aEditorBase) const {
    161    return mEditorBase == &aEditorBase;
    162  }
    163  bool IsEditorHandlingEventForComposition() const;
    164  bool KeepAliveDuringDeactive() const {
    165    return mIMENotificationRequests &&
    166           mIMENotificationRequests->WantDuringDeactive();
    167  }
    168  [[nodiscard]] bool EditorIsTextEditor() const {
    169    return mEditorBase && mEditorBase->IsTextEditor();
    170  }
    171  nsIWidget* GetWidget() const { return mWidget; }
    172  void SuppressNotifyingIME();
    173  void UnsuppressNotifyingIME();
    174  nsPresContext* GetPresContext() const;
    175  nsresult GetSelectionAndRoot(dom::Selection** aSelection,
    176                               dom::Element** aRootElement) const;
    177 
    178  /**
    179   * TryToFlushPendingNotifications() should be called when pending events
    180   * should be flushed.  This tries to run the queued IMENotificationSender.
    181   * Doesn't do anything in child processes where flushing happens
    182   * asynchronously unless aAllowAsync is false.
    183   */
    184  void TryToFlushPendingNotifications(bool aAllowAsync);
    185 
    186  /**
    187   * MaybeNotifyCompositionEventHandled() posts composition event handled
    188   * notification into the pseudo queue.
    189   */
    190  void MaybeNotifyCompositionEventHandled();
    191 
    192  /**
    193   * Following methods are called when the editor:
    194   *   - an edit action handled.
    195   *   - before handling an edit action.
    196   *   - canceled handling an edit action after calling BeforeEditAction().
    197   */
    198  void OnEditActionHandled();
    199  void BeforeEditAction();
    200  void CancelEditAction();
    201 
    202  /**
    203   * Called when text control value is changed while this is not observing
    204   * mRootElement.  This is typically there is no frame for the editor (i.e.,
    205   * no proper anonymous <div> element for the editor yet) or the TextEditor
    206   * has not been created (i.e., IMEStateManager has not been reinitialized
    207   * this instance with new anonymous <div> element yet).
    208   */
    209  void OnTextControlValueChangedWhileNotObservable(const nsAString& aNewValue);
    210 
    211  /**
    212   * Return an Element if and only if this instance is observing the element.
    213   * The element is the anonymous <div> of a text control element if this is
    214   * initialized with a TextEditor.  Otherwise, the focused editing host.
    215   * If you want the text control if this is initialized with a TextEditor, use
    216   * GetObservingEditingHostOrTextControlElement() instead.
    217   */
    218  dom::Element* GetObservingElement() const {
    219    return mIsObserving ? mRootElement.get() : nullptr;
    220  }
    221 
    222  /**
    223   * Return an Element if and only if this instance is observing the element.
    224   * The element is a text control element if this is initalized with a
    225   * TextEditor.  Otherwise, the focused editing host.
    226   * If you want the anonymous <div> in the text control if this is initialized
    227   * with TextEditor, use GetObservingElement() instead.
    228   */
    229  dom::Element* GetObservingEditingHostOrTextControlElement() const {
    230    return mIsTextControl ? GetObservingTextControlElement()
    231                          : GetObservingElement();
    232  }
    233 
    234  /**
    235   * Return an element if and only if this instance is initialized with a
    236   * TextEditor and observing its anonymous <div>.
    237   */
    238  dom::Element* GetObservingTextControlElement() const {
    239    return mIsObserving && mIsTextControl
    240               ? dom::Element::FromNodeOrNull(
    241                     mRootEditableNodeOrTextControlElement)
    242               : nullptr;
    243  }
    244 
    245 private:
    246  ~IMEContentObserver() = default;
    247 
    248  enum State {
    249    eState_NotObserving,
    250    eState_Initializing,
    251    eState_StoppedObserving,
    252    eState_Observing
    253  };
    254  State GetState() const;
    255  MOZ_CAN_RUN_SCRIPT bool InitWithEditor(nsPresContext& aPresContext,
    256                                         dom::Element* aElement,
    257                                         EditorBase& aEditorBase);
    258  void OnIMEReceivedFocus();
    259  void Clear();
    260 
    261  /**
    262   * Return true if aElement is observed by this instance.
    263   */
    264  [[nodiscard]] bool IsObservingElement(const nsPresContext& aPresContext,
    265                                        const dom::Element* aElement) const;
    266 
    267  [[nodiscard]] bool IsReflowLocked() const;
    268  [[nodiscard]] bool IsSafeToNotifyIME() const;
    269  [[nodiscard]] bool IsEditorComposing() const;
    270 
    271  // Following methods are called by DocumentObserver when
    272  // beginning to update the contents and ending updating the contents.
    273  void BeginDocumentUpdate();
    274  void EndDocumentUpdate();
    275 
    276  // Following methods manages added nodes during a document change.
    277 
    278  /**
    279   * IsInDocumentChange() returns true while the DOM tree is being modified
    280   * with mozAutoDocUpdate.  E.g., it's being modified by setting innerHTML or
    281   * insertAdjacentHTML().  This returns false when user types something in
    282   * the focused editor editor.
    283   */
    284  bool IsInDocumentChange() const {
    285    return mDocumentObserver && mDocumentObserver->IsUpdating();
    286  }
    287 
    288  [[nodiscard]] bool EditorIsHandlingEditSubAction() const;
    289 
    290  void PostFocusSetNotification();
    291  void MaybeNotifyIMEOfFocusSet();
    292  void PostTextChangeNotification();
    293  void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData);
    294  void CancelNotifyingIMEOfTextChange();
    295  void PostSelectionChangeNotification();
    296  void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition,
    297                                       bool aCausedBySelectionEvent,
    298                                       bool aOccurredDuringComposition);
    299  void PostPositionChangeNotification();
    300  void MaybeNotifyIMEOfPositionChange();
    301  void CancelNotifyingIMEOfPositionChange();
    302  void PostCompositionEventHandledNotification();
    303 
    304  void ContentAdded(nsINode* aContainer, nsIContent* aFirstContent,
    305                    nsIContent* aLastContent);
    306 
    307  struct MOZ_STACK_CLASS OffsetAndLengthAdjustments {
    308    [[nodiscard]] uint32_t AdjustedOffset(uint32_t aOffset) const {
    309      MOZ_ASSERT_IF(mOffsetAdjustment < 0, aOffset >= mOffsetAdjustment);
    310      return aOffset + mOffsetAdjustment;
    311    }
    312    [[nodiscard]] uint32_t AdjustedLength(uint32_t aLength) const {
    313      MOZ_ASSERT_IF(mOffsetAdjustment < 0, aLength >= mLengthAdjustment);
    314      return aLength + mLengthAdjustment;
    315    }
    316    [[nodiscard]] uint32_t AdjustedEndOffset(uint32_t aEndOffset) const {
    317      MOZ_ASSERT_IF(mOffsetAdjustment + mLengthAdjustment < 0,
    318                    aEndOffset >= mOffsetAdjustment + mLengthAdjustment);
    319      return aEndOffset + (mOffsetAdjustment + mLengthAdjustment);
    320    }
    321 
    322    int64_t mOffsetAdjustment = 0;
    323    int64_t mLengthAdjustment = 0;
    324  };
    325 
    326  /**
    327   * Posts a text change caused by cached added content in mAddedContentCache.
    328   *
    329   * @param aOffsetOfFirstContent
    330   *                            Flattened text offset of mFirst.  This can be
    331   *                            different value from the computed value in the
    332   *                            current tree.  However, in the case,
    333   *                            aAdjustments should have the difference. If this
    334   *                            is Nothing, it's computed with the current DOM.
    335   * @param aLengthOfContentNNodes
    336   *                            Flattened text length starting from mFirst and
    337   *                            ending by end of mLast. This can be different
    338   *                            value from the computed value in the current
    339   *                            tree.  However, in the case, aAdjustments should
    340   *                            have the difference. If this is Nothing, it's
    341   *                            computed with the current DOM.
    342   * @param aAdjustments        When aOffsetOfFirstContent and/or
    343   *                            aLengthOfContentNodes are specified different
    344   *                            value(s) from the computed value(s) in the
    345   *                            current DOM, these members should have non-zero
    346   *                            values of the differences.
    347   */
    348  void NotifyIMEOfCachedConsecutiveNewNodes(
    349      const char* aCallerName,
    350      const Maybe<uint32_t>& aOffsetOfFirstContent = Nothing(),
    351      const Maybe<uint32_t>& aLengthOfContentNNodes = Nothing(),
    352      const OffsetAndLengthAdjustments& aAdjustments =
    353          OffsetAndLengthAdjustments{0, 0});
    354 
    355  void ObserveEditableNode();
    356  /**
    357   *  NotifyIMEOfBlur() notifies IME of blur.
    358   */
    359  void NotifyIMEOfBlur();
    360  /**
    361   *  UnregisterObservers() unregisters all listeners and observers.
    362   */
    363  void UnregisterObservers();
    364  void FlushMergeableNotifications();
    365  bool NeedsTextChangeNotification() const {
    366    return mIMENotificationRequests &&
    367           mIMENotificationRequests->WantTextChange();
    368  }
    369  bool NeedsPositionChangeNotification() const {
    370    return mIMENotificationRequests &&
    371           mIMENotificationRequests->WantPositionChanged();
    372  }
    373  void ClearPendingNotifications() {
    374    mNeedsToNotifyIMEOfFocusSet = false;
    375    mNeedsToNotifyIMEOfTextChange = false;
    376    mNeedsToNotifyIMEOfSelectionChange = false;
    377    mNeedsToNotifyIMEOfPositionChange = false;
    378    mNeedsToNotifyIMEOfCompositionEventHandled = false;
    379    mTextChangeData.Clear();
    380  }
    381  bool NeedsToNotifyIMEOfSomething() const {
    382    return mNeedsToNotifyIMEOfFocusSet || mNeedsToNotifyIMEOfTextChange ||
    383           mNeedsToNotifyIMEOfSelectionChange ||
    384           mNeedsToNotifyIMEOfPositionChange ||
    385           mNeedsToNotifyIMEOfCompositionEventHandled;
    386  }
    387 
    388  /**
    389   * UpdateSelectionCache() updates mSelectionData with the latest selection.
    390   * This should be called only when IsSafeToNotifyIME() returns true.
    391   */
    392  MOZ_CAN_RUN_SCRIPT bool UpdateSelectionCache(bool aRequireFlush = true);
    393 
    394  /**
    395   * Return the document node if aNode is in the design mode.  Return the
    396   * editing host of aNode if and only if it's editable.  Otherwise, nullptr.
    397   */
    398  [[nodiscard]] static nsINode* GetMostDistantInclusiveEditableAncestorNode(
    399      const nsPresContext& aPresContext, const dom::Element* aElement);
    400 
    401  nsCOMPtr<nsIWidget> mWidget;
    402  // mFocusedWidget has the editor observed by the instance.  E.g., if the
    403  // focused editor is in XUL panel, this should be the widget of the panel.
    404  // On the other hand, mWidget is its parent which handles IME.
    405  nsCOMPtr<nsIWidget> mFocusedWidget;
    406  RefPtr<dom::Selection> mSelection;
    407  // The anonymous <div> element if mEditorBase is a TextEditor or an editing
    408  // host if mEditorBase is an HTMLEditor.
    409  RefPtr<dom::Element> mRootElement;
    410  // If it's in the design mode, this is set to the document node.
    411  // If it's initialized with a TextEditor, this is the text control element.
    412  // Otherwise, this is an editing host element.
    413  nsCOMPtr<nsINode> mRootEditableNodeOrTextControlElement;
    414  nsCOMPtr<nsIDocShell> mDocShell;
    415  RefPtr<EditorBase> mEditorBase;
    416 
    417  /**
    418   * Helper classes to notify IME.
    419   */
    420 
    421  class AChangeEvent : public Runnable {
    422   protected:
    423    enum ChangeEventType {
    424      eChangeEventType_Focus,
    425      eChangeEventType_Selection,
    426      eChangeEventType_Text,
    427      eChangeEventType_Position,
    428      eChangeEventType_CompositionEventHandled
    429    };
    430 
    431    explicit AChangeEvent(const char* aName,
    432                          IMEContentObserver* aIMEContentObserver)
    433        : Runnable(aName),
    434          mIMEContentObserver(do_GetWeakReference(
    435              static_cast<nsIReflowObserver*>(aIMEContentObserver))) {
    436      MOZ_ASSERT(aIMEContentObserver);
    437    }
    438 
    439    already_AddRefed<IMEContentObserver> GetObserver() const {
    440      nsCOMPtr<nsIReflowObserver> observer =
    441          do_QueryReferent(mIMEContentObserver);
    442      return observer.forget().downcast<IMEContentObserver>();
    443    }
    444 
    445    nsWeakPtr mIMEContentObserver;
    446 
    447    /**
    448     * CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
    449     */
    450    bool CanNotifyIME(ChangeEventType aChangeEventType) const;
    451 
    452    /**
    453     * IsSafeToNotifyIME() checks if it's safe to noitify IME.
    454     */
    455    bool IsSafeToNotifyIME(ChangeEventType aChangeEventType) const;
    456  };
    457 
    458  class IMENotificationSender : public AChangeEvent {
    459   public:
    460    explicit IMENotificationSender(IMEContentObserver* aIMEContentObserver)
    461        : AChangeEvent("IMENotificationSender", aIMEContentObserver),
    462          mIsRunning(false) {}
    463    MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
    464 
    465    void Dispatch(nsIDocShell* aDocShell);
    466 
    467   private:
    468    MOZ_CAN_RUN_SCRIPT void SendFocusSet();
    469    MOZ_CAN_RUN_SCRIPT void SendSelectionChange();
    470    void SendTextChange();
    471    void SendPositionChange();
    472    void SendCompositionEventHandled();
    473 
    474    bool mIsRunning;
    475  };
    476 
    477  // mQueuedSender is, it was put into the event queue but not run yet.
    478  RefPtr<IMENotificationSender> mQueuedSender;
    479 
    480  /**
    481   * IMEContentObserver is a mutation observer of mRootContent.  However,
    482   * it needs to know the beginning of content changes and end of it too for
    483   * reducing redundant computation of text offset with ContentEventHandler.
    484   * Therefore, it needs helper class to listen only them since if
    485   * both mutations were observed by IMEContentObserver directly, each
    486   * methods need to check if the changing node is in mRootContent but it's
    487   * too expensive.
    488   */
    489  class DocumentObserver final : public nsStubDocumentObserver {
    490   public:
    491    DocumentObserver() = delete;
    492    explicit DocumentObserver(IMEContentObserver& aIMEContentObserver)
    493        : mIMEContentObserver(&aIMEContentObserver), mDocumentUpdating(0) {
    494      SetEnabledCallbacks(nsIMutationObserver::kBeginUpdate |
    495                          nsIMutationObserver::kEndUpdate);
    496    }
    497 
    498    NS_DECL_CYCLE_COLLECTION_CLASS(DocumentObserver)
    499    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    500    NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
    501    NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
    502 
    503    void Observe(dom::Document*);
    504    void StopObserving();
    505    void Destroy();
    506 
    507    bool Destroyed() const { return !mIMEContentObserver; }
    508    bool IsObserving() const { return mDocument != nullptr; }
    509    bool IsUpdating() const { return mDocumentUpdating != 0; }
    510 
    511   private:
    512    virtual ~DocumentObserver() { Destroy(); }
    513 
    514    RefPtr<IMEContentObserver> mIMEContentObserver;
    515    RefPtr<dom::Document> mDocument;
    516    uint32_t mDocumentUpdating;
    517  };
    518  RefPtr<DocumentObserver> mDocumentObserver;
    519 
    520  /**
    521   * FlatTextCache stores length of flattened text starting from start of
    522   * the observing node (typically editing host or the anonymous <div> of
    523   * TextEditor) to:
    524   * - end of mContent if it's set (IsCachingToEndOfContent() returns true)
    525   * - before first content of mContainerNode if mContent is not set
    526   * (IsCachingToStartOfContainer() returns true).  In this case, the text
    527   * length includes a line break length which is caused by the open tag of
    528   * mContainerNode if and only if it's an element node and the open tag causes
    529   * a line break.
    530   */
    531  struct FlatTextCache {
    532   public:
    533    explicit FlatTextCache(const char* aInstanceName)
    534        : mInstanceName(aInstanceName) {}
    535 
    536    void Clear(const char* aCallerName);
    537 
    538    [[nodiscard]] bool HasCache() const { return !!mContainerNode; }
    539 
    540    /**
    541     * Return true if mFlatTextLength caches flattened text length starting from
    542     * start of the observing node to the end of mContent.
    543     */
    544    [[nodiscard]] bool IsCachingToEndOfContent() const {
    545      return mContainerNode && mContent;
    546    }
    547 
    548    /**
    549     * Return true if mFlatTextLength caches flattened text length starting from
    550     * start of the observing node to the start of mContainerNode.  Note that if
    551     * mContainerNode is an element and whose open tag causes a line break,
    552     * mFlatTextLength includes the line break length too.
    553     */
    554    [[nodiscard]] bool IsCachingToStartOfContainer() const {
    555      return mContainerNode && !mContent;
    556    }
    557 
    558    /**
    559     * Compute flattened text length starting from first content of aRootElement
    560     * and ending at end of aContent.
    561     *
    562     * @param aContent          This will be set to mContent which points the
    563     *                          last child content node which participates in
    564     *                          the computed mFlatTextLength.
    565     * @param aRootElement      The root element of the editor, i.e., editing
    566     *                          host or the anonymous <div> in a text control.
    567     *                          (This is required to suppress
    568     *                          ContentEventHandler to generate a line break
    569     *                          caused by open tag of the editable root element
    570     *                          due to not editable.  Therefore, we need to call
    571     *                          ContentEventHandler methods with this.)
    572     */
    573    [[nodiscard]] nsresult ComputeAndCacheFlatTextLengthBeforeEndOfContent(
    574        const char* aCallerName, const nsIContent& aContent,
    575        const dom::Element* aRootElement);
    576 
    577    void CacheFlatTextLengthBeforeEndOfContent(
    578        const char* aCallerName, const nsIContent& aContent,
    579        uint32_t aFlatTextLength, const dom::Element* aRootElement);
    580 
    581    /**
    582     * Compute flattened text length starting from first content of aRootElement
    583     * and ending at start of the first content of aContainer.
    584     *
    585     * @param aContainer        This will be set to mContainer and mContent will
    586     *                          be set to nullptr.
    587     * @param aRootElement      The root element of the editor, i.e., editing
    588     *                          host or the anonymous <div> in a text control.
    589     *                          (This is required to suppress
    590     *                          ContentEventHandler to generate a line break
    591     *                          caused by open tag of the editable root element
    592     *                          due to not editable.  Therefore, we need to call
    593     *                          ContentEventHandler methods with this.)
    594     */
    595    [[nodiscard]] nsresult ComputeAndCacheFlatTextLengthBeforeFirstContent(
    596        const char* aCallerName, const nsINode& aContainer,
    597        const dom::Element* aRootElement);
    598 
    599    void CacheFlatTextLengthBeforeFirstContent(
    600        const char* aCallerName, const nsINode& aContainer,
    601        uint32_t aFlatTextLength, const dom::Element* aRootElement);
    602 
    603    /**
    604     * Return flattened text length of aContent.  I.e., the length includes a
    605     * line break caused by the open tag of aContent if it's an element node.
    606     *
    607     * @param aRemovingContent  The content node which is being removed.
    608     * @param aRootElement      The root element of the editor, i.e., editing
    609     *                          host or the anonymous <div> in a text control.
    610     *                          For avoiding to generate a redundant line break
    611     *                          at open tag of this element, this is required
    612     *                          to call methods of ContentEventHandler.
    613     * @param aForRemoval       Whether aContent is about to be removed.
    614     */
    615    [[nodiscard]] static Result<uint32_t, nsresult> ComputeTextLengthOfContent(
    616        const nsIContent& aContent, const dom::Element* aRootElement,
    617        ForRemoval = ForRemoval::No);
    618 
    619    /**
    620     * Return flattened text length of starting from first content of
    621     * aRootElement and ending at before aContent (if ContentEventHandler
    622     * generates a line break at open tag of aContent, the result does not
    623     * contain the line break length).
    624     *
    625     * @param aContent          The content node which is immediately after a
    626     *                          content which you want to compute the flattened
    627     *                          text length before end of it.
    628     * @param aRootElement      The root element of the editor, i.e., editing
    629     *                          host or the anonymous <div> in a text control.
    630     *                          For avoiding to generate a redundant line break
    631     *                          at open tag of this element, this is required
    632     *                          to call methods of ContentEventHandler.
    633     */
    634    [[nodiscard]] static Result<uint32_t, nsresult>
    635    ComputeTextLengthBeforeContent(const nsIContent& aContent,
    636                                   const dom::Element* aRootElement);
    637 
    638    /**
    639     * Return flattened text length starting from first content of aRootElement
    640     * and ending at start of the first content of aContainer.  This means that
    641     * if ContentEventHandler generates a line break at the open tag of
    642     * aContainer, the result includes the line break length.
    643     * NOTE: The difference from ComputeTextLengthBeforeContent() is, result of
    644     * this method includes a line break caused by the open tag of aContainer
    645     * if and only if it's an element node and ContentEventHandler generates
    646     * a line break for its open tag.
    647     *
    648     * @param aContainer        The container node which you want to compute the
    649     *                          flattened text length before the first content
    650     *                          of.
    651     * @param aRootElement      The root element of the editor, i.e., editing
    652     *                          host or the anonymous <div> in a text control.
    653     *                          For avoiding to generate a redundant line break
    654     *                          at open tag of this element, this is required
    655     *                          to call methods of ContentEventHandler.
    656     */
    657    [[nodiscard]] static Result<uint32_t, nsresult>
    658    ComputeTextLengthBeforeFirstContentOf(const nsINode& aContainer,
    659                                          const dom::Element* aRootElement);
    660 
    661    /**
    662     * Return flattened text length of starting from start of aStartContent and
    663     * ending at end of aEndContent.  If ContentEventHandler generates a line
    664     * break at open tag of aStartContent, the result includes the line break
    665     * length.
    666     *
    667     * @param aStartContent     The first content node of consecutive nodes
    668     *                          which you want to compute flattened text length
    669     *                          starting from.
    670     * @param aEndContent       The last content node of consecutive nodes
    671     *                          which you want to compute flattened text length
    672     *                          ending at.
    673     * @param aRootElement      The root element of the editor, i.e., editing
    674     *                          host or the anonymous <div> in a text control.
    675     *                          For avoiding to generate a redundant line break
    676     *                          at open tag of this element, this is required
    677     *                          to call methods of ContentEventHandler.
    678     */
    679    [[nodiscard]] static Result<uint32_t, nsresult>
    680    ComputeTextLengthStartOfContentToEndOfContent(
    681        const nsIContent& aStartContent, const nsIContent& aEndContent,
    682        const dom::Element* aRootElement);
    683 
    684    [[nodiscard]] uint32_t GetFlatTextLength() const { return mFlatTextLength; }
    685 
    686    /**
    687     * Return text length if it's exactly cached or can compute it quickly from
    688     * the cached data.  aContent must not be new node which is inserted before
    689     * mContent because the cached text length does not include the text length
    690     * of aContent in such case.
    691     */
    692    [[nodiscard]] Maybe<uint32_t> GetFlatTextLengthBeforeContent(
    693        const nsIContent& aContent, const dom::Element* aRootElement,
    694        ForRemoval = ForRemoval::No) const;
    695 
    696    /**
    697     * Return text length before aFirstContent if it's exactly cached or can
    698     * compute it quickly from the caching data.  This is called when the nodes
    699     * between aFirstContent and aLastContent are inserted into the tree.
    700     */
    701    [[nodiscard]] Maybe<uint32_t> GetFlatTextOffsetOnInsertion(
    702        const nsIContent& aFirstContent, const nsIContent& aLastContent,
    703        const dom::Element* aRootElement) const;
    704 
    705    /**
    706     * This works only in the debug build and
    707     * test.ime_content_observer.assert_valid_cache pref is enabled.  This
    708     * checks with expensive computation, therefore, the pref is enabled only
    709     * when running automated tests for editors.
    710     */
    711    void AssertValidCache(const dom::Element* aRootElement) const;
    712 
    713    /**
    714     * Called when content nodes from aFirstContent to aLastContent are added.
    715     * aAddedFlatTextLength may be flattened text length from start of
    716     * aFirstContent to end of aLastContent if it's computed by the caller.
    717     * Note that aFirstContent and aLastContent can be in different container
    718     * nodes, but this method is currently called with (maybe indirect) siblings
    719     * in the same container.
    720     */
    721    void ContentAdded(const char* aCallerName, const nsIContent& aFirstContent,
    722                      const nsIContent& aLastContent,
    723                      const Maybe<uint32_t>& aAddedFlatTextLength,
    724                      const dom::Element* aRootElement);
    725 
    726    /**
    727     * Called when aContent will be removed. aFlatTextLengthOfContent is
    728     * flattened text length of aContent.
    729     */
    730    void ContentWillBeRemoved(const nsIContent& aContent,
    731                              uint32_t aFlatTextLengthOfContent,
    732                              const dom::Element* aRootElement);
    733 
    734   public:
    735    // mContainerNode is parent node of mContent when it's cached.
    736    nsCOMPtr<nsINode> mContainerNode;
    737    // mContent points to the last child which participates in the current
    738    // mFlatTextLength.  If this is nullptr, mFlatTextLength means that it
    739    // length before the first content of mContainerNode, i.e., including the
    740    // line break of that caused by the open tag of mContainerNode.
    741    nsCOMPtr<nsIContent> mContent;
    742 
    743   private:
    744    // Length of flat text generated from contents between the start of the
    745    // observing node (typically editing host or the anonymous <div> of
    746    // TextEditor) and the end of mContent.
    747    uint32_t mFlatTextLength = 0;
    748    MOZ_DEFINE_DBG(FlatTextCache, mContainerNode, mContent, mFlatTextLength);
    749 
    750    const char* mInstanceName;
    751  };
    752 
    753  friend std::ostream& operator<<(std::ostream& aStream,
    754                                  const FlatTextCache& aCache);
    755 
    756  // mEndOfAddedTextCache caches text length from the start of the observing
    757  // node to the end of the last added content only while an edit action is
    758  // being handled by the editor and no other mutation (e.g., removing node)
    759  // occur.
    760  FlatTextCache mEndOfAddedTextCache = FlatTextCache("mEndOfAddedTextCache");
    761  // mStartOfRemovingTextRangeCache caches text length from the start of the
    762  // observing node to the start of the last removed content only while an edit
    763  // action is being handled by the editor and no other mutation (e.g., adding
    764  // node) occur.  In other words, this caches text length before end of
    765  // mContent or before first child of mContainerNode.
    766  FlatTextCache mStartOfRemovingTextRangeCache =
    767      FlatTextCache("mStartOfRemovingTextRangeCache");
    768 
    769  /**
    770   * Caches the DOM node ranges with storing the first node and the last node.
    771   * This is designed for mAddedContentCache.  See comment at declaration of it
    772   * for the detail.
    773   */
    774  struct AddedContentCache {
    775    /**
    776     * Clear the range. Callers should call this with __FUNCTION__ which will be
    777     * used to log which caller did it.
    778     */
    779    void Clear(const char* aCallerName);
    780 
    781    [[nodiscard]] bool HasCache() const { return mFirst && mLast; }
    782 
    783    /**
    784     * Return true if aFirstContent and aLastContent can be merged into the
    785     * cached range.  This should be called only when the instance caches
    786     * something.
    787     */
    788    [[nodiscard]] bool CanMergeWith(const nsIContent& aFirstContent,
    789                                    const nsIContent& aLastContent,
    790                                    const dom::Element* aRootElement) const;
    791 
    792    /**
    793     * Return true if aContent is in the cached range.  aContent can be not
    794     * a child of the common container of the caching range.
    795     */
    796    [[nodiscard]] bool IsInRange(const nsIContent& aContent,
    797                                 const dom::Element* aRootElement) const;
    798 
    799    /**
    800     * Try to cache the range represented by aFirstContent and aLastContent.
    801     * If there is a cache, this will extend the caching range to contain
    802     * the new range.
    803     *
    804     * @return          true if cached, otherwise, false.
    805     */
    806    bool TryToCache(const nsIContent& aFirstContent,
    807                    const nsIContent& aLastContent,
    808                    const dom::Element* aRootElement);
    809 
    810    /**
    811     * Compute offset and length of the cached range before the nodes between
    812     * aNewFirstContent and aNewLastContent are inserted.
    813     *
    814     * @return The first one is offset, the other is length.
    815     */
    816    [[nodiscard]] Result<std::pair<uint32_t, uint32_t>, nsresult>
    817    ComputeFlatTextRangeBeforeInsertingNewContent(
    818        const nsIContent& aNewFirstContent, const nsIContent& aNewLastContent,
    819        const dom::Element* aRootElement,
    820        OffsetAndLengthAdjustments& aDifferences) const;
    821 
    822    MOZ_DEFINE_DBG(AddedContentCache, mFirst, mLast);
    823 
    824    nsCOMPtr<nsIContent> mFirst;
    825    nsCOMPtr<nsIContent> mLast;
    826  };
    827 
    828  // Caches the first node and the last node of new inserted nodes while editor
    829  // handles an editing command/operation.  Therefore, the range is always in
    830  // the same container node.  So, the range means that the direct siblings
    831  // between the first node and the last node are the inserted nodes, but not
    832  // yet post a text change notification.
    833  // FYI: This is cleared when editor ends handling current edit
    834  // operation/command.  Therefore, the strong pointers in this member don't
    835  // need to be added to the cycle collection.
    836  AddedContentCache mAddedContentCache;
    837 
    838  TextChangeData mTextChangeData;
    839 
    840  // mSelectionData is the last selection data which was notified.  The
    841  // selection information is modified by UpdateSelectionCache().  The reason
    842  // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
    843  SelectionChangeData mSelectionData;
    844 
    845  EventStateManager* mESM = nullptr;
    846 
    847  const IMENotificationRequests* mIMENotificationRequests = nullptr;
    848  int64_t mPreCharacterDataChangeLength = -1;
    849  uint32_t mSuppressNotifications = 0;
    850 
    851  // If the observing editor is a text control's one, this is set to the value
    852  // length.
    853  uint32_t mTextControlValueLength = 0;
    854 
    855  // mSendingNotification is a notification which is now sending from
    856  // IMENotificationSender.  When the value is NOTIFY_IME_OF_NOTHING, it's
    857  // not sending any notification.
    858  IMEMessage mSendingNotification = widget::NOTIFY_IME_OF_NOTHING;
    859 
    860  bool mIsObserving = false;
    861  bool mIsTextControl = false;
    862  bool mIMEHasFocus = false;
    863  bool mNeedsToNotifyIMEOfFocusSet = false;
    864  bool mNeedsToNotifyIMEOfTextChange = false;
    865  bool mNeedsToNotifyIMEOfSelectionChange = false;
    866  bool mNeedsToNotifyIMEOfPositionChange = false;
    867  bool mNeedsToNotifyIMEOfCompositionEventHandled = false;
    868  // mIsHandlingQueryContentEvent is true when IMEContentObserver is handling
    869  // WidgetQueryContentEvent with ContentEventHandler.
    870  bool mIsHandlingQueryContentEvent = false;
    871 };
    872 
    873 }  // namespace mozilla
    874 
    875 #endif  // mozilla_IMEContentObserver_h