TextControlState.h (22930B)
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_TextControlState_h 8 #define mozilla_TextControlState_h 9 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/Nullable.h" 18 #include "nsCycleCollectionParticipant.h" 19 #include "nsITimer.h" 20 21 class nsTextControlFrame; 22 class nsISelectionController; 23 class nsFrameSelection; 24 class nsFrame; 25 26 namespace mozilla { 27 28 class AutoTextControlHandlingState; 29 class ErrorResult; 30 class IMEContentObserver; 31 class TextEditor; 32 class TextInputListener; 33 class TextInputSelectionController; 34 35 enum class SelectionDirection : uint8_t { 36 None, 37 Forward, 38 Backward, 39 }; 40 41 namespace dom { 42 enum class SelectionMode : uint8_t; 43 class Element; 44 class HTMLInputElement; 45 } // namespace dom 46 47 /** 48 * PasswordMaskData stores making information and necessary timer for 49 * `TextEditor` instances. 50 */ 51 struct PasswordMaskData final { 52 // Timer to mask unmasked characters automatically. Used only when it's 53 // a password field. 54 nsCOMPtr<nsITimer> mTimer; 55 56 // Unmasked character range. Used only when it's a password field. 57 // If mUnmaskedLength is 0, it means there is no unmasked characters. 58 uint32_t mUnmaskedStart = UINT32_MAX; 59 uint32_t mUnmaskedLength = 0; 60 61 // Set to true if all characters are masked or waiting notification from 62 // `mTimer`. Otherwise, i.e., part of or all of password is unmasked 63 // without setting `mTimer`, set to false. 64 bool mIsMaskingPassword = true; 65 66 // Set to true if a manager of the instance wants to disable echoing 67 // password temporarily. 68 bool mEchoingPasswordPrevented = false; 69 70 MOZ_ALWAYS_INLINE bool IsAllMasked() const { 71 return mUnmaskedStart == UINT32_MAX && mUnmaskedLength == 0; 72 } 73 MOZ_ALWAYS_INLINE uint32_t UnmaskedEnd() const { 74 return mUnmaskedStart + mUnmaskedLength; 75 } 76 MOZ_ALWAYS_INLINE void MaskAll() { 77 mUnmaskedStart = UINT32_MAX; 78 mUnmaskedLength = 0; 79 } 80 MOZ_ALWAYS_INLINE void Reset() { 81 MaskAll(); 82 mIsMaskingPassword = true; 83 } 84 enum class ReleaseTimer { No, Yes }; 85 MOZ_ALWAYS_INLINE void CancelTimer(ReleaseTimer aReleaseTimer) { 86 if (mTimer) { 87 mTimer->Cancel(); 88 if (aReleaseTimer == ReleaseTimer::Yes) { 89 mTimer = nullptr; 90 } 91 } 92 if (mIsMaskingPassword) { 93 MaskAll(); 94 } 95 } 96 }; 97 98 /** 99 * TextControlState is a class which is responsible for managing the state of 100 * plaintext controls. This currently includes the following HTML elements: 101 * <input type=text> 102 * <input type=search> 103 * <input type=url> 104 * <input type=telephone> 105 * <input type=email> 106 * <input type=password> 107 * <textarea> 108 * 109 * This class is held as a member of HTMLInputElement and HTMLTextAreaElement. 110 * The public functions in this class include the public APIs which dom/ 111 * uses. Layout code uses the TextControlElement interface to invoke 112 * functions on this class. 113 * 114 * The design motivation behind this class is maintaining all of the things 115 * which collectively are considered the "state" of the text control in a single 116 * location. This state includes several things: 117 * 118 * * The control's value. This value is stored in the mValue member, and is 119 * only used when there is no frame for the control, or when the editor object 120 * has not been initialized yet. 121 * 122 * * The control's associated frame. This value is stored in the mBoundFrame 123 * member. A text control might never have an associated frame during its life 124 * cycle, or might have several different ones, but at any given moment in time 125 * there is a maximum of 1 bound frame to each text control. 126 * 127 * * The control's associated editor. This value is stored in the mTextEditor 128 * member. An editor is initialized for the control only when necessary (that 129 * is, when either the user is about to interact with the text control, or when 130 * some other code needs to access the editor object. Without a frame bound to 131 * the control, an editor is never initialized. Once initialized, the editor 132 * might outlive the frame, in which case the same editor will be used if a new 133 * frame gets bound to the text control. 134 * 135 * * The anonymous content associated with the text control's frame, including 136 * the value div (the DIV element responsible for holding the value of the text 137 * control) and the placeholder div (the DIV element responsible for holding the 138 * placeholder value of the text control.) These values are stored in the 139 * mRootNode and mPlaceholderDiv members, respectively. They will be created 140 * when a frame is bound to the text control. They will be destroyed when the 141 * frame is unbound from the object. We could try and hold on to the anonymous 142 * content between different frames, but unfortunately that is not currently 143 * possible because they are not unbound from the document in time. 144 * 145 * * The frame selection controller. This value is stored in the mSelCon 146 * member. The frame selection controller is responsible for maintaining the 147 * selection state on a frame. It is created when a frame is bound to the text 148 * control element, and will be destroy when the frame is being unbound from the 149 * text control element. It is created alongside with the frame selection object 150 * which is stored in the mFrameSel member. 151 * 152 * * The editor text listener. This value is stored in the mTextListener 153 * member. Its job is to listen to selection and keyboard events, and act 154 * accordingly. It is created when an a frame is first bound to the control, and 155 * will be destroyed when the frame is unbound from the text control element. 156 * 157 * * The editor's cached value. This value is stored in the mCachedValue 158 * member. It is used to improve the performance of append operations to the 159 * text control. A mutation observer stored in the mMutationObserver has the 160 * job of invalidating this cache when the anonymous contect containing the 161 * value is changed. 162 * 163 * * The editor's cached selection properties. These vales are stored in the 164 * mSelectionProperties member, and include the selection's start, end and 165 * direction. They are only used when there is no frame available for the 166 * text field. 167 * 168 * 169 * As a general rule, TextControlState objects own the value of the text 170 * control, and any attempt to retrieve or set the value must be made through 171 * those objects. Internally, the value can be represented in several different 172 * ways, based on the state the control is in. 173 * 174 * * When the control is first initialized, its value is equal to the default 175 * value of the DOM node. For <input> text controls, this default value is the 176 * value of the value attribute. For <textarea> elements, this default value is 177 * the value of the text node children of the element. 178 * 179 * * If the value has been changed through the DOM node (before the editor for 180 * the object is initialized), the value is stored as a simple string inside the 181 * mValue member of the TextControlState object. 182 * 183 * * If an editor has been initialized for the control, the value is set and 184 * retrievd via the nsIEditor interface, and is internally managed by the 185 * editor as the native anonymous content tree attached to the control's frame. 186 * 187 * * If the text control state object is unbound from the control's frame, the 188 * value is transferred to the mValue member variable, and will be managed there 189 * until a new frame is bound to the text editor state object. 190 */ 191 192 class RestoreSelectionState; 193 194 class TextControlState final : public SupportsWeakPtr { 195 public: 196 using Element = dom::Element; 197 using HTMLInputElement = dom::HTMLInputElement; 198 199 static TextControlState* Construct(TextControlElement* aOwningElement); 200 201 // Note that this does not run script actually because of `sHasShutDown` 202 // is set to true before calling `DeleteOrCacheForReuse()`. 203 MOZ_CAN_RUN_SCRIPT_BOUNDARY static void Shutdown(); 204 205 /** 206 * Destroy() deletes the instance immediately or later. 207 */ 208 MOZ_CAN_RUN_SCRIPT void Destroy(); 209 210 TextControlState() = delete; 211 explicit TextControlState(const TextControlState&) = delete; 212 TextControlState(TextControlState&&) = delete; 213 214 void operator=(const TextControlState&) = delete; 215 void operator=(TextControlState&&) = delete; 216 217 void Traverse(nsCycleCollectionTraversalCallback& cb); 218 MOZ_CAN_RUN_SCRIPT_BOUNDARY void Unlink(); 219 220 bool IsBusy() const { return !!mHandlingState || mValueTransferInProgress; } 221 222 MOZ_CAN_RUN_SCRIPT TextEditor* GetTextEditor(); 223 TextEditor* GetExtantTextEditor() const; 224 nsISelectionController* GetSelectionController() const; 225 nsFrameSelection* GetIndependentFrameSelection() const; 226 nsresult BindToFrame(nsTextControlFrame* aFrame); 227 MOZ_CAN_RUN_SCRIPT void UnbindFromFrame(nsTextControlFrame* aFrame); 228 [[nodiscard]] nsTextControlFrame* GetBoundFrame() const { 229 return mBoundFrame; 230 } 231 MOZ_CAN_RUN_SCRIPT nsresult PrepareEditor(const nsAString* aValue = nullptr); 232 void InitializeKeyboardEventListeners(); 233 234 /** 235 * OnEditActionHandled() is called when mTextEditor handles something 236 * and immediately before dispatching "input" event. 237 */ 238 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnEditActionHandled(); 239 240 enum class ValueSetterOption { 241 // The call is for setting value to initial one, computed one, etc. 242 ByInternalAPI, 243 // The value is changed by a call of setUserInput() API from chrome. 244 BySetUserInputAPI, 245 // The value is changed by changing value attribute of the element or 246 // something like setRangeText(). 247 ByContentAPI, 248 // The value is changed by setRangeText(). Intended to prevent silent 249 // selection range change. 250 BySetRangeTextAPI, 251 // Whether SetValueChanged should be called as a result of this value 252 // change. 253 SetValueChanged, 254 // Whether to move the cursor to end of the value (in the case when we have 255 // cached selection offsets), in the case when the value has changed. If 256 // this is not set and MoveCursorToBeginSetSelectionDirectionForward 257 // is not set, the cached selection offsets will simply be clamped to 258 // be within the length of the new value. In either case, if the value has 259 // not changed the cursor won't move. 260 // TODO(mbrodesser): update comment and enumerator identifier to reflect 261 // that also the direction is set to forward. 262 MoveCursorToEndIfValueChanged, 263 264 // The value change should preserve undo history. 265 PreserveUndoHistory, 266 267 // Whether it should be tried to move the cursor to the beginning of the 268 // text control and set the selection direction to "forward". 269 // TODO(mbrodesser): As soon as "none" is supported 270 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1541454), it should be set 271 // to "none" and only fall back to "forward" if the platform doesn't support 272 // it. 273 MoveCursorToBeginSetSelectionDirectionForward, 274 }; 275 using ValueSetterOptions = EnumSet<ValueSetterOption, uint32_t>; 276 277 /** 278 * SetValue() sets the value to aValue with replacing \r\n and \r with \n. 279 * 280 * @param aValue The new value. Can contain \r. 281 * @param aOldValue Optional. If you have already know current value, 282 * set this to it. However, this must not contain \r 283 * for the performance. 284 * @param aOptions See ValueSetterOption. 285 */ 286 [[nodiscard]] MOZ_CAN_RUN_SCRIPT bool SetValue( 287 const nsAString& aValue, const nsAString* aOldValue, 288 const ValueSetterOptions& aOptions); 289 [[nodiscard]] MOZ_CAN_RUN_SCRIPT bool SetValue( 290 const nsAString& aValue, const ValueSetterOptions& aOptions) { 291 return SetValue(aValue, nullptr, aOptions); 292 } 293 294 /** 295 * GetValue() returns current value either with or without TextEditor. 296 * The result never includes \r. 297 */ 298 void GetValue(nsAString& aValue, bool aForDisplay) const; 299 300 /** 301 * ValueEquals() is designed for internal use so that aValue shouldn't 302 * include \r character. It should be handled before calling this with 303 * nsContentUtils::PlatformToDOMLineBreaks(). 304 */ 305 bool ValueEquals(const nsAString& aValue) const; 306 // The following methods are for textarea element to use whether default 307 // value or not. 308 // XXX We might have to add assertion when it is into editable, 309 // or reconsider fixing bug 597525 to remove these. 310 void EmptyValue() { 311 if (!mValue.IsVoid()) { 312 mValue.Truncate(); 313 } 314 } 315 bool IsEmpty() const { return mValue.IsEmpty(); } 316 317 const nsAString& LastInteractiveValueIfLastChangeWasNonInteractive() const { 318 return mLastInteractiveValue; 319 } 320 // When an interactive value change happens, we clear mLastInteractiveValue 321 // because it's not needed (mValue is the new interactive value). 322 void ClearLastInteractiveValue() { mLastInteractiveValue.SetIsVoid(true); } 323 324 Element* GetRootNode(); 325 Element* GetPreviewNode(); 326 327 bool IsSingleLineTextControl() const { 328 return mTextCtrlElement->IsSingleLineTextControl(); 329 } 330 bool IsTextArea() const { return mTextCtrlElement->IsTextArea(); } 331 bool IsPasswordTextControl() const { 332 return mTextCtrlElement->IsPasswordTextControl(); 333 } 334 int32_t GetColsOrDefault() { return mTextCtrlElement->GetColsOrDefault(); } 335 int32_t GetWrapCols() { 336 int32_t wrapCols = mTextCtrlElement->GetWrapCols(); 337 MOZ_ASSERT(wrapCols >= 0); 338 return wrapCols; 339 } 340 int32_t GetRows() { return mTextCtrlElement->GetRows(); } 341 342 // preview methods 343 void SetPreviewText(const nsAString& aValue, bool aNotify); 344 void GetPreviewText(nsAString& aValue); 345 346 struct SelectionProperties { 347 public: 348 bool IsDefault() const { 349 return mStart == 0 && mEnd == 0 && 350 mDirection == SelectionDirection::Forward; 351 } 352 uint32_t GetStart() const { return mStart; } 353 bool SetStart(uint32_t value) { 354 uint32_t newValue = std::min(value, *mMaxLength); 355 return SetStartInternal(newValue); 356 } 357 uint32_t GetEnd() const { return mEnd; } 358 bool SetEnd(uint32_t value) { 359 uint32_t newValue = std::min(value, *mMaxLength); 360 return SetEndInternal(newValue); 361 } 362 void CollapseToStart() { 363 // 0 is always a fine value regardless of max length. 364 SetStartInternal(0); 365 SetEndInternal(0); 366 } 367 SelectionDirection GetDirection() const { return mDirection; } 368 bool SetDirection(SelectionDirection value) { 369 bool changed = mDirection != value; 370 mDirection = value; 371 mIsDirty |= changed; 372 return changed; 373 } 374 void SetMaxLength(uint32_t aMax) { 375 mMaxLength = Some(aMax); 376 // recompute against the new max length 377 SetStart(GetStart()); 378 SetEnd(GetEnd()); 379 } 380 bool HasMaxLength() { return mMaxLength.isSome(); } 381 382 // return true only if mStart, mEnd, or mDirection have been modified, 383 // or if SetIsDirty() was explicitly called. 384 bool IsDirty() const { return mIsDirty; } 385 void SetIsDirty() { mIsDirty = true; } 386 387 private: 388 bool SetStartInternal(uint32_t aNewValue) { 389 bool changed = mStart != aNewValue; 390 mStart = aNewValue; 391 mIsDirty |= changed; 392 return changed; 393 } 394 395 bool SetEndInternal(uint32_t aNewValue) { 396 bool changed = mEnd != aNewValue; 397 mEnd = aNewValue; 398 mIsDirty |= changed; 399 return changed; 400 } 401 402 uint32_t mStart = 0; 403 uint32_t mEnd = 0; 404 Maybe<uint32_t> mMaxLength; 405 bool mIsDirty = false; 406 SelectionDirection mDirection = SelectionDirection::Forward; 407 }; 408 409 bool IsSelectionCached() const { return mSelectionCached; } 410 SelectionProperties& GetSelectionProperties() { return mSelectionProperties; } 411 MOZ_CAN_RUN_SCRIPT void SetSelectionProperties(SelectionProperties& aProps); 412 bool HasNeverInitializedBefore() const { return !mEverInited; } 413 // Sync up our selection properties with our editor prior to being destroyed. 414 // This will invoke UnbindFromFrame() to ensure that we grab whatever 415 // selection state may be at the moment. 416 MOZ_CAN_RUN_SCRIPT void SyncUpSelectionPropertiesBeforeDestruction(); 417 418 // Get the selection range start and end points in our text. 419 void GetSelectionRange(uint32_t* aSelectionStart, uint32_t* aSelectionEnd, 420 ErrorResult& aRv); 421 422 // Get the selection direction 423 SelectionDirection GetSelectionDirection(ErrorResult& aRv); 424 425 enum class ScrollAfterSelection { No, Yes }; 426 427 // Set the selection range (start, end, direction). aEnd is allowed to be 428 // smaller than aStart; in that case aStart will be reset to the same value as 429 // aEnd. This basically implements 430 // https://html.spec.whatwg.org/multipage/forms.html#set-the-selection-range 431 // but with the start/end already coerced to zero if null (and without the 432 // special infinity value), and the direction already converted to a 433 // SelectionDirection. 434 // 435 // If we have a frame, this method will scroll the selection into view. 436 MOZ_CAN_RUN_SCRIPT void SetSelectionRange( 437 uint32_t aStart, uint32_t aEnd, SelectionDirection aDirection, 438 ErrorResult& aRv, 439 ScrollAfterSelection aScroll = ScrollAfterSelection::Yes); 440 441 // Set the selection range, but with an optional string for the direction. 442 // This will convert aDirection to an nsITextControlFrame::SelectionDirection 443 // and then call our other SetSelectionRange overload. 444 MOZ_CAN_RUN_SCRIPT void SetSelectionRange( 445 uint32_t aSelectionStart, uint32_t aSelectionEnd, 446 const dom::Optional<nsAString>& aDirection, ErrorResult& aRv, 447 ScrollAfterSelection aScroll = ScrollAfterSelection::Yes); 448 449 // Set the selection start. This basically implements the 450 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionstart 451 // setter. 452 MOZ_CAN_RUN_SCRIPT void SetSelectionStart( 453 const dom::Nullable<uint32_t>& aStart, ErrorResult& aRv); 454 455 // Set the selection end. This basically implements the 456 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionend 457 // setter. 458 MOZ_CAN_RUN_SCRIPT void SetSelectionEnd(const dom::Nullable<uint32_t>& aEnd, 459 ErrorResult& aRv); 460 461 // Get the selection direction as a string. This implements the 462 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection 463 // getter. 464 void GetSelectionDirectionString(nsAString& aDirection, ErrorResult& aRv); 465 466 // Set the selection direction. This basically implements the 467 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection 468 // setter. 469 MOZ_CAN_RUN_SCRIPT void SetSelectionDirection(const nsAString& aDirection, 470 ErrorResult& aRv); 471 472 // Set the range text. This basically implements 473 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-setrangetext 474 MOZ_CAN_RUN_SCRIPT void SetRangeText(const nsAString& aReplacement, 475 ErrorResult& aRv); 476 // The last two arguments are -1 if we don't know our selection range; 477 // otherwise they're the start and end of our selection range. 478 MOZ_CAN_RUN_SCRIPT void SetRangeText( 479 const nsAString& aReplacement, uint32_t aStart, uint32_t aEnd, 480 dom::SelectionMode aSelectMode, ErrorResult& aRv, 481 const Maybe<uint32_t>& aSelectionStart = Nothing(), 482 const Maybe<uint32_t>& aSelectionEnd = Nothing()); 483 484 private: 485 explicit TextControlState(TextControlElement* aOwningElement); 486 MOZ_CAN_RUN_SCRIPT ~TextControlState(); 487 488 /** 489 * Delete the instance or cache to reuse it if possible. 490 */ 491 MOZ_CAN_RUN_SCRIPT void DeleteOrCacheForReuse(); 492 493 MOZ_CAN_RUN_SCRIPT void UnlinkInternal(); 494 495 MOZ_CAN_RUN_SCRIPT void DestroyEditor(); 496 MOZ_CAN_RUN_SCRIPT void Clear(); 497 498 nsresult InitializeRootNode(); 499 500 void FinishedRestoringSelection(); 501 502 bool EditorHasComposition(); 503 504 /** 505 * SetValueWithTextEditor() modifies the editor value with mTextEditor. 506 * This may cause destroying mTextEditor, mBoundFrame, the TextControlState 507 * itself. Must be called when both mTextEditor and mBoundFrame are not 508 * nullptr. 509 * 510 * @param aHandlingSetValue Must be inner-most handling state for SetValue. 511 * @return false if fallible allocation failed. Otherwise, 512 * true. 513 */ 514 MOZ_CAN_RUN_SCRIPT bool SetValueWithTextEditor( 515 AutoTextControlHandlingState& aHandlingSetValue); 516 517 /** 518 * SetValueWithoutTextEditor() modifies the value without editor. I.e., 519 * modifying the value in this instance and mBoundFrame. Must be called 520 * when at least mTextEditor or mBoundFrame is nullptr. 521 * 522 * @param aHandlingSetValue Must be inner-most handling state for SetValue. 523 * @return false if fallible allocation failed. Otherwise, 524 * true. 525 */ 526 MOZ_CAN_RUN_SCRIPT bool SetValueWithoutTextEditor( 527 AutoTextControlHandlingState& aHandlingSetValue); 528 529 IMEContentObserver* GetIMEContentObserver() const; 530 531 // When this class handles something which may run script, this should be 532 // set to non-nullptr. If so, this class claims that it's busy and that 533 // prevents destroying TextControlState instance. 534 AutoTextControlHandlingState* mHandlingState = nullptr; 535 536 // The text control element owns this object, and ensures that this object 537 // has a smaller lifetime except the owner releases the instance while it 538 // does something with this. 539 TextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement; 540 RefPtr<TextInputSelectionController> mSelCon; 541 RefPtr<RestoreSelectionState> mRestoringSelection; 542 RefPtr<TextEditor> mTextEditor; 543 nsTextControlFrame* mBoundFrame = nullptr; 544 RefPtr<TextInputListener> mTextListener; 545 UniquePtr<PasswordMaskData> mPasswordMaskData; 546 547 nsString mValue{VoidString()}; // Void if there's no value. 548 549 // If our input's last value change was not interactive (as in, the value 550 // change was caused by a ValueChangeKind::UserInteraction), this is the value 551 // that the last interaction had. 552 nsString mLastInteractiveValue{VoidString()}; 553 554 SelectionProperties mSelectionProperties; 555 556 bool mEverInited : 1; // Have we ever been initialized? 557 bool mEditorInitialized : 1; 558 bool mValueTransferInProgress : 1; // Whether a value is being transferred to 559 // the frame 560 bool mSelectionCached : 1; // Whether mSelectionProperties is valid 561 562 friend class AutoTextControlHandlingState; 563 friend class PrepareEditorEvent; 564 friend class RestoreSelectionState; 565 }; 566 567 } // namespace mozilla 568 569 #endif // #ifndef mozilla_TextControlState_h