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