LCOV - code coverage report
Current view: top level - dom/html - nsTextEditorState.h (source / functions) Hit Total Coverage
Test: output.info Lines: 69 80 86.2 %
Date: 2017-07-14 16:53:18 Functions: 26 32 81.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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 nsTextEditorState_h__
       8             : #define nsTextEditorState_h__
       9             : 
      10             : #include "nsString.h"
      11             : #include "nsITextControlElement.h"
      12             : #include "nsITextControlFrame.h"
      13             : #include "nsCycleCollectionParticipant.h"
      14             : #include "mozilla/dom/Element.h"
      15             : #include "mozilla/Attributes.h"
      16             : #include "mozilla/Maybe.h"
      17             : #include "mozilla/TextEditor.h"
      18             : #include "mozilla/WeakPtr.h"
      19             : #include "mozilla/dom/HTMLInputElementBinding.h"
      20             : #include "mozilla/dom/Nullable.h"
      21             : 
      22             : class nsTextInputListener;
      23             : class nsTextControlFrame;
      24             : class nsTextInputSelectionImpl;
      25             : class nsAnonDivObserver;
      26             : class nsISelectionController;
      27             : class nsFrameSelection;
      28             : class nsITextControlElement;
      29             : class nsFrame;
      30             : 
      31             : namespace mozilla {
      32             : 
      33             : class ErrorResult;
      34             : 
      35             : namespace dom {
      36             : class HTMLInputElement;
      37             : } // namespace dom
      38             : } // namespace mozilla
      39             : 
      40             : /**
      41             :  * nsTextEditorState is a class which is responsible for managing the state of
      42             :  * plaintext controls.  This currently includes the following HTML elements:
      43             :  *   <input type=text>
      44             :  *   <input type=password>
      45             :  *   <textarea>
      46             :  * and also XUL controls such as <textbox> which use one of these elements behind
      47             :  * the scenes.
      48             :  *
      49             :  * This class is held as a member of HTMLInputElement and nsHTMLTextAreaElement.
      50             :  * The public functions in this class include the public APIs which content/ uses.
      51             :  * Layout code uses the nsITextControlElement interface to invoke functions on this
      52             :  * class.
      53             :  *
      54             :  * The design motivation behind this class is maintaining all of the things which
      55             :  * collectively are considered the "state" of the text control in a single location.
      56             :  * This state includes several things:
      57             :  *
      58             :  *  * The control's value.  This value is stored in the mValue member, and is only
      59             :  *    used when there is no frame for the control, or when the editor object has
      60             :  *    not been initialized yet.
      61             :  *
      62             :  *  * The control's associated frame.  This value is stored in the mBoundFrame member.
      63             :  *    A text control might never have an associated frame during its life cycle,
      64             :  *    or might have several different ones, but at any given moment in time there is
      65             :  *    a maximum of 1 bound frame to each text control.
      66             :  *
      67             :  *  * The control's associated editor.  This value is stored in the mEditor member.
      68             :  *    An editor is initilized for the control only when necessary (that is, when either
      69             :  *    the user is about to interact with the text control, or when some other code
      70             :  *    needs to access the editor object.  Without a frame bound to the control, an
      71             :  *    editor is never initialzied.  Once initialized, the editor might outlive the frame,
      72             :  *    in which case the same editor will be used if a new frame gets bound to the
      73             :  *    text control.
      74             :  *
      75             :  *  * The anonymous content associated with the text control's frame, including the
      76             :  *    value div (the DIV element responsible for holding the value of the text control)
      77             :  *    and the placeholder div (the DIV element responsible for holding the placeholder
      78             :  *    value of the text control.)  These values are stored in the mRootNode and
      79             :  *    mPlaceholderDiv members, respectively.  They will be created when a
      80             :  *    frame is bound to the text control.  They will be destroyed when the frame is
      81             :  *    unbound from the object.  We could try and hold on to the anonymous content
      82             :  *    between different frames, but unfortunately that is not currently possible
      83             :  *    because they are not unbound from the document in time.
      84             :  *
      85             :  *  * The frame selection controller.  This value is stored in the mSelCon member.
      86             :  *    The frame selection controller is responsible for maintaining the selection state
      87             :  *    on a frame.  It is created when a frame is bound to the text control element,
      88             :  *    and will be destroy when the frame is being unbound from the text control element.
      89             :  *    It is created alongside with the frame selection object which is stored in the
      90             :  *    mFrameSel member.
      91             :  *
      92             :  *  * The editor text listener.  This value is stored in the mTextListener member.
      93             :  *    Its job is to listen to selection and keyboard events, and act accordingly.
      94             :  *    It is created when an a frame is first bound to the control, and will be destroyed
      95             :  *    when the frame is unbound from the text control element.
      96             :  *
      97             :  *  * The editor's cached value.  This value is stored in the mCachedValue member.
      98             :  *    It is used to improve the performance of append operations to the text
      99             :  *    control.  A mutation observer stored in the mMutationObserver has the job of
     100             :  *    invalidating this cache when the anonymous contect containing the value is
     101             :  *    changed.
     102             :  *
     103             :  *  * The editor's cached selection properties.  These vales are stored in the
     104             :  *    mSelectionProperties member, and include the selection's start, end and
     105             :  *    direction. They are only used when there is no frame available for the
     106             :  *    text field.
     107             :  *
     108             :  *
     109             :  * As a general rule, nsTextEditorState objects own the value of the text control, and any
     110             :  * attempt to retrieve or set the value must be made through those objects.  Internally,
     111             :  * the value can be represented in several different ways, based on the state the control is
     112             :  * in.
     113             :  *
     114             :  *   * When the control is first initialized, its value is equal to the default value of
     115             :  *     the DOM node.  For <input> text controls, this default value is the value of the
     116             :  *     value attribute.  For <textarea> elements, this default value is the value of the
     117             :  *     text node children of the element.
     118             :  *
     119             :  *   * If the value has been changed through the DOM node (before the editor for the object
     120             :  *     is initialized), the value is stored as a simple string inside the mValue member of
     121             :  *     the nsTextEditorState object.
     122             :  *
     123             :  *   * If an editor has been initialized for the control, the value is set and retrievd via
     124             :  *     the nsIPlaintextEditor interface, and is internally managed by the editor as the
     125             :  *     native anonymous content tree attached to the control's frame.
     126             :  *
     127             :  *   * If the text editor state object is unbound from the control's frame, the value is
     128             :  *     transferred to the mValue member variable, and will be managed there until a new
     129             :  *     frame is bound to the text editor state object.
     130             :  */
     131             : 
     132             : class RestoreSelectionState;
     133             : 
     134             : class nsTextEditorState : public mozilla::SupportsWeakPtr<nsTextEditorState> {
     135             : public:
     136          12 :   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsTextEditorState)
     137             :   explicit nsTextEditorState(nsITextControlElement* aOwningElement);
     138             :   static nsTextEditorState*
     139             :   Construct(nsITextControlElement* aOwningElement,
     140             :             nsTextEditorState** aReusedState);
     141             :   ~nsTextEditorState();
     142             : 
     143             :   void Traverse(nsCycleCollectionTraversalCallback& cb);
     144             :   void Unlink();
     145             : 
     146           1 :   void PrepareForReuse()
     147             :   {
     148           1 :     Unlink();
     149           1 :     mValue.reset();
     150           1 :     mCachedValue.Truncate();
     151           1 :     mValueBeingSet.Truncate();
     152           1 :     mTextCtrlElement = nullptr;
     153           1 :     MOZ_ASSERT(!mMutationObserver);
     154           1 :   }
     155             : 
     156             :   mozilla::TextEditor* GetTextEditor();
     157             :   nsISelectionController* GetSelectionController() const;
     158             :   nsFrameSelection* GetConstFrameSelection();
     159             :   nsresult BindToFrame(nsTextControlFrame* aFrame);
     160             :   void UnbindFromFrame(nsTextControlFrame* aFrame);
     161             :   nsresult PrepareEditor(const nsAString *aValue = nullptr);
     162             :   void InitializeKeyboardEventListeners();
     163             : 
     164             :   enum SetValueFlags
     165             :   {
     166             :     // The call is for internal processing.
     167             :     eSetValue_Internal              = 0,
     168             :     // The value is changed by a call of setUserInput() from chrome.
     169             :     eSetValue_BySetUserInput        = 1 << 0,
     170             :     // The value is changed by changing value attribute of the element or
     171             :     // something like setRangeText().
     172             :     eSetValue_ByContent             = 1 << 1,
     173             :     // Whether the value change should be notified to the frame/contet nor not.
     174             :     eSetValue_Notify                = 1 << 2,
     175             :     // Whether to move the cursor to end of the value (in the case when we have
     176             :     // cached selection offsets), in the case when the value has changed.  If
     177             :     // this is not set, the cached selection offsets will simply be clamped to
     178             :     // be within the length of the new value.  In either case, if the value has
     179             :     // not changed the cursor won't move.
     180             :     eSetValue_MoveCursorToEndIfValueChanged = 1 << 3,
     181             :   };
     182             :   MOZ_MUST_USE bool SetValue(const nsAString& aValue,
     183             :                              const nsAString* aOldValue,
     184             :                              uint32_t aFlags);
     185           2 :   MOZ_MUST_USE bool SetValue(const nsAString& aValue,
     186             :                              uint32_t aFlags)
     187             :   {
     188           2 :     return SetValue(aValue, nullptr, aFlags);
     189             :   }
     190             :   void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
     191             :   bool HasNonEmptyValue();
     192             :   // The following methods are for textarea element to use whether default
     193             :   // value or not.
     194             :   // XXX We might have to add assertion when it is into editable,
     195             :   // or reconsider fixing bug 597525 to remove these.
     196           0 :   void EmptyValue() { if (mValue) mValue->Truncate(); }
     197           0 :   bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
     198             : 
     199             :   nsresult CreatePlaceholderNode();
     200             :   nsresult CreatePreviewNode();
     201             :   mozilla::dom::Element* CreateEmptyDivNode();
     202             : 
     203          25 :   mozilla::dom::Element* GetRootNode() {
     204          25 :     return mRootNode;
     205             :   }
     206         106 :   mozilla::dom::Element* GetPlaceholderNode() {
     207         106 :     return mPlaceholderDiv;
     208             :   }
     209          85 :   mozilla::dom::Element* GetPreviewNode() {
     210          85 :     return mPreviewDiv;
     211             :   }
     212             : 
     213          21 :   bool IsSingleLineTextControl() const {
     214          21 :     return mTextCtrlElement->IsSingleLineTextControl();
     215             :   }
     216             :   bool IsTextArea() const {
     217             :     return mTextCtrlElement->IsTextArea();
     218             :   }
     219           6 :   bool IsPlainTextControl() const {
     220           6 :     return mTextCtrlElement->IsPlainTextControl();
     221             :   }
     222           4 :   bool IsPasswordTextControl() const {
     223           4 :     return mTextCtrlElement->IsPasswordTextControl();
     224             :   }
     225             :   int32_t GetCols() {
     226             :     return mTextCtrlElement->GetCols();
     227             :   }
     228           5 :   int32_t GetWrapCols() {
     229           5 :     return mTextCtrlElement->GetWrapCols();
     230             :   }
     231             :   int32_t GetRows() {
     232             :     return mTextCtrlElement->GetRows();
     233             :   }
     234             : 
     235             :   void UpdateOverlayTextVisibility(bool aNotify);
     236             : 
     237             :   // placeholder methods
     238          48 :   bool GetPlaceholderVisibility() {
     239          48 :     return mPlaceholderVisibility;
     240             :   }
     241             :   void UpdatePlaceholderText(bool aNotify);
     242             : 
     243             :   // preview methods
     244             :   void SetPreviewText(const nsAString& aValue, bool aNotify);
     245             :   void GetPreviewText(nsAString& aValue);
     246           0 :   bool GetPreviewVisibility() {
     247           0 :     return mPreviewVisibility;
     248             :   }
     249             : 
     250             :   /**
     251             :    * Get the maxlength attribute
     252             :    * @param aMaxLength the value of the max length attr
     253             :    * @returns false if attr not defined
     254             :    */
     255             :   int32_t GetMaxLength();
     256             : 
     257           3 :   void ClearValueCache() { mCachedValue.Truncate(); }
     258             : 
     259             :   void HideSelectionIfBlurred();
     260             : 
     261             :   struct SelectionProperties {
     262             :     public:
     263          17 :       SelectionProperties() : mStart(0), mEnd(0),
     264          17 :         mDirection(nsITextControlFrame::eForward) {}
     265           1 :       bool IsDefault() const
     266             :       {
     267           2 :         return mStart == 0 && mEnd == 0 &&
     268           2 :                mDirection == nsITextControlFrame::eForward;
     269             :       }
     270           5 :       uint32_t GetStart() const
     271             :       {
     272           5 :         return mStart;
     273             :       }
     274           1 :       void SetStart(uint32_t value)
     275             :       {
     276           1 :         mIsDirty = true;
     277           1 :         mStart = value;
     278           1 :       }
     279           5 :       uint32_t GetEnd() const
     280             :       {
     281           5 :         return mEnd;
     282             :       }
     283           1 :       void SetEnd(uint32_t value)
     284             :       {
     285           1 :         mIsDirty = true;
     286           1 :         mEnd = value;
     287           1 :       }
     288           2 :       nsITextControlFrame::SelectionDirection GetDirection() const
     289             :       {
     290           2 :         return mDirection;
     291             :       }
     292           1 :       void SetDirection(nsITextControlFrame::SelectionDirection value)
     293             :       {
     294           1 :         mIsDirty = true;
     295           1 :         mDirection = value;
     296           1 :       }
     297             :       // return true only if mStart, mEnd, or mDirection have been modified,
     298             :       // or if SetIsDirty() was explicitly called.
     299           2 :       bool IsDirty() const
     300             :       {
     301           2 :         return mIsDirty;
     302             :       }
     303           5 :       void SetIsDirty()
     304             :       {
     305           5 :         mIsDirty = true;
     306           5 :       }
     307             :     private:
     308             :       uint32_t mStart, mEnd;
     309             :       bool mIsDirty = false;
     310             :       nsITextControlFrame::SelectionDirection mDirection;
     311             :   };
     312             : 
     313             :   bool IsSelectionCached() const;
     314             :   SelectionProperties& GetSelectionProperties();
     315             :   void SetSelectionProperties(SelectionProperties& aProps);
     316           0 :   void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
     317           4 :   bool HasNeverInitializedBefore() const { return !mEverInited; }
     318             :   // Sync up our selection properties with our editor prior to being destroyed.
     319             :   // This will invoke UnbindFromFrame() to ensure that we grab whatever
     320             :   // selection state may be at the moment.
     321             :   void SyncUpSelectionPropertiesBeforeDestruction();
     322             : 
     323             :   // Get the selection range start and end points in our text.
     324             :   void GetSelectionRange(uint32_t* aSelectionStart, uint32_t* aSelectionEnd,
     325             :                          mozilla::ErrorResult& aRv);
     326             : 
     327             :   // Get the selection direction
     328             :   nsITextControlFrame::SelectionDirection
     329             :     GetSelectionDirection(mozilla::ErrorResult& aRv);
     330             : 
     331             :   // Set the selection range (start, end, direction).  aEnd is allowed to be
     332             :   // smaller than aStart; in that case aStart will be reset to the same value as
     333             :   // aEnd.  This basically implements
     334             :   // https://html.spec.whatwg.org/multipage/forms.html#set-the-selection-range
     335             :   // but with the start/end already coerced to zero if null (and without the
     336             :   // special infinity value), and the direction already converted to a
     337             :   // SelectionDirection.
     338             :   //
     339             :   // If we have a frame, this method will scroll the selection into view.
     340             :   //
     341             :   // XXXbz This should really take uint32_t, but none of our guts (either the
     342             :   // frame or our cached selection state) work with uint32_t at the moment...
     343             :   void SetSelectionRange(uint32_t aStart, uint32_t aEnd,
     344             :                          nsITextControlFrame::SelectionDirection aDirection,
     345             :                          mozilla::ErrorResult& aRv);
     346             : 
     347             :   // Set the selection range, but with an optional string for the direction.
     348             :   // This will convert aDirection to an nsITextControlFrame::SelectionDirection
     349             :   // and then call our other SetSelectionRange overload.
     350             :   void SetSelectionRange(uint32_t aSelectionStart,
     351             :                          uint32_t aSelectionEnd,
     352             :                          const mozilla::dom::Optional<nsAString>& aDirection,
     353             :                          mozilla::ErrorResult& aRv);
     354             : 
     355             :   // Set the selection start.  This basically implements the
     356             :   // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionstart
     357             :   // setter.
     358             :   void SetSelectionStart(const mozilla::dom::Nullable<uint32_t>& aStart,
     359             :                          mozilla::ErrorResult& aRv);
     360             : 
     361             :   // Set the selection end.  This basically implements the
     362             :   // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionend
     363             :   // setter.
     364             :   void SetSelectionEnd(const mozilla::dom::Nullable<uint32_t>& aEnd,
     365             :                        mozilla::ErrorResult& aRv);
     366             : 
     367             :   // Get the selection direction as a string.  This implements the
     368             :   // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
     369             :   // getter.
     370             :   void GetSelectionDirectionString(nsAString& aDirection,
     371             :                                    mozilla::ErrorResult& aRv);
     372             : 
     373             :   // Set the selection direction.  This basically implements the
     374             :   // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
     375             :   // setter.
     376             :   void SetSelectionDirection(const nsAString& aDirection,
     377             :                              mozilla::ErrorResult& aRv);
     378             : 
     379             :   // Set the range text.  This basically implements
     380             :   // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-setrangetext
     381             :   void SetRangeText(const nsAString& aReplacement, mozilla::ErrorResult& aRv);
     382             :   // The last two arguments are -1 if we don't know our selection range;
     383             :   // otherwise they're the start and end of our selection range.
     384             :   void SetRangeText(const nsAString& aReplacement, uint32_t aStart,
     385             :                     uint32_t aEnd, mozilla::dom::SelectionMode aSelectMode,
     386             :                     mozilla::ErrorResult& aRv,
     387             :                     const mozilla::Maybe<uint32_t>& aSelectionStart =
     388             :                       mozilla::Nothing(),
     389             :                     const mozilla::Maybe<uint32_t>& aSelectionEnd =
     390             :                       mozilla::Nothing());
     391             : 
     392             :   void UpdateEditableState(bool aNotify) {
     393             :     if (mRootNode) {
     394             :       mRootNode->UpdateEditableState(aNotify);
     395             :     }
     396             :   }
     397             : 
     398             : private:
     399             :   friend class RestoreSelectionState;
     400             : 
     401             :   // not copy constructible
     402             :   nsTextEditorState(const nsTextEditorState&);
     403             :   // not assignable
     404             :   void operator= (const nsTextEditorState&);
     405             : 
     406             :   nsresult CreateRootNode();
     407             : 
     408             :   void ValueWasChanged(bool aNotify);
     409             : 
     410             :   void DestroyEditor();
     411             :   void Clear();
     412             : 
     413             :   nsresult InitializeRootNode();
     414             : 
     415             :   void FinishedRestoringSelection();
     416             : 
     417             :   mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
     418             : 
     419             :   bool EditorHasComposition();
     420             : 
     421             :   class InitializationGuard {
     422             :   public:
     423           2 :     explicit InitializationGuard(nsTextEditorState& aState) :
     424             :       mState(aState),
     425           2 :       mGuardSet(false)
     426             :     {
     427           2 :       if (!mState.mInitializing) {
     428           2 :         mGuardSet = true;
     429           2 :         mState.mInitializing = true;
     430             :       }
     431           2 :     }
     432           4 :     ~InitializationGuard() {
     433           2 :       if (mGuardSet) {
     434           2 :         mState.mInitializing = false;
     435             :       }
     436           2 :     }
     437           2 :     bool IsInitializingRecursively() const {
     438           2 :       return !mGuardSet;
     439             :     }
     440             :   private:
     441             :     nsTextEditorState& mState;
     442             :     bool mGuardSet;
     443             :   };
     444             :   friend class InitializationGuard;
     445             :   friend class PrepareEditorEvent;
     446             : 
     447             :   // The text control element owns this object, and ensures that this object
     448             :   // has a smaller lifetime.
     449             :   nsITextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement;
     450             :   // mSelCon is non-null while we have an mBoundFrame.
     451             :   RefPtr<nsTextInputSelectionImpl> mSelCon;
     452             :   RefPtr<RestoreSelectionState> mRestoringSelection;
     453             :   RefPtr<mozilla::TextEditor> mTextEditor;
     454             :   nsCOMPtr<mozilla::dom::Element> mRootNode;
     455             :   nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
     456             :   nsCOMPtr<mozilla::dom::Element> mPreviewDiv;
     457             :   nsTextControlFrame* mBoundFrame;
     458             :   RefPtr<nsTextInputListener> mTextListener;
     459             :   mozilla::Maybe<nsString> mValue;
     460             :   RefPtr<nsAnonDivObserver> mMutationObserver;
     461             :   mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
     462             :   // mValueBeingSet is available only while SetValue() is requesting to commit
     463             :   // composition.  I.e., this is valid only while mIsCommittingComposition is
     464             :   // true.  While active composition is being committed, GetValue() needs
     465             :   // the latest value which is set by SetValue().  So, this is cache for that.
     466             :   nsString mValueBeingSet;
     467             :   SelectionProperties mSelectionProperties;
     468             :   bool mEverInited; // Have we ever been initialized?
     469             :   bool mEditorInitialized;
     470             :   bool mInitializing; // Whether we're in the process of initialization
     471             :   bool mValueTransferInProgress; // Whether a value is being transferred to the frame
     472             :   bool mSelectionCached; // Whether mSelectionProperties is valid
     473             :   mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
     474             :   bool mPlaceholderVisibility;
     475             :   bool mPreviewVisibility;
     476             :   bool mIsCommittingComposition;
     477             : };
     478             : 
     479             : inline void
     480           0 : ImplCycleCollectionUnlink(nsTextEditorState& aField)
     481             : {
     482           0 :   aField.Unlink();
     483           0 : }
     484             : 
     485             : inline void
     486           0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
     487             :                             nsTextEditorState& aField,
     488             :                             const char* aName,
     489             :                             uint32_t aFlags = 0)
     490             : {
     491           0 :   aField.Traverse(aCallback);
     492           0 : }
     493             : 
     494             : #endif

Generated by: LCOV version 1.13