Bug 1880216 - Migrate Fenix docs into Sphinx. r=owlish,geckoview-reviewers,android...
[gecko.git] / dom / html / TextControlState.h
blob3dba24d2556115fee27b80758e6fec8a6b56456e
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"
21 #include "nsITimer.h"
23 class nsTextControlFrame;
24 class nsISelectionController;
25 class nsFrameSelection;
26 class nsFrame;
28 namespace mozilla {
30 class AutoTextControlHandlingState;
31 class ErrorResult;
32 class IMEContentObserver;
33 class TextEditor;
34 class TextInputListener;
35 class TextInputSelectionController;
37 namespace dom {
38 class Element;
39 class HTMLInputElement;
40 } // namespace dom
42 /**
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
48 // a password field.
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;
73 mUnmaskedLength = 0;
75 MOZ_ALWAYS_INLINE void Reset() {
76 MaskAll();
77 mIsMaskingPassword = true;
79 enum class ReleaseTimer { No, Yes };
80 MOZ_ALWAYS_INLINE void CancelTimer(ReleaseTimer aReleaseTimer) {
81 if (mTimer) {
82 mTimer->Cancel();
83 if (aReleaseTimer == ReleaseTimer::Yes) {
84 mTimer = nullptr;
87 if (mIsMaskingPassword) {
88 MaskAll();
93 /**
94 * TextControlState is a class which is responsible for managing the state of
95 * plaintext controls. This currently includes the following HTML elements:
96 * <input type=text>
97 * <input type=search>
98 * <input type=url>
99 * <input type=telephone>
100 * <input type=email>
101 * <input type=password>
102 * <textarea>
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
156 * value is changed.
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
161 * text field.
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 {
190 public:
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.
235 ByInternalAPI,
236 // The value is changed by a call of setUserInput() API from chrome.
237 BySetUserInputAPI,
238 // The value is changed by changing value attribute of the element or
239 // something like setRangeText().
240 ByContentAPI,
241 // The value is changed by setRangeText(). Intended to prevent silent
242 // selection range change.
243 BySetRangeTextAPI,
244 // Whether SetValueChanged should be called as a result of this value
245 // change.
246 SetValueChanged,
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.
258 PreserveUndoHistory,
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
265 // it.
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
300 // value or not.
301 // XXX We might have to add assertion when it is into editable,
302 // or reconsider fixing bug 597525 to remove these.
303 void EmptyValue() {
304 if (!mValue.IsVoid()) {
305 mValue.Truncate();
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);
331 return wrapCols;
333 int32_t GetRows() { return mTextCtrlElement->GetRows(); }
335 // preview methods
336 void SetPreviewText(const nsAString& aValue, bool aNotify);
337 void GetPreviewText(nsAString& aValue);
339 struct SelectionProperties {
340 public:
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;
349 mStart = newValue;
350 mIsDirty |= changed;
351 return changed;
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;
357 mEnd = newValue;
358 mIsDirty |= changed;
359 return changed;
361 SelectionDirection GetDirection() const { return mDirection; }
362 bool SetDirection(SelectionDirection value) {
363 bool changed = mDirection != value;
364 mDirection = value;
365 mIsDirty |= changed;
366 return changed;
368 void SetMaxLength(uint32_t aMax) {
369 mMaxLength = Some(aMax);
370 // recompute against the new max length
371 SetStart(GetStart());
372 SetEnd(GetEnd());
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; }
381 private:
382 uint32_t mStart = 0;
383 uint32_t mEnd = 0;
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,
400 ErrorResult& aRv);
402 // Get the selection direction
403 nsITextControlFrame::SelectionDirection GetSelectionDirection(
404 ErrorResult& aRv);
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
432 // setter.
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
438 // setter.
439 MOZ_CAN_RUN_SCRIPT void SetSelectionEnd(const dom::Nullable<uint32_t>& aEnd,
440 ErrorResult& aRv);
442 // Get the selection direction as a string. This implements the
443 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
444 // getter.
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
449 // setter.
450 MOZ_CAN_RUN_SCRIPT void SetSelectionDirection(const nsAString& aDirection,
451 ErrorResult& aRv);
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,
456 ErrorResult& aRv);
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());
465 private:
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
489 * nullptr.
491 * @param aHandlingSetValue Must be inner-most handling state for SetValue.
492 * @return false if fallible allocation failed. Otherwise,
493 * true.
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,
505 * true.
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
540 // the frame
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