1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et 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/. */
7 #ifndef nsTextEditorState_h__
8 #define nsTextEditorState_h__
10 #include "nsAutoPtr.h"
12 #include "nsITextControlElement.h"
13 #include "nsITextControlFrame.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/WeakPtr.h"
18 class nsTextInputListener
;
19 class nsTextControlFrame
;
20 class nsTextInputSelectionImpl
;
21 class nsAnonDivObserver
;
22 class nsISelectionController
;
23 class nsFrameSelection
;
25 class nsITextControlElement
;
30 class HTMLInputElement
;
35 * nsTextEditorState is a class which is responsible for managing the state of
36 * plaintext controls. This currently includes the following HTML elements:
38 * <input type=password>
40 * and also XUL controls such as <textbox> which use one of these elements behind
43 * This class is held as a member of HTMLInputElement and nsHTMLTextAreaElement.
44 * The public functions in this class include the public APIs which content/ uses.
45 * Layout code uses the nsITextControlElement interface to invoke functions on this
48 * The design motivation behind this class is maintaining all of the things which
49 * collectively are considered the "state" of the text control in a single location.
50 * This state includes several things:
52 * * The control's value. This value is stored in the mValue member, and is only
53 * used when there is no frame for the control, or when the editor object has
54 * not been initialized yet.
56 * * The control's associated frame. This value is stored in the mBoundFrame member.
57 * A text control might never have an associated frame during its life cycle,
58 * or might have several different ones, but at any given moment in time there is
59 * a maximum of 1 bound frame to each text control.
61 * * The control's associated editor. This value is stored in the mEditor member.
62 * An editor is initilized for the control only when necessary (that is, when either
63 * the user is about to interact with the text control, or when some other code
64 * needs to access the editor object. Without a frame bound to the control, an
65 * editor is never initialzied. Once initialized, the editor might outlive the frame,
66 * in which case the same editor will be used if a new frame gets bound to the
69 * * The anonymous content associated with the text control's frame, including the
70 * value div (the DIV element responsible for holding the value of the text control)
71 * and the placeholder div (the DIV element responsible for holding the placeholder
72 * value of the text control.) These values are stored in the mRootNode and
73 * mPlaceholderDiv members, respectively. They will be created when a
74 * frame is bound to the text control. They will be destroyed when the frame is
75 * unbound from the object. We could try and hold on to the anonymous content
76 * between different frames, but unfortunately that is not currently possible
77 * because they are not unbound from the document in time.
79 * * The frame selection controller. This value is stored in the mSelCon member.
80 * The frame selection controller is responsible for maintaining the selection state
81 * on a frame. It is created when a frame is bound to the text control element,
82 * and will be destroy when the frame is being unbound from the text control element.
83 * It is created alongside with the frame selection object which is stored in the
86 * * The editor text listener. This value is stored in the mTextListener member.
87 * Its job is to listen to selection and keyboard events, and act accordingly.
88 * It is created when an a frame is first bound to the control, and will be destroyed
89 * when the frame is unbound from the text control element.
91 * * The editor's cached value. This value is stored in the mCachedValue member.
92 * It is used to improve the performance of append operations to the text
93 * control. A mutation observer stored in the mMutationObserver has the job of
94 * invalidating this cache when the anonymous contect containing the value is
97 * * The editor's cached selection properties. These vales are stored in the
98 * mSelectionProperties member, and include the selection's start, end and
99 * direction. They are only used when there is no frame available for the
103 * As a general rule, nsTextEditorState objects own the value of the text control, and any
104 * attempt to retrieve or set the value must be made through those objects. Internally,
105 * the value can be represented in several different ways, based on the state the control is
108 * * When the control is first initialized, its value is equal to the default value of
109 * the DOM node. For <input> text controls, this default value is the value of the
110 * value attribute. For <textarea> elements, this default value is the value of the
111 * text node children of the element.
113 * * If the value has been changed through the DOM node (before the editor for the object
114 * is initialized), the value is stored as a simple string inside the mValue member of
115 * the nsTextEditorState object.
117 * * If an editor has been initialized for the control, the value is set and retrievd via
118 * the nsIPlaintextEditor interface, and is internally managed by the editor as the
119 * native anonymous content tree attached to the control's frame.
121 * * If the text editor state object is unbound from the control's frame, the value is
122 * transferred to the mValue member variable, and will be managed there until a new
123 * frame is bound to the text editor state object.
126 class RestoreSelectionState
;
128 class nsTextEditorState
: public mozilla::SupportsWeakPtr
<nsTextEditorState
> {
130 MOZ_DECLARE_REFCOUNTED_TYPENAME(nsTextEditorState
)
131 explicit nsTextEditorState(nsITextControlElement
* aOwningElement
);
132 ~nsTextEditorState();
134 void Traverse(nsCycleCollectionTraversalCallback
& cb
);
137 nsIEditor
* GetEditor();
138 nsISelectionController
* GetSelectionController() const;
139 nsFrameSelection
* GetConstFrameSelection();
140 nsresult
BindToFrame(nsTextControlFrame
* aFrame
);
141 void UnbindFromFrame(nsTextControlFrame
* aFrame
);
142 nsresult
PrepareEditor(const nsAString
*aValue
= nullptr);
143 void InitializeKeyboardEventListeners();
145 void SetValue(const nsAString
& aValue
, bool aUserInput
,
146 bool aSetValueAsChanged
);
147 void GetValue(nsAString
& aValue
, bool aIgnoreWrap
) const;
148 void EmptyValue() { if (mValue
) mValue
->Truncate(); }
149 bool IsEmpty() const { return mValue
? mValue
->IsEmpty() : true; }
151 nsresult
CreatePlaceholderNode();
153 mozilla::dom::Element
* GetRootNode() {
158 mozilla::dom::Element
* GetPlaceholderNode() {
159 return mPlaceholderDiv
;
162 bool IsSingleLineTextControl() const {
163 return mTextCtrlElement
->IsSingleLineTextControl();
165 bool IsTextArea() const {
166 return mTextCtrlElement
->IsTextArea();
168 bool IsPlainTextControl() const {
169 return mTextCtrlElement
->IsPlainTextControl();
171 bool IsPasswordTextControl() const {
172 return mTextCtrlElement
->IsPasswordTextControl();
175 return mTextCtrlElement
->GetCols();
177 int32_t GetWrapCols() {
178 return mTextCtrlElement
->GetWrapCols();
181 return mTextCtrlElement
->GetRows();
184 // placeholder methods
185 void UpdatePlaceholderVisibility(bool aNotify
);
186 bool GetPlaceholderVisibility() {
187 return mPlaceholderVisibility
;
189 void UpdatePlaceholderText(bool aNotify
);
192 * Get the maxlength attribute
193 * @param aMaxLength the value of the max length attr
194 * @returns false if attr not defined
196 bool GetMaxLength(int32_t* aMaxLength
);
198 void ClearValueCache() { mCachedValue
.Truncate(); }
200 void HideSelectionIfBlurred();
202 struct SelectionProperties
{
203 SelectionProperties() : mStart(0), mEnd(0),
204 mDirection(nsITextControlFrame::eForward
) {}
205 bool IsDefault() const {
206 return mStart
== 0 && mEnd
== 0 &&
207 mDirection
== nsITextControlFrame::eForward
;
209 int32_t mStart
, mEnd
;
210 nsITextControlFrame::SelectionDirection mDirection
;
213 bool IsSelectionCached() const;
214 SelectionProperties
& GetSelectionProperties();
215 void WillInitEagerly() { mSelectionRestoreEagerInit
= true; }
216 bool HasNeverInitializedBefore() const { return !mEverInited
; }
218 void UpdateEditableState(bool aNotify
) {
220 mRootNode
->UpdateEditableState(aNotify
);
225 friend class RestoreSelectionState
;
227 // not copy constructible
228 nsTextEditorState(const nsTextEditorState
&);
230 void operator= (const nsTextEditorState
&);
232 nsresult
CreateRootNode();
234 void ValueWasChanged(bool aNotify
);
236 void DestroyEditor();
239 nsresult
InitializeRootNode();
241 void FinishedRestoringSelection() { mRestoringSelection
= nullptr; }
243 mozilla::dom::HTMLInputElement
* GetParentNumberControl(nsFrame
* aFrame
) const;
245 class InitializationGuard
{
247 explicit InitializationGuard(nsTextEditorState
& aState
) :
251 if (!mState
.mInitializing
) {
253 mState
.mInitializing
= true;
256 ~InitializationGuard() {
258 mState
.mInitializing
= false;
261 bool IsInitializingRecursively() const {
265 nsTextEditorState
& mState
;
268 friend class InitializationGuard
;
269 friend class PrepareEditorEvent
;
271 nsITextControlElement
* const mTextCtrlElement
;
272 nsRefPtr
<nsTextInputSelectionImpl
> mSelCon
;
273 RestoreSelectionState
* mRestoringSelection
;
274 nsCOMPtr
<nsIEditor
> mEditor
;
275 nsCOMPtr
<mozilla::dom::Element
> mRootNode
;
276 nsCOMPtr
<mozilla::dom::Element
> mPlaceholderDiv
;
277 nsTextControlFrame
* mBoundFrame
;
278 nsTextInputListener
* mTextListener
;
279 nsAutoPtr
<nsCString
> mValue
;
280 nsRefPtr
<nsAnonDivObserver
> mMutationObserver
;
281 mutable nsString mCachedValue
; // Caches non-hard-wrapped value on a multiline control.
282 bool mEverInited
; // Have we ever been initialized?
283 bool mEditorInitialized
;
284 bool mInitializing
; // Whether we're in the process of initialization
285 bool mValueTransferInProgress
; // Whether a value is being transferred to the frame
286 bool mSelectionCached
; // Whether mSelectionProperties is valid
287 mutable bool mSelectionRestoreEagerInit
; // Whether we're eager initing because of selection restore
288 SelectionProperties mSelectionProperties
;
289 bool mPlaceholderVisibility
;
293 ImplCycleCollectionUnlink(nsTextEditorState
& aField
)
299 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
300 nsTextEditorState
& aField
,
304 aField
.Traverse(aCallback
);