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/. */
7 #ifndef mozilla_TextControlState_h
8 #define mozilla_TextControlState_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EnumSet.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/TextControlElement.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/WeakPtr.h"
17 #include "mozilla/dom/HTMLInputElementBinding.h"
18 #include "mozilla/dom/Nullable.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsITextControlFrame.h"
23 class nsTextControlFrame
;
24 class nsISelectionController
;
25 class nsFrameSelection
;
30 class AutoTextControlHandlingState
;
32 class IMEContentObserver
;
34 class TextInputListener
;
35 class TextInputSelectionController
;
39 class HTMLInputElement
;
43 * PasswordMaskData stores making information and necessary timer for
44 * `TextEditor` instances.
46 struct PasswordMaskData final
{
47 // Timer to mask unmasked characters automatically. Used only when it's
49 nsCOMPtr
<nsITimer
> mTimer
;
51 // Unmasked character range. Used only when it's a password field.
52 // If mUnmaskedLength is 0, it means there is no unmasked characters.
53 uint32_t mUnmaskedStart
= UINT32_MAX
;
54 uint32_t mUnmaskedLength
= 0;
56 // Set to true if all characters are masked or waiting notification from
57 // `mTimer`. Otherwise, i.e., part of or all of password is unmasked
58 // without setting `mTimer`, set to false.
59 bool mIsMaskingPassword
= true;
61 // Set to true if a manager of the instance wants to disable echoing
62 // password temporarily.
63 bool mEchoingPasswordPrevented
= false;
65 MOZ_ALWAYS_INLINE
bool IsAllMasked() const {
66 return mUnmaskedStart
== UINT32_MAX
&& mUnmaskedLength
== 0;
68 MOZ_ALWAYS_INLINE
uint32_t UnmaskedEnd() const {
69 return mUnmaskedStart
+ mUnmaskedLength
;
71 MOZ_ALWAYS_INLINE
void MaskAll() {
72 mUnmaskedStart
= UINT32_MAX
;
75 MOZ_ALWAYS_INLINE
void Reset() {
77 mIsMaskingPassword
= true;
79 enum class ReleaseTimer
{ No
, Yes
};
80 MOZ_ALWAYS_INLINE
void CancelTimer(ReleaseTimer aReleaseTimer
) {
83 if (aReleaseTimer
== ReleaseTimer::Yes
) {
87 if (mIsMaskingPassword
) {
94 * TextControlState is a class which is responsible for managing the state of
95 * plaintext controls. This currently includes the following HTML elements:
99 * <input type=telephone>
101 * <input type=password>
104 * This class is held as a member of HTMLInputElement and HTMLTextAreaElement.
105 * The public functions in this class include the public APIs which dom/
106 * uses. Layout code uses the TextControlElement interface to invoke
107 * functions on this class.
109 * The design motivation behind this class is maintaining all of the things
110 * which collectively are considered the "state" of the text control in a single
111 * location. This state includes several things:
113 * * The control's value. This value is stored in the mValue member, and is
114 * only used when there is no frame for the control, or when the editor object
115 * has not been initialized yet.
117 * * The control's associated frame. This value is stored in the mBoundFrame
118 * member. A text control might never have an associated frame during its life
119 * cycle, or might have several different ones, but at any given moment in time
120 * there is a maximum of 1 bound frame to each text control.
122 * * The control's associated editor. This value is stored in the mTextEditor
123 * member. An editor is initialized for the control only when necessary (that
124 * is, when either the user is about to interact with the text control, or when
125 * some other code needs to access the editor object. Without a frame bound to
126 * the control, an editor is never initialized. Once initialized, the editor
127 * might outlive the frame, in which case the same editor will be used if a new
128 * frame gets bound to the text control.
130 * * The anonymous content associated with the text control's frame, including
131 * the value div (the DIV element responsible for holding the value of the text
132 * control) and the placeholder div (the DIV element responsible for holding the
133 * placeholder value of the text control.) These values are stored in the
134 * mRootNode and mPlaceholderDiv members, respectively. They will be created
135 * when a frame is bound to the text control. They will be destroyed when the
136 * frame is unbound from the object. We could try and hold on to the anonymous
137 * content between different frames, but unfortunately that is not currently
138 * possible because they are not unbound from the document in time.
140 * * The frame selection controller. This value is stored in the mSelCon
141 * member. The frame selection controller is responsible for maintaining the
142 * selection state on a frame. It is created when a frame is bound to the text
143 * control element, and will be destroy when the frame is being unbound from the
144 * text control element. It is created alongside with the frame selection object
145 * which is stored in the mFrameSel member.
147 * * The editor text listener. This value is stored in the mTextListener
148 * member. Its job is to listen to selection and keyboard events, and act
149 * accordingly. It is created when an a frame is first bound to the control, and
150 * will be destroyed when the frame is unbound from the text control element.
152 * * The editor's cached value. This value is stored in the mCachedValue
153 * member. It is used to improve the performance of append operations to the
154 * text control. A mutation observer stored in the mMutationObserver has the
155 * job of invalidating this cache when the anonymous contect containing the
158 * * The editor's cached selection properties. These vales are stored in the
159 * mSelectionProperties member, and include the selection's start, end and
160 * direction. They are only used when there is no frame available for the
164 * As a general rule, TextControlState objects own the value of the text
165 * control, and any attempt to retrieve or set the value must be made through
166 * those objects. Internally, the value can be represented in several different
167 * ways, based on the state the control is in.
169 * * When the control is first initialized, its value is equal to the default
170 * value of the DOM node. For <input> text controls, this default value is the
171 * value of the value attribute. For <textarea> elements, this default value is
172 * the value of the text node children of the element.
174 * * If the value has been changed through the DOM node (before the editor for
175 * the object is initialized), the value is stored as a simple string inside the
176 * mValue member of the TextControlState object.
178 * * If an editor has been initialized for the control, the value is set and
179 * retrievd via the nsIEditor interface, and is internally managed by the
180 * editor as the native anonymous content tree attached to the control's frame.
182 * * If the text control state object is unbound from the control's frame, the
183 * value is transferred to the mValue member variable, and will be managed there
184 * until a new frame is bound to the text editor state object.
187 class RestoreSelectionState
;
189 class TextControlState final
: public SupportsWeakPtr
{
191 using Element
= dom::Element
;
192 using HTMLInputElement
= dom::HTMLInputElement
;
193 using SelectionDirection
= nsITextControlFrame::SelectionDirection
;
195 static TextControlState
* Construct(TextControlElement
* aOwningElement
);
197 // Note that this does not run script actually because of `sHasShutDown`
198 // is set to true before calling `DeleteOrCacheForReuse()`.
199 MOZ_CAN_RUN_SCRIPT_BOUNDARY
static void Shutdown();
202 * Destroy() deletes the instance immediately or later.
204 MOZ_CAN_RUN_SCRIPT
void Destroy();
206 TextControlState() = delete;
207 explicit TextControlState(const TextControlState
&) = delete;
208 TextControlState(TextControlState
&&) = delete;
210 void operator=(const TextControlState
&) = delete;
211 void operator=(TextControlState
&&) = delete;
213 void Traverse(nsCycleCollectionTraversalCallback
& cb
);
214 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void Unlink();
216 bool IsBusy() const { return !!mHandlingState
|| mValueTransferInProgress
; }
218 MOZ_CAN_RUN_SCRIPT TextEditor
* GetTextEditor();
219 TextEditor
* GetTextEditorWithoutCreation() const;
220 nsISelectionController
* GetSelectionController() const;
221 nsFrameSelection
* GetConstFrameSelection();
222 nsresult
BindToFrame(nsTextControlFrame
* aFrame
);
223 MOZ_CAN_RUN_SCRIPT
void UnbindFromFrame(nsTextControlFrame
* aFrame
);
224 MOZ_CAN_RUN_SCRIPT nsresult
PrepareEditor(const nsAString
* aValue
= nullptr);
225 void InitializeKeyboardEventListeners();
228 * OnEditActionHandled() is called when mTextEditor handles something
229 * and immediately before dispatching "input" event.
231 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
OnEditActionHandled();
233 enum class ValueSetterOption
{
234 // The call is for setting value to initial one, computed one, etc.
236 // The value is changed by a call of setUserInput() API from chrome.
238 // The value is changed by changing value attribute of the element or
239 // something like setRangeText().
241 // The value is changed by setRangeText(). Intended to prevent silent
242 // selection range change.
244 // Whether SetValueChanged should be called as a result of this value
247 // Whether to move the cursor to end of the value (in the case when we have
248 // cached selection offsets), in the case when the value has changed. If
249 // this is not set and MoveCursorToBeginSetSelectionDirectionForward
250 // is not set, the cached selection offsets will simply be clamped to
251 // be within the length of the new value. In either case, if the value has
252 // not changed the cursor won't move.
253 // TODO(mbrodesser): update comment and enumerator identifier to reflect
254 // that also the direction is set to forward.
255 MoveCursorToEndIfValueChanged
,
257 // The value change should preserve undo history.
260 // Whether it should be tried to move the cursor to the beginning of the
261 // text control and set the selection direction to "forward".
262 // TODO(mbrodesser): As soon as "none" is supported
263 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1541454), it should be set
264 // to "none" and only fall back to "forward" if the platform doesn't support
266 MoveCursorToBeginSetSelectionDirectionForward
,
268 using ValueSetterOptions
= EnumSet
<ValueSetterOption
, uint32_t>;
271 * SetValue() sets the value to aValue with replacing \r\n and \r with \n.
273 * @param aValue The new value. Can contain \r.
274 * @param aOldValue Optional. If you have already know current value,
275 * set this to it. However, this must not contain \r
276 * for the performance.
277 * @param aOptions See ValueSetterOption.
279 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
bool SetValue(
280 const nsAString
& aValue
, const nsAString
* aOldValue
,
281 const ValueSetterOptions
& aOptions
);
282 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
bool SetValue(
283 const nsAString
& aValue
, const ValueSetterOptions
& aOptions
) {
284 return SetValue(aValue
, nullptr, aOptions
);
288 * GetValue() returns current value either with or without TextEditor.
289 * The result never includes \r.
291 void GetValue(nsAString
& aValue
, bool aIgnoreWrap
, bool aForDisplay
) const;
294 * ValueEquals() is designed for internal use so that aValue shouldn't
295 * include \r character. It should be handled before calling this with
296 * nsContentUtils::PlatformToDOMLineBreaks().
298 bool ValueEquals(const nsAString
& aValue
) const;
299 // The following methods are for textarea element to use whether default
301 // XXX We might have to add assertion when it is into editable,
302 // or reconsider fixing bug 597525 to remove these.
304 if (!mValue
.IsVoid()) {
308 bool IsEmpty() const { return mValue
.IsEmpty(); }
310 const nsAString
& LastInteractiveValueIfLastChangeWasNonInteractive() const {
311 return mLastInteractiveValue
;
313 // When an interactive value change happens, we clear mLastInteractiveValue
314 // because it's not needed (mValue is the new interactive value).
315 void ClearLastInteractiveValue() { mLastInteractiveValue
.SetIsVoid(true); }
317 Element
* GetRootNode();
318 Element
* GetPreviewNode();
320 bool IsSingleLineTextControl() const {
321 return mTextCtrlElement
->IsSingleLineTextControl();
323 bool IsTextArea() const { return mTextCtrlElement
->IsTextArea(); }
324 bool IsPasswordTextControl() const {
325 return mTextCtrlElement
->IsPasswordTextControl();
327 int32_t GetCols() { return mTextCtrlElement
->GetCols(); }
328 int32_t GetWrapCols() {
329 int32_t wrapCols
= mTextCtrlElement
->GetWrapCols();
330 MOZ_ASSERT(wrapCols
>= 0);
333 int32_t GetRows() { return mTextCtrlElement
->GetRows(); }
336 void SetPreviewText(const nsAString
& aValue
, bool aNotify
);
337 void GetPreviewText(nsAString
& aValue
);
339 struct SelectionProperties
{
341 bool IsDefault() const {
342 return mStart
== 0 && mEnd
== 0 &&
343 mDirection
== SelectionDirection::Forward
;
345 uint32_t GetStart() const { return mStart
; }
346 bool SetStart(uint32_t value
) {
347 uint32_t newValue
= std::min(value
, *mMaxLength
);
348 bool changed
= mStart
!= newValue
;
353 uint32_t GetEnd() const { return mEnd
; }
354 bool SetEnd(uint32_t value
) {
355 uint32_t newValue
= std::min(value
, *mMaxLength
);
356 bool changed
= mEnd
!= newValue
;
361 SelectionDirection
GetDirection() const { return mDirection
; }
362 bool SetDirection(SelectionDirection value
) {
363 bool changed
= mDirection
!= value
;
368 void SetMaxLength(uint32_t aMax
) {
369 mMaxLength
= Some(aMax
);
370 // recompute against the new max length
371 SetStart(GetStart());
374 bool HasMaxLength() { return mMaxLength
.isSome(); }
376 // return true only if mStart, mEnd, or mDirection have been modified,
377 // or if SetIsDirty() was explicitly called.
378 bool IsDirty() const { return mIsDirty
; }
379 void SetIsDirty() { mIsDirty
= true; }
384 Maybe
<uint32_t> mMaxLength
;
385 bool mIsDirty
= false;
386 SelectionDirection mDirection
= SelectionDirection::Forward
;
389 bool IsSelectionCached() const { return mSelectionCached
; }
390 SelectionProperties
& GetSelectionProperties() { return mSelectionProperties
; }
391 MOZ_CAN_RUN_SCRIPT
void SetSelectionProperties(SelectionProperties
& aProps
);
392 bool HasNeverInitializedBefore() const { return !mEverInited
; }
393 // Sync up our selection properties with our editor prior to being destroyed.
394 // This will invoke UnbindFromFrame() to ensure that we grab whatever
395 // selection state may be at the moment.
396 MOZ_CAN_RUN_SCRIPT
void SyncUpSelectionPropertiesBeforeDestruction();
398 // Get the selection range start and end points in our text.
399 void GetSelectionRange(uint32_t* aSelectionStart
, uint32_t* aSelectionEnd
,
402 // Get the selection direction
403 nsITextControlFrame::SelectionDirection
GetSelectionDirection(
406 enum class ScrollAfterSelection
{ No
, Yes
};
408 // Set the selection range (start, end, direction). aEnd is allowed to be
409 // smaller than aStart; in that case aStart will be reset to the same value as
410 // aEnd. This basically implements
411 // https://html.spec.whatwg.org/multipage/forms.html#set-the-selection-range
412 // but with the start/end already coerced to zero if null (and without the
413 // special infinity value), and the direction already converted to a
414 // SelectionDirection.
416 // If we have a frame, this method will scroll the selection into view.
417 MOZ_CAN_RUN_SCRIPT
void SetSelectionRange(
418 uint32_t aStart
, uint32_t aEnd
,
419 nsITextControlFrame::SelectionDirection aDirection
, ErrorResult
& aRv
,
420 ScrollAfterSelection aScroll
= ScrollAfterSelection::Yes
);
422 // Set the selection range, but with an optional string for the direction.
423 // This will convert aDirection to an nsITextControlFrame::SelectionDirection
424 // and then call our other SetSelectionRange overload.
425 MOZ_CAN_RUN_SCRIPT
void SetSelectionRange(
426 uint32_t aSelectionStart
, uint32_t aSelectionEnd
,
427 const dom::Optional
<nsAString
>& aDirection
, ErrorResult
& aRv
,
428 ScrollAfterSelection aScroll
= ScrollAfterSelection::Yes
);
430 // Set the selection start. This basically implements the
431 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionstart
433 MOZ_CAN_RUN_SCRIPT
void SetSelectionStart(
434 const dom::Nullable
<uint32_t>& aStart
, ErrorResult
& aRv
);
436 // Set the selection end. This basically implements the
437 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionend
439 MOZ_CAN_RUN_SCRIPT
void SetSelectionEnd(const dom::Nullable
<uint32_t>& aEnd
,
442 // Get the selection direction as a string. This implements the
443 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
445 void GetSelectionDirectionString(nsAString
& aDirection
, ErrorResult
& aRv
);
447 // Set the selection direction. This basically implements the
448 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
450 MOZ_CAN_RUN_SCRIPT
void SetSelectionDirection(const nsAString
& aDirection
,
453 // Set the range text. This basically implements
454 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-setrangetext
455 MOZ_CAN_RUN_SCRIPT
void SetRangeText(const nsAString
& aReplacement
,
457 // The last two arguments are -1 if we don't know our selection range;
458 // otherwise they're the start and end of our selection range.
459 MOZ_CAN_RUN_SCRIPT
void SetRangeText(
460 const nsAString
& aReplacement
, uint32_t aStart
, uint32_t aEnd
,
461 dom::SelectionMode aSelectMode
, ErrorResult
& aRv
,
462 const Maybe
<uint32_t>& aSelectionStart
= Nothing(),
463 const Maybe
<uint32_t>& aSelectionEnd
= Nothing());
466 explicit TextControlState(TextControlElement
* aOwningElement
);
467 MOZ_CAN_RUN_SCRIPT
~TextControlState();
470 * Delete the instance or cache to reuse it if possible.
472 MOZ_CAN_RUN_SCRIPT
void DeleteOrCacheForReuse();
474 MOZ_CAN_RUN_SCRIPT
void UnlinkInternal();
476 MOZ_CAN_RUN_SCRIPT
void DestroyEditor();
477 MOZ_CAN_RUN_SCRIPT
void Clear();
479 nsresult
InitializeRootNode();
481 void FinishedRestoringSelection();
483 bool EditorHasComposition();
486 * SetValueWithTextEditor() modifies the editor value with mTextEditor.
487 * This may cause destroying mTextEditor, mBoundFrame, the TextControlState
488 * itself. Must be called when both mTextEditor and mBoundFrame are not
491 * @param aHandlingSetValue Must be inner-most handling state for SetValue.
492 * @return false if fallible allocation failed. Otherwise,
495 MOZ_CAN_RUN_SCRIPT
bool SetValueWithTextEditor(
496 AutoTextControlHandlingState
& aHandlingSetValue
);
499 * SetValueWithoutTextEditor() modifies the value without editor. I.e.,
500 * modifying the value in this instance and mBoundFrame. Must be called
501 * when at least mTextEditor or mBoundFrame is nullptr.
503 * @param aHandlingSetValue Must be inner-most handling state for SetValue.
504 * @return false if fallible allocation failed. Otherwise,
507 MOZ_CAN_RUN_SCRIPT
bool SetValueWithoutTextEditor(
508 AutoTextControlHandlingState
& aHandlingSetValue
);
510 IMEContentObserver
* GetIMEContentObserver() const;
512 // When this class handles something which may run script, this should be
513 // set to non-nullptr. If so, this class claims that it's busy and that
514 // prevents destroying TextControlState instance.
515 AutoTextControlHandlingState
* mHandlingState
= nullptr;
517 // The text control element owns this object, and ensures that this object
518 // has a smaller lifetime except the owner releases the instance while it
519 // does something with this.
520 TextControlElement
* MOZ_NON_OWNING_REF mTextCtrlElement
;
521 RefPtr
<TextInputSelectionController
> mSelCon
;
522 RefPtr
<RestoreSelectionState
> mRestoringSelection
;
523 RefPtr
<TextEditor
> mTextEditor
;
524 nsTextControlFrame
* mBoundFrame
= nullptr;
525 RefPtr
<TextInputListener
> mTextListener
;
526 UniquePtr
<PasswordMaskData
> mPasswordMaskData
;
528 nsString mValue
{VoidString()}; // Void if there's no value.
530 // If our input's last value change was not interactive (as in, the value
531 // change was caused by a ValueChangeKind::UserInteraction), this is the value
532 // that the last interaction had.
533 nsString mLastInteractiveValue
{VoidString()};
535 SelectionProperties mSelectionProperties
;
537 bool mEverInited
: 1; // Have we ever been initialized?
538 bool mEditorInitialized
: 1;
539 bool mValueTransferInProgress
: 1; // Whether a value is being transferred to
541 bool mSelectionCached
: 1; // Whether mSelectionProperties is valid
543 friend class AutoTextControlHandlingState
;
544 friend class PrepareEditorEvent
;
545 friend class RestoreSelectionState
;
548 } // namespace mozilla
550 #endif // #ifndef mozilla_TextControlState_h