tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TextControlState.cpp (110419B)


      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 #include "TextControlState.h"
      8 
      9 #include "mozilla/Attributes.h"
     10 #include "mozilla/AutoRestore.h"
     11 #include "mozilla/CaretAssociationHint.h"
     12 #include "mozilla/ErrorResult.h"
     13 #include "mozilla/EventListenerManager.h"
     14 #include "mozilla/IMEContentObserver.h"
     15 #include "mozilla/IMEStateManager.h"
     16 #include "mozilla/InputEventOptions.h"
     17 #include "mozilla/KeyEventHandler.h"
     18 #include "mozilla/Maybe.h"
     19 #include "mozilla/NativeKeyBindingsType.h"
     20 #include "mozilla/Preferences.h"
     21 #include "mozilla/PresShell.h"
     22 #include "mozilla/ScrollContainerFrame.h"
     23 #include "mozilla/ScrollTypes.h"
     24 #include "mozilla/ShortcutKeys.h"
     25 #include "mozilla/StaticPrefs_dom.h"
     26 #include "mozilla/StaticPrefs_ui.h"
     27 #include "mozilla/TextComposition.h"
     28 #include "mozilla/TextEvents.h"
     29 #include "mozilla/TextInputListener.h"
     30 #include "mozilla/dom/Event.h"
     31 #include "mozilla/dom/HTMLInputElement.h"
     32 #include "mozilla/dom/HTMLTextAreaElement.h"
     33 #include "mozilla/dom/KeyboardEvent.h"
     34 #include "mozilla/dom/ScriptSettings.h"
     35 #include "mozilla/dom/Selection.h"
     36 #include "mozilla/dom/Text.h"
     37 #include "nsAttrValue.h"
     38 #include "nsAttrValueInlines.h"
     39 #include "nsBaseCommandController.h"
     40 #include "nsCOMPtr.h"
     41 #include "nsCaret.h"
     42 #include "nsContentCreatorFunctions.h"
     43 #include "nsContentUtils.h"
     44 #include "nsFocusManager.h"
     45 #include "nsFrameSelection.h"
     46 #include "nsGenericHTMLElement.h"
     47 #include "nsIController.h"
     48 #include "nsIControllers.h"
     49 #include "nsIDOMEventListener.h"
     50 #include "nsIDocumentEncoder.h"
     51 #include "nsIWidget.h"
     52 #include "nsPIDOMWindow.h"
     53 #include "nsServiceManagerUtils.h"
     54 #include "nsTextControlFrame.h"
     55 #include "nsTextNode.h"
     56 
     57 namespace mozilla {
     58 
     59 using namespace dom;
     60 using ValueSetterOption = TextControlState::ValueSetterOption;
     61 using ValueSetterOptions = TextControlState::ValueSetterOptions;
     62 
     63 /*****************************************************************************
     64 * TextControlElement
     65 *****************************************************************************/
     66 
     67 NS_IMPL_CYCLE_COLLECTION_CLASS(TextControlElement)
     68 
     69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
     70    TextControlElement, nsGenericHTMLFormControlElementWithState)
     71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     72 
     73 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
     74    TextControlElement, nsGenericHTMLFormControlElementWithState)
     75 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     76 
     77 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(
     78    TextControlElement, nsGenericHTMLFormControlElementWithState)
     79 
     80 /*static*/
     81 already_AddRefed<TextControlElement>
     82 TextControlElement::GetTextControlElementFromEditingHost(nsIContent* aHost) {
     83  if (!aHost) {
     84    return nullptr;
     85  }
     86 
     87  RefPtr<TextControlElement> parent =
     88      TextControlElement::FromNodeOrNull(aHost->GetParent());
     89  return parent.forget();
     90 }
     91 
     92 TextControlElement::FocusTristate TextControlElement::FocusState() {
     93  // We can't be focused if we aren't in a (composed) document
     94  Document* doc = GetComposedDoc();
     95  if (!doc) {
     96    return FocusTristate::eUnfocusable;
     97  }
     98 
     99  // first see if we are disabled or not. If disabled then do nothing.
    100  if (IsDisabled()) {
    101    return FocusTristate::eUnfocusable;
    102  }
    103 
    104  return IsInActiveTab(doc) ? FocusTristate::eActiveWindow
    105                            : FocusTristate::eInactiveWindow;
    106 }
    107 
    108 using ValueChangeKind = TextControlElement::ValueChangeKind;
    109 
    110 MOZ_CAN_RUN_SCRIPT inline nsresult SetEditorFlagsIfNecessary(
    111    EditorBase& aEditorBase, uint32_t aFlags) {
    112  if (aEditorBase.Flags() == aFlags) {
    113    return NS_OK;
    114  }
    115  return aEditorBase.SetFlags(aFlags);
    116 }
    117 
    118 /*****************************************************************************
    119 * mozilla::AutoInputEventSuppresser
    120 *****************************************************************************/
    121 
    122 class MOZ_STACK_CLASS AutoInputEventSuppresser final {
    123 public:
    124  explicit AutoInputEventSuppresser(TextEditor* aTextEditor)
    125      : mTextEditor(aTextEditor),
    126        // To protect against a reentrant call to SetValue, we check whether
    127        // another SetValue is already happening for this editor.  If it is,
    128        // we must wait until we unwind to re-enable oninput events.
    129        mOuterTransaction(aTextEditor->IsSuppressingDispatchingInputEvent()) {
    130    MOZ_ASSERT(mTextEditor);
    131    mTextEditor->SuppressDispatchingInputEvent(true);
    132  }
    133  ~AutoInputEventSuppresser() {
    134    mTextEditor->SuppressDispatchingInputEvent(mOuterTransaction);
    135  }
    136 
    137 private:
    138  RefPtr<TextEditor> mTextEditor;
    139  bool mOuterTransaction;
    140 };
    141 
    142 /*****************************************************************************
    143 * mozilla::RestoreSelectionState
    144 *****************************************************************************/
    145 
    146 class RestoreSelectionState : public Runnable {
    147 public:
    148  RestoreSelectionState(TextControlState* aState, nsTextControlFrame* aFrame)
    149      : Runnable("RestoreSelectionState"),
    150        mFrame(aFrame),
    151        mTextControlState(aState) {}
    152 
    153  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
    154    if (!mTextControlState) {
    155      return NS_OK;
    156    }
    157 
    158    AutoHideSelectionChanges hideSelectionChanges(
    159        mFrame->GetConstFrameSelection());
    160 
    161    if (mFrame) {
    162      // EnsureEditorInitialized and SetSelectionRange leads to
    163      // Selection::AddRangeAndSelectFramesAndNotifyListeners which flushes
    164      // Layout - need to block script to avoid nested PrepareEditor calls (bug
    165      // 642800).
    166      nsAutoScriptBlocker scriptBlocker;
    167      mFrame->EnsureEditorInitialized();
    168      TextControlState::SelectionProperties& properties =
    169          mTextControlState->GetSelectionProperties();
    170      if (properties.IsDirty()) {
    171        mFrame->SetSelectionRange(properties.GetStart(), properties.GetEnd(),
    172                                  properties.GetDirection());
    173      }
    174    }
    175 
    176    if (mTextControlState) {
    177      mTextControlState->FinishedRestoringSelection();
    178    }
    179    return NS_OK;
    180  }
    181 
    182  // Let the text editor tell us we're no longer relevant - avoids use of
    183  // AutoWeakFrame
    184  void Revoke() {
    185    mFrame = nullptr;
    186    mTextControlState = nullptr;
    187  }
    188 
    189 private:
    190  nsTextControlFrame* mFrame;
    191  TextControlState* mTextControlState;
    192 };
    193 
    194 /*****************************************************************************
    195 * mozilla::AutoRestoreEditorState
    196 *****************************************************************************/
    197 
    198 class MOZ_RAII AutoRestoreEditorState final {
    199 public:
    200  MOZ_CAN_RUN_SCRIPT explicit AutoRestoreEditorState(TextEditor* aTextEditor)
    201      : mTextEditor(aTextEditor),
    202        mSavedFlags(mTextEditor->Flags()),
    203        mSavedMaxLength(mTextEditor->MaxTextLength()),
    204        mSavedEchoingPasswordPrevented(
    205            mTextEditor->EchoingPasswordPrevented()) {
    206    MOZ_ASSERT(mTextEditor);
    207 
    208    // EditorBase::SetFlags() is a virtual method.  Even though it does nothing
    209    // if new flags and current flags are same, the calling cost causes
    210    // appearing the method in profile.  So, this class should check if it's
    211    // necessary to call.
    212    uint32_t flags = mSavedFlags;
    213    flags &= ~nsIEditor::eEditorReadonlyMask;
    214    if (mSavedFlags != flags) {
    215      // It's aTextEditor and whose lifetime must be guaranteed by the caller.
    216      MOZ_KnownLive(mTextEditor)->SetFlags(flags);
    217    }
    218    mTextEditor->PreventToEchoPassword();
    219    mTextEditor->SetMaxTextLength(-1);
    220  }
    221 
    222  MOZ_CAN_RUN_SCRIPT ~AutoRestoreEditorState() {
    223    if (!mSavedEchoingPasswordPrevented) {
    224      mTextEditor->AllowToEchoPassword();
    225    }
    226    mTextEditor->SetMaxTextLength(mSavedMaxLength);
    227    // mTextEditor's lifetime must be guaranteed by owner of the instance
    228    // since the constructor is marked as `MOZ_CAN_RUN_SCRIPT` and this is
    229    // a stack only class.
    230    SetEditorFlagsIfNecessary(MOZ_KnownLive(*mTextEditor), mSavedFlags);
    231  }
    232 
    233 private:
    234  TextEditor* mTextEditor;
    235  uint32_t mSavedFlags;
    236  int32_t mSavedMaxLength;
    237  bool mSavedEchoingPasswordPrevented;
    238 };
    239 
    240 /*****************************************************************************
    241 * mozilla::AutoDisableUndo
    242 *****************************************************************************/
    243 
    244 class MOZ_RAII AutoDisableUndo final {
    245 public:
    246  explicit AutoDisableUndo(TextEditor* aTextEditor)
    247      : mTextEditor(aTextEditor), mNumberOfMaximumTransactions(0) {
    248    MOZ_ASSERT(mTextEditor);
    249 
    250    mNumberOfMaximumTransactions =
    251        mTextEditor ? mTextEditor->NumberOfMaximumTransactions() : 0;
    252    DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
    253    NS_WARNING_ASSERTION(disabledUndoRedo,
    254                         "Failed to disable undo/redo transactions");
    255  }
    256 
    257  ~AutoDisableUndo() {
    258    // Don't change enable/disable of undo/redo if it's enabled after
    259    // it's disabled by the constructor because we shouldn't change
    260    // the maximum undo/redo count to the old value.
    261    if (mTextEditor->IsUndoRedoEnabled()) {
    262      return;
    263    }
    264    // If undo/redo was enabled, mNumberOfMaximumTransactions is -1 or lager
    265    // than 0.  Only when it's 0, it was disabled.
    266    if (mNumberOfMaximumTransactions) {
    267      DebugOnly<bool> enabledUndoRedo =
    268          mTextEditor->EnableUndoRedo(mNumberOfMaximumTransactions);
    269      NS_WARNING_ASSERTION(enabledUndoRedo,
    270                           "Failed to enable undo/redo transactions");
    271    } else {
    272      DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
    273      NS_WARNING_ASSERTION(disabledUndoRedo,
    274                           "Failed to disable undo/redo transactions");
    275    }
    276  }
    277 
    278 private:
    279  TextEditor* mTextEditor;
    280  int32_t mNumberOfMaximumTransactions;
    281 };
    282 
    283 static bool SuppressEventHandlers(nsPresContext* aPresContext) {
    284  bool suppressHandlers = false;
    285 
    286  if (aPresContext) {
    287    // Right now we only suppress event handlers and controller manipulation
    288    // when in a print preview or print context!
    289 
    290    // In the current implementation, we only paginate when
    291    // printing or in print preview.
    292 
    293    suppressHandlers = aPresContext->IsPaginated();
    294  }
    295 
    296  return suppressHandlers;
    297 }
    298 
    299 /*****************************************************************************
    300 * mozilla::TextInputSelectionController
    301 *****************************************************************************/
    302 
    303 class TextInputSelectionController final : public nsSupportsWeakReference,
    304                                           public nsISelectionController {
    305  ~TextInputSelectionController() = default;
    306 
    307 public:
    308  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    309  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextInputSelectionController,
    310                                           nsISelectionController)
    311 
    312  TextInputSelectionController(PresShell* aPresShell,
    313                               Element& aEditorRootAnonymousDiv);
    314 
    315  void SetScrollContainerFrame(ScrollContainerFrame* aScrollContainerFrame);
    316  nsFrameSelection* GetIndependentFrameSelection() const {
    317    return mFrameSelection;
    318  }
    319  // Will return null if !mFrameSelection.
    320  Selection* GetSelection(SelectionType aSelectionType);
    321 
    322  // NSISELECTIONCONTROLLER INTERFACES
    323  NS_IMETHOD SetDisplaySelection(int16_t toggle) override;
    324  NS_IMETHOD GetDisplaySelection(int16_t* _retval) override;
    325  NS_IMETHOD SetSelectionFlags(int16_t aInEnable) override;
    326  NS_IMETHOD GetSelectionFlags(int16_t* aOutEnable) override;
    327  NS_IMETHOD GetSelectionFromScript(RawSelectionType aRawSelectionType,
    328                                    Selection** aSelection) override;
    329  Selection* GetSelection(RawSelectionType aRawSelectionType) override;
    330  MOZ_CAN_RUN_SCRIPT NS_IMETHOD ScrollSelectionIntoView(
    331      RawSelectionType aRawSelectionType, SelectionRegion aRegion,
    332      ControllerScrollFlags aFlags) override;
    333  NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
    334  nsresult RepaintSelection(nsPresContext* aPresContext,
    335                            SelectionType aSelectionType);
    336  NS_IMETHOD SetCaretEnabled(bool enabled) override;
    337  NS_IMETHOD SetCaretReadOnly(bool aReadOnly) override;
    338  NS_IMETHOD GetCaretEnabled(bool* _retval) override;
    339  NS_IMETHOD GetCaretVisible(bool* _retval) override;
    340  NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility) override;
    341  MOZ_CAN_RUN_SCRIPT NS_IMETHOD PhysicalMove(int16_t aDirection,
    342                                             int16_t aAmount,
    343                                             bool aExtend) override;
    344  MOZ_CAN_RUN_SCRIPT NS_IMETHOD CharacterMove(bool aForward,
    345                                              bool aExtend) override;
    346  MOZ_CAN_RUN_SCRIPT NS_IMETHOD WordMove(bool aForward, bool aExtend) override;
    347  MOZ_CAN_RUN_SCRIPT NS_IMETHOD LineMove(bool aForward, bool aExtend) override;
    348  MOZ_CAN_RUN_SCRIPT NS_IMETHOD IntraLineMove(bool aForward,
    349                                              bool aExtend) override;
    350  MOZ_CAN_RUN_SCRIPT NS_IMETHOD PageMove(bool aForward, bool aExtend) override;
    351  NS_IMETHOD CompleteScroll(bool aForward) override;
    352  MOZ_CAN_RUN_SCRIPT NS_IMETHOD CompleteMove(bool aForward,
    353                                             bool aExtend) override;
    354  NS_IMETHOD ScrollPage(bool aForward) override;
    355  NS_IMETHOD ScrollLine(bool aForward) override;
    356  NS_IMETHOD ScrollCharacter(bool aRight) override;
    357  void SelectionWillTakeFocus() override;
    358  void SelectionWillLoseFocus() override;
    359  using nsISelectionController::ScrollSelectionIntoView;
    360 
    361 private:
    362  RefPtr<nsFrameSelection> mFrameSelection;
    363  ScrollContainerFrame* mScrollContainerFrame = nullptr;
    364  nsWeakPtr mPresShellWeak;
    365 };
    366 
    367 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextInputSelectionController)
    368 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextInputSelectionController)
    369 NS_INTERFACE_TABLE_HEAD(TextInputSelectionController)
    370  NS_INTERFACE_TABLE(TextInputSelectionController, nsISelectionController,
    371                     nsISelectionDisplay, nsISupportsWeakReference)
    372  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(TextInputSelectionController)
    373 NS_INTERFACE_MAP_END
    374 
    375 NS_IMPL_CYCLE_COLLECTION_WEAK(TextInputSelectionController, mFrameSelection)
    376 
    377 TextInputSelectionController::TextInputSelectionController(
    378    PresShell* aPresShell, Element& aEditorRootAnonymousDiv) {
    379  if (aPresShell) {
    380    const bool accessibleCaretEnabled = PresShell::AccessibleCaretEnabled(
    381        aEditorRootAnonymousDiv.OwnerDoc()->GetDocShell());
    382    mFrameSelection = new nsFrameSelection(aPresShell, accessibleCaretEnabled,
    383                                           &aEditorRootAnonymousDiv);
    384    mPresShellWeak = do_GetWeakReference(aPresShell);
    385  }
    386 }
    387 
    388 void TextInputSelectionController::SetScrollContainerFrame(
    389    ScrollContainerFrame* aScrollContainerFrame) {
    390  mScrollContainerFrame = aScrollContainerFrame;
    391  if (!mScrollContainerFrame && mFrameSelection) {
    392    mFrameSelection->DisconnectFromPresShell();
    393    mFrameSelection = nullptr;
    394  }
    395 }
    396 
    397 Selection* TextInputSelectionController::GetSelection(
    398    SelectionType aSelectionType) {
    399  if (!mFrameSelection) {
    400    return nullptr;
    401  }
    402 
    403  return mFrameSelection->GetSelection(aSelectionType);
    404 }
    405 
    406 NS_IMETHODIMP
    407 TextInputSelectionController::SetDisplaySelection(int16_t aToggle) {
    408  if (!mFrameSelection) {
    409    return NS_ERROR_NULL_POINTER;
    410  }
    411  mFrameSelection->SetDisplaySelection(aToggle);
    412  return NS_OK;
    413 }
    414 
    415 NS_IMETHODIMP
    416 TextInputSelectionController::GetDisplaySelection(int16_t* aToggle) {
    417  if (!mFrameSelection) {
    418    return NS_ERROR_NULL_POINTER;
    419  }
    420  *aToggle = mFrameSelection->GetDisplaySelection();
    421  return NS_OK;
    422 }
    423 
    424 NS_IMETHODIMP
    425 TextInputSelectionController::SetSelectionFlags(int16_t aToggle) {
    426  return NS_OK;  // stub this out. not used in input
    427 }
    428 
    429 NS_IMETHODIMP
    430 TextInputSelectionController::GetSelectionFlags(int16_t* aOutEnable) {
    431  *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
    432  return NS_OK;
    433 }
    434 
    435 NS_IMETHODIMP
    436 TextInputSelectionController::GetSelectionFromScript(
    437    RawSelectionType aRawSelectionType, Selection** aSelection) {
    438  if (!mFrameSelection) {
    439    return NS_ERROR_NULL_POINTER;
    440  }
    441 
    442  *aSelection =
    443      mFrameSelection->GetSelection(ToSelectionType(aRawSelectionType));
    444 
    445  // GetSelection() fails only when aRawSelectionType is invalid value.
    446  if (!(*aSelection)) {
    447    return NS_ERROR_INVALID_ARG;
    448  }
    449 
    450  NS_ADDREF(*aSelection);
    451  return NS_OK;
    452 }
    453 
    454 Selection* TextInputSelectionController::GetSelection(
    455    RawSelectionType aRawSelectionType) {
    456  return GetSelection(ToSelectionType(aRawSelectionType));
    457 }
    458 
    459 NS_IMETHODIMP
    460 TextInputSelectionController::ScrollSelectionIntoView(
    461    RawSelectionType aRawSelectionType, SelectionRegion aRegion,
    462    ControllerScrollFlags aFlags) {
    463  if (!mFrameSelection) {
    464    return NS_ERROR_NULL_POINTER;
    465  }
    466  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    467  return frameSelection->ScrollSelectionIntoView(
    468      ToSelectionType(aRawSelectionType), aRegion, aFlags);
    469 }
    470 
    471 NS_IMETHODIMP
    472 TextInputSelectionController::RepaintSelection(
    473    RawSelectionType aRawSelectionType) {
    474  if (!mFrameSelection) {
    475    return NS_ERROR_NULL_POINTER;
    476  }
    477  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    478  return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
    479 }
    480 
    481 nsresult TextInputSelectionController::RepaintSelection(
    482    nsPresContext* aPresContext, SelectionType aSelectionType) {
    483  if (!mFrameSelection) {
    484    return NS_ERROR_NULL_POINTER;
    485  }
    486  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    487  return frameSelection->RepaintSelection(aSelectionType);
    488 }
    489 
    490 NS_IMETHODIMP
    491 TextInputSelectionController::SetCaretEnabled(bool enabled) {
    492  if (!mPresShellWeak) {
    493    return NS_ERROR_NOT_INITIALIZED;
    494  }
    495  RefPtr<PresShell> presShell = do_QueryReferent(mPresShellWeak);
    496  if (!presShell) {
    497    return NS_ERROR_FAILURE;
    498  }
    499 
    500  // tell the pres shell to enable the caret, rather than settings its
    501  // visibility directly. this way the presShell's idea of caret visibility is
    502  // maintained.
    503  presShell->SetCaretEnabled(enabled);
    504 
    505  return NS_OK;
    506 }
    507 
    508 NS_IMETHODIMP
    509 TextInputSelectionController::SetCaretReadOnly(bool aReadOnly) {
    510  if (!mPresShellWeak) {
    511    return NS_ERROR_NOT_INITIALIZED;
    512  }
    513  nsresult rv;
    514  RefPtr<PresShell> presShell = do_QueryReferent(mPresShellWeak, &rv);
    515  if (!presShell) {
    516    return NS_ERROR_FAILURE;
    517  }
    518  RefPtr<nsCaret> caret = presShell->GetCaret();
    519  if (!caret) {
    520    return NS_ERROR_FAILURE;
    521  }
    522 
    523  if (!mFrameSelection) {
    524    return NS_ERROR_FAILURE;
    525  }
    526 
    527  caret->SetCaretReadOnly(aReadOnly);
    528  return NS_OK;
    529 }
    530 
    531 NS_IMETHODIMP
    532 TextInputSelectionController::GetCaretEnabled(bool* _retval) {
    533  return GetCaretVisible(_retval);
    534 }
    535 
    536 NS_IMETHODIMP
    537 TextInputSelectionController::GetCaretVisible(bool* _retval) {
    538  if (!mPresShellWeak) {
    539    return NS_ERROR_NOT_INITIALIZED;
    540  }
    541  nsresult rv;
    542  RefPtr<PresShell> presShell = do_QueryReferent(mPresShellWeak, &rv);
    543  if (!presShell) {
    544    return NS_ERROR_FAILURE;
    545  }
    546  RefPtr<nsCaret> caret = presShell->GetCaret();
    547  if (!caret) {
    548    return NS_ERROR_FAILURE;
    549  }
    550  *_retval = caret->IsVisible();
    551  return NS_OK;
    552 }
    553 
    554 NS_IMETHODIMP
    555 TextInputSelectionController::SetCaretVisibilityDuringSelection(
    556    bool aVisibility) {
    557  if (!mPresShellWeak) {
    558    return NS_ERROR_NOT_INITIALIZED;
    559  }
    560  nsresult rv;
    561  RefPtr<PresShell> presShell = do_QueryReferent(mPresShellWeak, &rv);
    562  if (!presShell) {
    563    return NS_ERROR_FAILURE;
    564  }
    565  RefPtr<nsCaret> caret = presShell->GetCaret();
    566  if (!caret) {
    567    return NS_ERROR_FAILURE;
    568  }
    569 
    570  caret->SetVisibilityDuringSelection(aVisibility);
    571  return NS_OK;
    572 }
    573 
    574 NS_IMETHODIMP
    575 TextInputSelectionController::PhysicalMove(int16_t aDirection, int16_t aAmount,
    576                                           bool aExtend) {
    577  if (!mFrameSelection) {
    578    return NS_ERROR_NULL_POINTER;
    579  }
    580  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    581  return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
    582 }
    583 
    584 NS_IMETHODIMP
    585 TextInputSelectionController::CharacterMove(bool aForward, bool aExtend) {
    586  if (!mFrameSelection) {
    587    return NS_ERROR_NULL_POINTER;
    588  }
    589  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    590  return frameSelection->CharacterMove(aForward, aExtend);
    591 }
    592 
    593 NS_IMETHODIMP
    594 TextInputSelectionController::WordMove(bool aForward, bool aExtend) {
    595  if (!mFrameSelection) {
    596    return NS_ERROR_NULL_POINTER;
    597  }
    598  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    599  return frameSelection->WordMove(aForward, aExtend);
    600 }
    601 
    602 NS_IMETHODIMP
    603 TextInputSelectionController::LineMove(bool aForward, bool aExtend) {
    604  if (!mFrameSelection) {
    605    return NS_ERROR_NULL_POINTER;
    606  }
    607  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    608  nsresult result = frameSelection->LineMove(aForward, aExtend);
    609  if (NS_FAILED(result)) {
    610    result = CompleteMove(aForward, aExtend);
    611  }
    612  return result;
    613 }
    614 
    615 NS_IMETHODIMP
    616 TextInputSelectionController::IntraLineMove(bool aForward, bool aExtend) {
    617  if (!mFrameSelection) {
    618    return NS_ERROR_NULL_POINTER;
    619  }
    620  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    621  return frameSelection->IntraLineMove(aForward, aExtend);
    622 }
    623 
    624 NS_IMETHODIMP
    625 TextInputSelectionController::PageMove(bool aForward, bool aExtend) {
    626  // expected behavior for PageMove is to scroll AND move the caret
    627  // and to remain relative position of the caret in view. see Bug 4302.
    628  if (mScrollContainerFrame) {
    629    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    630    // We won't scroll parent scrollable element of mScrollContainerFrame.
    631    // Therefore, this may be handled when mScrollContainerFrame is completely
    632    // outside of the view. In such case, user may be confused since they might
    633    // have wanted to scroll a parent scrollable element. For making clearer
    634    // which element handles PageDown/PageUp, we should move selection into view
    635    // even if selection is not changed.
    636    return frameSelection->PageMove(aForward, aExtend, mScrollContainerFrame,
    637                                    nsFrameSelection::SelectionIntoView::Yes);
    638  }
    639  // Similarly, if there is no scrollable frame, we should move the editor
    640  // frame into the view for making it clearer which element handles
    641  // PageDown/PageUp.
    642  return ScrollSelectionIntoView(SelectionType::eNormal,
    643                                 nsISelectionController::SELECTION_FOCUS_REGION,
    644                                 SelectionScrollMode::SyncFlush);
    645 }
    646 
    647 NS_IMETHODIMP
    648 TextInputSelectionController::CompleteScroll(bool aForward) {
    649  if (!mScrollContainerFrame) {
    650    return NS_ERROR_NOT_INITIALIZED;
    651  }
    652 
    653  mScrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
    654                                  ScrollUnit::WHOLE, ScrollMode::Instant);
    655  return NS_OK;
    656 }
    657 
    658 NS_IMETHODIMP
    659 TextInputSelectionController::CompleteMove(bool aForward, bool aExtend) {
    660  if (NS_WARN_IF(!mFrameSelection)) {
    661    return NS_ERROR_NULL_POINTER;
    662  }
    663  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    664 
    665  // grab the parent / root DIV for this text widget
    666  Element* const parentDIV =
    667      frameSelection->GetIndependentSelectionRootElement();
    668  if (!parentDIV) {
    669    return NS_ERROR_UNEXPECTED;
    670  }
    671 
    672  // make the caret be either at the very beginning (0) or the very end
    673  uint32_t offset = 0;
    674  CaretAssociationHint hint = CaretAssociationHint::Before;
    675  if (aForward) {
    676    offset = parentDIV->GetChildCount();
    677 
    678    // Prevent the caret from being placed after the last
    679    // BR node in the content tree!
    680 
    681    if (offset) {
    682      nsIContent* child = parentDIV->GetLastChild();
    683 
    684      if (child->IsHTMLElement(nsGkAtoms::br)) {
    685        --offset;
    686        hint = CaretAssociationHint::After;  // for Bug 106855
    687      }
    688    }
    689  }
    690 
    691  const OwningNonNull<Element> pinnedParentDIV(*parentDIV);
    692  const nsFrameSelection::FocusMode focusMode =
    693      aExtend ? nsFrameSelection::FocusMode::kExtendSelection
    694              : nsFrameSelection::FocusMode::kCollapseToNewPoint;
    695  frameSelection->HandleClick(pinnedParentDIV, offset, offset, focusMode, hint);
    696 
    697  // if we got this far, attempt to scroll no matter what the above result is
    698  return CompleteScroll(aForward);
    699 }
    700 
    701 NS_IMETHODIMP
    702 TextInputSelectionController::ScrollPage(bool aForward) {
    703  if (!mScrollContainerFrame) {
    704    return NS_ERROR_NOT_INITIALIZED;
    705  }
    706 
    707  mScrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
    708                                  ScrollUnit::PAGES, ScrollMode::Smooth);
    709  return NS_OK;
    710 }
    711 
    712 NS_IMETHODIMP
    713 TextInputSelectionController::ScrollLine(bool aForward) {
    714  if (!mScrollContainerFrame) {
    715    return NS_ERROR_NOT_INITIALIZED;
    716  }
    717 
    718  mScrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
    719                                  ScrollUnit::LINES, ScrollMode::Smooth);
    720  return NS_OK;
    721 }
    722 
    723 NS_IMETHODIMP
    724 TextInputSelectionController::ScrollCharacter(bool aRight) {
    725  if (!mScrollContainerFrame) {
    726    return NS_ERROR_NOT_INITIALIZED;
    727  }
    728 
    729  mScrollContainerFrame->ScrollBy(nsIntPoint(aRight ? 1 : -1, 0),
    730                                  ScrollUnit::LINES, ScrollMode::Smooth);
    731  return NS_OK;
    732 }
    733 
    734 void TextInputSelectionController::SelectionWillTakeFocus() {
    735  if (mFrameSelection) {
    736    if (PresShell* shell = mFrameSelection->GetPresShell()) {
    737      // text input selection always considers to move the
    738      // selection.
    739      shell->FrameSelectionWillTakeFocus(
    740          *mFrameSelection,
    741          StaticPrefs::dom_selection_mimic_chrome_tostring_enabled()
    742              ? PresShell::CanMoveLastSelectionForToString::Yes
    743              : PresShell::CanMoveLastSelectionForToString::No);
    744    }
    745  }
    746 }
    747 
    748 void TextInputSelectionController::SelectionWillLoseFocus() {
    749  if (mFrameSelection) {
    750    if (PresShell* shell = mFrameSelection->GetPresShell()) {
    751      shell->FrameSelectionWillLoseFocus(*mFrameSelection);
    752    }
    753  }
    754 }
    755 
    756 /*****************************************************************************
    757 * mozilla::TextInputListener
    758 *****************************************************************************/
    759 
    760 TextInputListener::TextInputListener(TextControlElement* aTxtCtrlElement)
    761    : mFrame(nullptr),
    762      mTxtCtrlElement(aTxtCtrlElement),
    763      mTextControlState(aTxtCtrlElement ? aTxtCtrlElement->GetTextControlState()
    764                                        : nullptr),
    765      mSelectionWasCollapsed(true),
    766      mHadUndoItems(false),
    767      mHadRedoItems(false),
    768      mSettingValue(false),
    769      mSetValueChanged(true),
    770      mListeningToSelectionChange(false) {}
    771 
    772 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextInputListener)
    773 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextInputListener)
    774 
    775 NS_INTERFACE_MAP_BEGIN(TextInputListener)
    776  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    777  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    778  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
    779  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(TextInputListener)
    780 NS_INTERFACE_MAP_END
    781 
    782 NS_IMPL_CYCLE_COLLECTION_CLASS(TextInputListener)
    783 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TextInputListener)
    784  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
    785 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    786 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TextInputListener)
    787 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    788 
    789 void TextInputListener::OnSelectionChange(Selection& aSelection,
    790                                          int16_t aReason) {
    791  if (!mListeningToSelectionChange) {
    792    return;
    793  }
    794 
    795  AutoWeakFrame weakFrame = mFrame;
    796 
    797  // Fire the select event
    798  // The specs don't exactly say when we should fire the select event.
    799  // IE: Whenever you add/remove a character to/from the selection. Also
    800  //     each time for select all. Also if you get to the end of the text
    801  //     field you will get new event for each keypress or a continuous
    802  //     stream of events if you use the mouse. IE will fire select event
    803  //     when the selection collapses to nothing if you are holding down
    804  //     the shift or mouse button.
    805  // Mozilla: If we have non-empty selection we will fire a new event for each
    806  //          keypress (or mouseup) if the selection changed. Mozilla will also
    807  //          create the event each time select all is called, even if
    808  //          everything was previously selected, because technically select all
    809  //          will first collapse and then extend. Mozilla will never create an
    810  //          event if the selection collapses to nothing.
    811  // FYI: If you want to skip dispatching eFormSelect event and if there are no
    812  //      event listeners, you can refer
    813  //      nsPIDOMWindow::HasFormSelectEventListeners(), but be careful about
    814  //      some C++ event handlers, e.g., HTMLTextAreaElement::PostHandleEvent().
    815  bool collapsed = aSelection.IsCollapsed();
    816  if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
    817                                nsISelectionListener::KEYPRESS_REASON |
    818                                nsISelectionListener::SELECTALL_REASON))) {
    819    if (nsCOMPtr<nsIContent> content = mFrame->GetContent()) {
    820      if (nsCOMPtr<Document> doc = content->GetComposedDoc()) {
    821        if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
    822          nsEventStatus status = nsEventStatus_eIgnore;
    823          WidgetEvent event(true, eFormSelect);
    824 
    825          presShell->HandleEventWithTarget(&event, mFrame, content, &status);
    826        }
    827      }
    828    }
    829  }
    830 
    831  // if the collapsed state did not change, don't fire notifications
    832  if (collapsed == mSelectionWasCollapsed) {
    833    return;
    834  }
    835 
    836  mSelectionWasCollapsed = collapsed;
    837 
    838  if (!weakFrame.IsAlive() || !mFrame ||
    839      nsFocusManager::GetFocusedElementStatic() != mFrame->GetContent()) {
    840    return;
    841  }
    842 
    843  UpdateTextInputCommands(u"select"_ns);
    844 }
    845 
    846 MOZ_CAN_RUN_SCRIPT
    847 static void DoCommandCallback(Command aCommand, void* aData) {
    848  nsTextControlFrame* frame = static_cast<nsTextControlFrame*>(aData);
    849  nsIContent* content = frame->GetContent();
    850 
    851  nsCOMPtr<nsIControllers> controllers;
    852  HTMLInputElement* input = HTMLInputElement::FromNode(content);
    853  if (input) {
    854    input->GetControllers(getter_AddRefs(controllers));
    855  } else {
    856    HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(content);
    857 
    858    if (textArea) {
    859      textArea->GetControllers(getter_AddRefs(controllers));
    860    }
    861  }
    862 
    863  if (!controllers) {
    864    NS_WARNING("Could not get controllers");
    865    return;
    866  }
    867 
    868  const char* commandStr = WidgetKeyboardEvent::GetCommandStr(aCommand);
    869 
    870  nsCOMPtr<nsIController> controller;
    871  controllers->GetControllerForCommand(commandStr, getter_AddRefs(controller));
    872  if (!controller) {
    873    return;
    874  }
    875 
    876  bool commandEnabled;
    877  if (NS_WARN_IF(NS_FAILED(
    878          controller->IsCommandEnabled(commandStr, &commandEnabled)))) {
    879    return;
    880  }
    881  if (commandEnabled) {
    882    controller->DoCommand(commandStr);
    883  }
    884 }
    885 
    886 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
    887 TextInputListener::HandleEvent(Event* aEvent) {
    888  if (aEvent->DefaultPrevented()) {
    889    return NS_OK;
    890  }
    891 
    892  if (!aEvent->IsTrusted()) {
    893    return NS_OK;
    894  }
    895 
    896  RefPtr<KeyboardEvent> keyEvent = aEvent->AsKeyboardEvent();
    897  if (!keyEvent) {
    898    return NS_ERROR_UNEXPECTED;
    899  }
    900 
    901  WidgetKeyboardEvent* widgetKeyEvent =
    902      aEvent->WidgetEventPtr()->AsKeyboardEvent();
    903  if (!widgetKeyEvent) {
    904    return NS_ERROR_UNEXPECTED;
    905  }
    906 
    907  {
    908    auto* input = HTMLInputElement::FromNode(mTxtCtrlElement);
    909    if (input && input->StepsInputValue(*widgetKeyEvent)) {
    910      // As an special case, don't handle key events that would step the value
    911      // of our <input type=number>.
    912      return NS_OK;
    913    }
    914  }
    915 
    916  auto ExecuteOurShortcutKeys = [&](TextControlElement& aTextControlElement)
    917                                    MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> bool {
    918    KeyEventHandler* keyHandlers = ShortcutKeys::GetHandlers(
    919        aTextControlElement.IsTextArea() ? HandlerType::eTextArea
    920                                         : HandlerType::eInput);
    921 
    922    RefPtr<nsAtom> eventTypeAtom =
    923        ShortcutKeys::ConvertEventToDOMEventType(widgetKeyEvent);
    924    for (KeyEventHandler* handler = keyHandlers; handler;
    925         handler = handler->GetNextHandler()) {
    926      if (!handler->EventTypeEquals(eventTypeAtom)) {
    927        continue;
    928      }
    929 
    930      if (!handler->KeyEventMatched(keyEvent, 0, IgnoreModifierState())) {
    931        continue;
    932      }
    933 
    934      // XXX Do we execute only one handler even if the handler neither stops
    935      //     propagation nor prevents default of the event?
    936      nsresult rv = handler->ExecuteHandler(&aTextControlElement, aEvent);
    937      if (NS_SUCCEEDED(rv)) {
    938        return true;
    939      }
    940    }
    941    return false;
    942  };
    943 
    944  auto ExecuteNativeKeyBindings =
    945      [&](TextControlElement& aTextControlElement)
    946          MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> bool {
    947    if (widgetKeyEvent->mMessage != eKeyPress) {
    948      return false;
    949    }
    950 
    951    NativeKeyBindingsType nativeKeyBindingsType =
    952        aTextControlElement.IsTextArea()
    953            ? NativeKeyBindingsType::MultiLineEditor
    954            : NativeKeyBindingsType::SingleLineEditor;
    955 
    956    nsIWidget* widget = widgetKeyEvent->mWidget;
    957    // If the event is created by chrome script, the widget is nullptr.
    958    if (MOZ_UNLIKELY(!widget)) {
    959      widget = mFrame->GetNearestWidget();
    960      if (MOZ_UNLIKELY(NS_WARN_IF(!widget))) {
    961        return false;
    962      }
    963    }
    964 
    965    // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
    966    // If the event is created by chrome script, it is nullptr but we need to
    967    // execute native key bindings.  Therefore, we need to set widget to
    968    // WidgetEvent::mWidget temporarily.
    969    AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(widgetKeyEvent->mWidget);
    970    widgetKeyEvent->mWidget = widget;
    971    if (widgetKeyEvent->ExecuteEditCommands(nativeKeyBindingsType,
    972                                            DoCommandCallback, mFrame)) {
    973      aEvent->PreventDefault();
    974      return true;
    975    }
    976    return false;
    977  };
    978 
    979  OwningNonNull<TextControlElement> textControlElement(*mTxtCtrlElement);
    980  if (StaticPrefs::
    981          ui_key_textcontrol_prefer_native_key_bindings_over_builtin_shortcut_key_definitions()) {
    982    if (!ExecuteNativeKeyBindings(textControlElement)) {
    983      ExecuteOurShortcutKeys(textControlElement);
    984    }
    985  } else {
    986    if (!ExecuteOurShortcutKeys(textControlElement)) {
    987      ExecuteNativeKeyBindings(textControlElement);
    988    }
    989  }
    990  return NS_OK;
    991 }
    992 
    993 nsresult TextInputListener::OnEditActionHandled(TextEditor& aTextEditor) {
    994  // Update the undo / redo menus
    995  //
    996  size_t numUndoItems = aTextEditor.NumberOfUndoItems();
    997  size_t numRedoItems = aTextEditor.NumberOfRedoItems();
    998  if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
    999      (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
   1000    // Modify the menu if undo or redo items are different
   1001    UpdateTextInputCommands(u"undo"_ns);
   1002 
   1003    mHadUndoItems = numUndoItems != 0;
   1004    mHadRedoItems = numRedoItems != 0;
   1005  }
   1006 
   1007  HandleValueChanged(aTextEditor);
   1008 
   1009  return mTextControlState ? mTextControlState->OnEditActionHandled() : NS_OK;
   1010 }
   1011 
   1012 void TextInputListener::HandleValueChanged(TextEditor& aTextEditor) {
   1013  // Make sure we know we were changed (do NOT set this to false if there are
   1014  // no undo items; JS could change the value and we'd still need to save it)
   1015  if (mSetValueChanged) {
   1016    mTxtCtrlElement->SetValueChanged(true);
   1017  }
   1018 
   1019  if (!mSettingValue) {
   1020    // NOTE(emilio): execCommand might get here even though it might not be a
   1021    // "proper" user-interactive change. Might be worth reconsidering which
   1022    // ValueChangeKind are we passing down.
   1023    mTxtCtrlElement->OnValueChanged(ValueChangeKind::UserInteraction,
   1024                                    aTextEditor.IsEmpty(), nullptr);
   1025    if (mTextControlState) {
   1026      mTextControlState->ClearLastInteractiveValue();
   1027    }
   1028  }
   1029 }
   1030 
   1031 nsresult TextInputListener::UpdateTextInputCommands(
   1032    const nsAString& aCommandsToUpdate) {
   1033  nsCOMPtr<Document> doc = mTxtCtrlElement->GetComposedDoc();
   1034  if (NS_WARN_IF(!doc)) {
   1035    return NS_ERROR_FAILURE;
   1036  }
   1037  nsPIDOMWindowOuter* domWindow = doc->GetWindow();
   1038  if (NS_WARN_IF(!domWindow)) {
   1039    return NS_ERROR_FAILURE;
   1040  }
   1041  domWindow->UpdateCommands(aCommandsToUpdate);
   1042  return NS_OK;
   1043 }
   1044 
   1045 /*****************************************************************************
   1046 * mozilla::AutoTextControlHandlingState
   1047 *
   1048 * This class is temporarily created in the stack and can manage nested
   1049 * handling state of TextControlState.  While this instance exists, lifetime of
   1050 * TextControlState which created the instance is guaranteed.  In other words,
   1051 * you can use this class as "kungFuDeathGrip" for TextControlState.
   1052 *****************************************************************************/
   1053 
   1054 enum class TextControlAction {
   1055  CommitComposition,
   1056  Destructor,
   1057  PrepareEditor,
   1058  SetRangeText,
   1059  SetSelectionRange,
   1060  SetValue,
   1061  UnbindFromFrame,
   1062  Unlink,
   1063 };
   1064 
   1065 class MOZ_STACK_CLASS AutoTextControlHandlingState {
   1066 public:
   1067  AutoTextControlHandlingState() = delete;
   1068  explicit AutoTextControlHandlingState(const AutoTextControlHandlingState&) =
   1069      delete;
   1070  AutoTextControlHandlingState(AutoTextControlHandlingState&&) = delete;
   1071  void operator=(AutoTextControlHandlingState&) = delete;
   1072  void operator=(const AutoTextControlHandlingState&) = delete;
   1073 
   1074  /**
   1075   * Generic constructor.  If TextControlAction does not require additional
   1076   * data, must use this constructor.
   1077   */
   1078  MOZ_CAN_RUN_SCRIPT AutoTextControlHandlingState(
   1079      TextControlState& aTextControlState, TextControlAction aTextControlAction)
   1080      : mParent(aTextControlState.mHandlingState),
   1081        mTextControlState(aTextControlState),
   1082        mTextCtrlElement(aTextControlState.mTextCtrlElement),
   1083        mTextInputListener(aTextControlState.mTextListener),
   1084        mTextControlAction(aTextControlAction) {
   1085    MOZ_ASSERT(aTextControlAction != TextControlAction::SetValue,
   1086               "Use specific constructor");
   1087    MOZ_DIAGNOSTIC_ASSERT_IF(
   1088        !aTextControlState.mTextListener,
   1089        !aTextControlState.mBoundFrame || !aTextControlState.mTextEditor);
   1090    mTextControlState.mHandlingState = this;
   1091    if (Is(TextControlAction::CommitComposition)) {
   1092      MOZ_ASSERT(mParent);
   1093      MOZ_ASSERT(mParent->Is(TextControlAction::SetValue));
   1094      // If we're trying to commit composition before handling SetValue,
   1095      // the parent old values will be outdated so that we need to clear
   1096      // them.
   1097      mParent->InvalidateOldValue();
   1098    }
   1099  }
   1100 
   1101  /**
   1102   * TextControlAction::SetValue specific constructor.  Current setting value
   1103   * must be specified and the creator should check whether we succeeded to
   1104   * allocate memory for line breaker conversion.
   1105   */
   1106  MOZ_CAN_RUN_SCRIPT AutoTextControlHandlingState(
   1107      TextControlState& aTextControlState, TextControlAction aTextControlAction,
   1108      const nsAString& aSettingValue, const nsAString* aOldValue,
   1109      const ValueSetterOptions& aOptions, ErrorResult& aRv)
   1110      : mParent(aTextControlState.mHandlingState),
   1111        mTextControlState(aTextControlState),
   1112        mTextCtrlElement(aTextControlState.mTextCtrlElement),
   1113        mTextInputListener(aTextControlState.mTextListener),
   1114        mSettingValue(aSettingValue),
   1115        mOldValue(aOldValue),
   1116        mValueSetterOptions(aOptions),
   1117        mTextControlAction(aTextControlAction) {
   1118    MOZ_ASSERT(aTextControlAction == TextControlAction::SetValue,
   1119               "Use generic constructor");
   1120    MOZ_DIAGNOSTIC_ASSERT_IF(
   1121        !aTextControlState.mTextListener,
   1122        !aTextControlState.mBoundFrame || !aTextControlState.mTextEditor);
   1123    mTextControlState.mHandlingState = this;
   1124    if (!nsContentUtils::PlatformToDOMLineBreaks(mSettingValue, fallible)) {
   1125      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1126      return;
   1127    }
   1128    // Update all setting value's new value because older value shouldn't
   1129    // overwrite newer value.
   1130    if (mParent) {
   1131      // If SetValue is nested, parents cannot trust their old value anymore.
   1132      // So, we need to clear them.
   1133      mParent->UpdateSettingValueAndInvalidateOldValue(mSettingValue);
   1134    }
   1135  }
   1136 
   1137  MOZ_CAN_RUN_SCRIPT ~AutoTextControlHandlingState() {
   1138    mTextControlState.mHandlingState = mParent;
   1139    if (!mParent && mTextControlStateDestroyed) {
   1140      mTextControlState.DeleteOrCacheForReuse();
   1141    }
   1142    if (!mTextControlStateDestroyed && mPrepareEditorLater) {
   1143      MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
   1144      MOZ_ASSERT(Is(TextControlAction::SetValue));
   1145      mTextControlState.PrepareEditor(&mSettingValue);
   1146    }
   1147  }
   1148 
   1149  void OnDestroyTextControlState() {
   1150    if (IsHandling(TextControlAction::Destructor)) {
   1151      // Do nothing since mTextContrlState.DeleteOrCacheForReuse() has
   1152      // already been called.
   1153      return;
   1154    }
   1155    mTextControlStateDestroyed = true;
   1156    if (mParent) {
   1157      mParent->OnDestroyTextControlState();
   1158    }
   1159  }
   1160 
   1161  void PrepareEditorLater() {
   1162    MOZ_ASSERT(IsHandling(TextControlAction::SetValue));
   1163    MOZ_ASSERT(!IsHandling(TextControlAction::PrepareEditor));
   1164    // Look for the top most SetValue.
   1165    AutoTextControlHandlingState* settingValue = nullptr;
   1166    for (AutoTextControlHandlingState* handlingSomething = this;
   1167         handlingSomething; handlingSomething = handlingSomething->mParent) {
   1168      if (handlingSomething->Is(TextControlAction::SetValue)) {
   1169        settingValue = handlingSomething;
   1170      }
   1171    }
   1172    settingValue->mPrepareEditorLater = true;
   1173  }
   1174 
   1175  /**
   1176   * WillSetValueWithTextEditor() is called when TextControlState sets
   1177   * value with its mTextEditor.
   1178   */
   1179  void WillSetValueWithTextEditor() {
   1180    MOZ_ASSERT(Is(TextControlAction::SetValue));
   1181    MOZ_ASSERT(mTextControlState.mBoundFrame);
   1182    mTextControlFrame = mTextControlState.mBoundFrame;
   1183    // If we'reemulating user input, we don't need to manage mTextInputListener
   1184    // by ourselves since everything should be handled by TextEditor as normal
   1185    // user input.
   1186    if (mValueSetterOptions.contains(ValueSetterOption::BySetUserInputAPI)) {
   1187      return;
   1188    }
   1189    // Otherwise, if we're setting the value programatically, we need to manage
   1190    // mTextInputListener by ourselves since TextEditor users special path
   1191    // for the performance.
   1192    mTextInputListener->SettingValue(true);
   1193    mTextInputListener->SetValueChanged(
   1194        mValueSetterOptions.contains(ValueSetterOption::SetValueChanged));
   1195    mEditActionHandled = false;
   1196    // Even if falling back to `TextControlState::SetValueWithoutTextEditor()`
   1197    // due to editor destruction, it shouldn't dispatch "beforeinput" event
   1198    // anymore.  Therefore, we should mark that we've already dispatched
   1199    // "beforeinput" event.
   1200    WillDispatchBeforeInputEvent();
   1201  }
   1202 
   1203  /**
   1204   * WillDispatchBeforeInputEvent() is called immediately before dispatching
   1205   * "beforeinput" event in `TextControlState`.
   1206   */
   1207  void WillDispatchBeforeInputEvent() {
   1208    mBeforeInputEventHasBeenDispatched = true;
   1209  }
   1210 
   1211  /**
   1212   * OnEditActionHandled() is called when the TextEditor handles something
   1213   * and immediately before dispatching "input" event.
   1214   */
   1215  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnEditActionHandled() {
   1216    MOZ_ASSERT(!mEditActionHandled);
   1217    mEditActionHandled = true;
   1218    if (!Is(TextControlAction::SetValue)) {
   1219      return NS_OK;
   1220    }
   1221    if (!mValueSetterOptions.contains(ValueSetterOption::BySetUserInputAPI)) {
   1222      mTextInputListener->SetValueChanged(true);
   1223      mTextInputListener->SettingValue(
   1224          mParent && mParent->IsHandling(TextControlAction::SetValue));
   1225    }
   1226    if (!IsOriginalTextControlFrameAlive()) {
   1227      return SetValueWithoutTextEditorAgain() ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   1228    }
   1229    // The new value never includes line breaks caused by hard-wrap.
   1230    // So, mCachedValue can always cache the new value.
   1231    nsTextControlFrame* textControlFrame =
   1232        do_QueryFrame(mTextControlFrame.GetFrame());
   1233    return textControlFrame->CacheValue(mSettingValue, fallible)
   1234               ? NS_OK
   1235               : NS_ERROR_OUT_OF_MEMORY;
   1236  }
   1237 
   1238  /**
   1239   * SetValueWithoutTextEditorAgain() should be called if the frame for
   1240   * mTextControlState was destroyed during setting value.
   1241   */
   1242  [[nodiscard]] MOZ_CAN_RUN_SCRIPT bool SetValueWithoutTextEditorAgain() {
   1243    MOZ_ASSERT(!IsOriginalTextControlFrameAlive());
   1244    // If the frame was destroyed because of a flush somewhere inside
   1245    // TextEditor, mBoundFrame here will be nullptr.  But it's also
   1246    // possible for the frame to go away because of another reason (such
   1247    // as deleting the existing selection -- see bug 574558), in which
   1248    // case we don't need to reset the value here.
   1249    if (mTextControlState.mBoundFrame) {
   1250      return true;
   1251    }
   1252    // XXX It's odd to drop flags except
   1253    //     ValueSetterOption::SetValueChanged.
   1254    //     Probably, this intended to drop ValueSetterOption::BySetUserInputAPI
   1255    //     and ValueSetterOption::ByContentAPI, but other flags are added later.
   1256    ErrorResult error;
   1257    AutoTextControlHandlingState handlingSetValueWithoutEditor(
   1258        mTextControlState, TextControlAction::SetValue, mSettingValue,
   1259        mOldValue, mValueSetterOptions & ValueSetterOption::SetValueChanged,
   1260        error);
   1261    if (error.Failed()) {
   1262      MOZ_ASSERT(error.ErrorCodeIs(NS_ERROR_OUT_OF_MEMORY));
   1263      error.SuppressException();
   1264      return false;
   1265    }
   1266    return mTextControlState.SetValueWithoutTextEditor(
   1267        handlingSetValueWithoutEditor);
   1268  }
   1269 
   1270  bool IsTextControlStateDestroyed() const {
   1271    return mTextControlStateDestroyed;
   1272  }
   1273  bool IsOriginalTextControlFrameAlive() const {
   1274    return const_cast<AutoTextControlHandlingState*>(this)
   1275        ->mTextControlFrame.IsAlive();
   1276  }
   1277  bool HasEditActionHandled() const { return mEditActionHandled; }
   1278  bool HasBeforeInputEventDispatched() const {
   1279    return mBeforeInputEventHasBeenDispatched;
   1280  }
   1281  bool Is(TextControlAction aTextControlAction) const {
   1282    return mTextControlAction == aTextControlAction;
   1283  }
   1284  bool IsHandling(TextControlAction aTextControlAction) const {
   1285    if (mTextControlAction == aTextControlAction) {
   1286      return true;
   1287    }
   1288    return mParent && mParent->IsHandling(aTextControlAction);
   1289  }
   1290  TextControlElement* GetTextControlElement() const { return mTextCtrlElement; }
   1291  TextInputListener* GetTextInputListener() const { return mTextInputListener; }
   1292  const ValueSetterOptions& ValueSetterOptionsRef() const {
   1293    MOZ_ASSERT(Is(TextControlAction::SetValue));
   1294    return mValueSetterOptions;
   1295  }
   1296  const nsAString* GetOldValue() const {
   1297    MOZ_ASSERT(Is(TextControlAction::SetValue));
   1298    return mOldValue;
   1299  }
   1300  const nsString& GetSettingValue() const {
   1301    MOZ_ASSERT(IsHandling(TextControlAction::SetValue));
   1302    if (mTextControlAction == TextControlAction::SetValue) {
   1303      return mSettingValue;
   1304    }
   1305    return mParent->GetSettingValue();
   1306  }
   1307 
   1308 private:
   1309  void UpdateSettingValueAndInvalidateOldValue(const nsString& aSettingValue) {
   1310    if (mTextControlAction == TextControlAction::SetValue) {
   1311      mSettingValue = aSettingValue;
   1312    }
   1313    mOldValue = nullptr;
   1314    if (mParent) {
   1315      mParent->UpdateSettingValueAndInvalidateOldValue(aSettingValue);
   1316    }
   1317  }
   1318  void InvalidateOldValue() {
   1319    mOldValue = nullptr;
   1320    if (mParent) {
   1321      mParent->InvalidateOldValue();
   1322    }
   1323  }
   1324 
   1325  AutoTextControlHandlingState* const mParent;
   1326  TextControlState& mTextControlState;
   1327  // mTextControlFrame should be set immediately before calling methods
   1328  // which may destroy the frame.  Then, you can check whether the frame
   1329  // was destroyed/replaced.
   1330  AutoWeakFrame mTextControlFrame;
   1331  // mTextCtrlElement grabs TextControlState::mTextCtrlElement since
   1332  // if the text control element releases mTextControlState, only this
   1333  // can guarantee the instance of the text control element.
   1334  RefPtr<TextControlElement> const mTextCtrlElement;
   1335  // mTextInputListener grabs TextControlState::mTextListener because if
   1336  // TextControlState is unbind from the frame, it's released.
   1337  RefPtr<TextInputListener> const mTextInputListener;
   1338  nsAutoString mSettingValue;
   1339  const nsAString* mOldValue = nullptr;
   1340  ValueSetterOptions mValueSetterOptions;
   1341  TextControlAction const mTextControlAction;
   1342  bool mTextControlStateDestroyed = false;
   1343  bool mEditActionHandled = false;
   1344  bool mPrepareEditorLater = false;
   1345  bool mBeforeInputEventHasBeenDispatched = false;
   1346 };
   1347 
   1348 /*****************************************************************************
   1349 * mozilla::TextControlState
   1350 *****************************************************************************/
   1351 
   1352 /**
   1353 * For avoiding allocation cost of the instance, we should reuse instances
   1354 * as far as possible.
   1355 *
   1356 * FYI: `25` is just a magic number considered without enough investigation,
   1357 *      but at least, this value must not make damage for footprint.
   1358 *      Feel free to change it if you find better number.
   1359 */
   1360 static constexpr size_t kMaxCountOfCacheToReuse = 25;
   1361 static AutoTArray<void*, kMaxCountOfCacheToReuse>* sReleasedInstances = nullptr;
   1362 static bool sHasShutDown = false;
   1363 
   1364 TextControlState::TextControlState(TextControlElement* aOwningElement)
   1365    : mTextCtrlElement(aOwningElement),
   1366      mEverInited(false),
   1367      mEditorInitialized(false),
   1368      mValueTransferInProgress(false),
   1369      mSelectionCached(true)
   1370 // When adding more member variable initializations here, add the same
   1371 // also to ::Construct.
   1372 {
   1373  MOZ_COUNT_CTOR(TextControlState);
   1374  static_assert(sizeof(*this) <= 128,
   1375                "Please keep small TextControlState as far as possible");
   1376 }
   1377 
   1378 TextControlState* TextControlState::Construct(
   1379    TextControlElement* aOwningElement) {
   1380  void* mem;
   1381  if (sReleasedInstances && !sReleasedInstances->IsEmpty()) {
   1382    mem = sReleasedInstances->PopLastElement();
   1383  } else {
   1384    mem = moz_xmalloc(sizeof(TextControlState));
   1385  }
   1386 
   1387  return new (mem) TextControlState(aOwningElement);
   1388 }
   1389 
   1390 TextControlState::~TextControlState() {
   1391  MOZ_ASSERT(!mHandlingState);
   1392  MOZ_COUNT_DTOR(TextControlState);
   1393  AutoTextControlHandlingState handlingDesctructor(
   1394      *this, TextControlAction::Destructor);
   1395  Clear();
   1396 }
   1397 
   1398 void TextControlState::Shutdown() {
   1399  sHasShutDown = true;
   1400  if (sReleasedInstances) {
   1401    for (void* mem : *sReleasedInstances) {
   1402      free(mem);
   1403    }
   1404    delete sReleasedInstances;
   1405  }
   1406 }
   1407 
   1408 void TextControlState::Destroy() {
   1409  // If we're handling something, we should be deleted later.
   1410  if (mHandlingState) {
   1411    mHandlingState->OnDestroyTextControlState();
   1412    return;
   1413  }
   1414  DeleteOrCacheForReuse();
   1415  // Note that this instance may have already been deleted here.  Don't touch
   1416  // any members.
   1417 }
   1418 
   1419 void TextControlState::DeleteOrCacheForReuse() {
   1420  MOZ_ASSERT(!IsBusy());
   1421 
   1422  void* mem = this;
   1423  this->~TextControlState();
   1424 
   1425  // If we can cache this instance, we should do it instead of deleting it.
   1426  if (!sHasShutDown && (!sReleasedInstances || sReleasedInstances->Length() <
   1427                                                   kMaxCountOfCacheToReuse)) {
   1428    // Put this instance to the cache.  Note that now, the array may be full,
   1429    // but it's not problem to cache more instances than kMaxCountOfCacheToReuse
   1430    // because it just requires reallocation cost of the array buffer.
   1431    if (!sReleasedInstances) {
   1432      sReleasedInstances = new AutoTArray<void*, kMaxCountOfCacheToReuse>;
   1433    }
   1434    sReleasedInstances->AppendElement(mem);
   1435  } else {
   1436    free(mem);
   1437  }
   1438 }
   1439 
   1440 nsresult TextControlState::OnEditActionHandled() {
   1441  return mHandlingState ? mHandlingState->OnEditActionHandled() : NS_OK;
   1442 }
   1443 
   1444 Element* TextControlState::GetRootNode() {
   1445  return mBoundFrame ? mBoundFrame->GetRootNode() : nullptr;
   1446 }
   1447 
   1448 Element* TextControlState::GetPreviewNode() {
   1449  return mBoundFrame ? mBoundFrame->GetPreviewNode() : nullptr;
   1450 }
   1451 
   1452 void TextControlState::Clear() {
   1453  MOZ_ASSERT(mHandlingState);
   1454  MOZ_ASSERT(mHandlingState->Is(TextControlAction::Destructor) ||
   1455             mHandlingState->Is(TextControlAction::Unlink));
   1456  if (mTextEditor) {
   1457    mTextEditor->SetTextInputListener(nullptr);
   1458  }
   1459 
   1460  if (mBoundFrame) {
   1461    // Oops, we still have a frame!
   1462    // This should happen when the type of a text input control is being changed
   1463    // to something which is not a text control.  In this case, we should
   1464    // pretend that a frame is being destroyed, and clean up after ourselves
   1465    // properly.
   1466    UnbindFromFrame(mBoundFrame);
   1467    mTextEditor = nullptr;
   1468  } else {
   1469    // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
   1470    // for us.
   1471    DestroyEditor();
   1472    MOZ_DIAGNOSTIC_ASSERT(!mBoundFrame || !mTextEditor);
   1473  }
   1474  mTextListener = nullptr;
   1475 }
   1476 
   1477 void TextControlState::Unlink() {
   1478  AutoTextControlHandlingState handlingUnlink(*this, TextControlAction::Unlink);
   1479  UnlinkInternal();
   1480 }
   1481 
   1482 void TextControlState::UnlinkInternal() {
   1483  MOZ_ASSERT(mHandlingState);
   1484  MOZ_ASSERT(mHandlingState->Is(TextControlAction::Unlink));
   1485  TextControlState* tmp = this;
   1486  tmp->Clear();
   1487  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelCon)
   1488  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextEditor)
   1489 }
   1490 
   1491 void TextControlState::Traverse(nsCycleCollectionTraversalCallback& cb) {
   1492  TextControlState* tmp = this;
   1493  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelCon)
   1494  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextEditor)
   1495 }
   1496 
   1497 nsFrameSelection* TextControlState::GetIndependentFrameSelection() const {
   1498  return mSelCon ? mSelCon->GetIndependentFrameSelection() : nullptr;
   1499 }
   1500 
   1501 TextEditor* TextControlState::GetTextEditor() {
   1502  // Note that if the instance is destroyed in PrepareEditor(), it returns
   1503  // NS_ERROR_NOT_INITIALIZED so that we don't need to create kungFuDeathGrip
   1504  // in this hot path.
   1505  if (!mTextEditor && NS_WARN_IF(NS_FAILED(PrepareEditor()))) {
   1506    return nullptr;
   1507  }
   1508  return mTextEditor;
   1509 }
   1510 
   1511 TextEditor* TextControlState::GetExtantTextEditor() const {
   1512  return mTextEditor;
   1513 }
   1514 
   1515 nsISelectionController* TextControlState::GetSelectionController() const {
   1516  return mSelCon;
   1517 }
   1518 
   1519 // Helper class, used below in BindToFrame().
   1520 class PrepareEditorEvent : public Runnable {
   1521 public:
   1522  PrepareEditorEvent(TextControlState& aState, nsIContent* aOwnerContent,
   1523                     const nsAString& aCurrentValue)
   1524      : Runnable("PrepareEditorEvent"),
   1525        mState(&aState),
   1526        mOwnerContent(aOwnerContent),
   1527        mCurrentValue(aCurrentValue) {
   1528    aState.mValueTransferInProgress = true;
   1529  }
   1530 
   1531  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
   1532    if (NS_WARN_IF(!mState)) {
   1533      return NS_ERROR_NULL_POINTER;
   1534    }
   1535 
   1536    // Transfer the saved value to the editor if we have one
   1537    const nsAString* value = nullptr;
   1538    if (!mCurrentValue.IsEmpty()) {
   1539      value = &mCurrentValue;
   1540    }
   1541 
   1542    nsAutoScriptBlocker scriptBlocker;
   1543 
   1544    mState->PrepareEditor(value);
   1545 
   1546    mState->mValueTransferInProgress = false;
   1547 
   1548    return NS_OK;
   1549  }
   1550 
   1551 private:
   1552  WeakPtr<TextControlState> mState;
   1553  nsCOMPtr<nsIContent> mOwnerContent;  // strong reference
   1554  nsAutoString mCurrentValue;
   1555 };
   1556 
   1557 nsresult TextControlState::BindToFrame(nsTextControlFrame* aFrame) {
   1558  MOZ_ASSERT(
   1559      !nsContentUtils::IsSafeToRunScript(),
   1560      "TextControlState::BindToFrame() has to be called with script blocker");
   1561  NS_ASSERTION(aFrame, "The frame to bind to should be valid");
   1562  if (!aFrame) {
   1563    return NS_ERROR_INVALID_ARG;
   1564  }
   1565 
   1566  NS_ASSERTION(!mBoundFrame, "Cannot bind twice, need to unbind first");
   1567  if (mBoundFrame) {
   1568    return NS_ERROR_FAILURE;
   1569  }
   1570 
   1571  // If we'll need to transfer our current value to the editor, save it before
   1572  // binding to the frame.
   1573  nsAutoString currentValue;
   1574  if (mTextEditor) {
   1575    GetValue(currentValue, /* aForDisplay = */ false);
   1576  }
   1577 
   1578  mBoundFrame = aFrame;
   1579 
   1580  MOZ_ASSERT(aFrame->GetRootNode());
   1581  Element& editorRootAnonymousDiv = *aFrame->GetRootNode();
   1582 
   1583  PresShell* presShell = aFrame->PresContext()->GetPresShell();
   1584  MOZ_ASSERT(presShell);
   1585 
   1586  // Create a SelectionController
   1587  mSelCon = new TextInputSelectionController(presShell, editorRootAnonymousDiv);
   1588  MOZ_ASSERT(!mTextListener, "Should not overwrite the object");
   1589  mTextListener = new TextInputListener(mTextCtrlElement);
   1590 
   1591  mTextListener->SetFrame(mBoundFrame);
   1592 
   1593  // Editor will override this as needed from InitializeSelection.
   1594  mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   1595 
   1596  // Get the caret and make it a selection listener.
   1597  // FYI: It's safe to use raw pointer for calling
   1598  //      Selection::AddSelectionListner() because it only appends the listener
   1599  //      to its internal array.
   1600  Selection* selection = mSelCon->GetSelection(SelectionType::eNormal);
   1601  if (selection) {
   1602    RefPtr<nsCaret> caret = presShell->GetCaret();
   1603    if (caret) {
   1604      selection->AddSelectionListener(caret);
   1605    }
   1606    mTextListener->StartToListenToSelectionChange();
   1607  }
   1608 
   1609  // If an editor exists from before, prepare it for usage
   1610  if (mTextEditor) {
   1611    if (NS_WARN_IF(!mTextCtrlElement)) {
   1612      return NS_ERROR_FAILURE;
   1613    }
   1614 
   1615    // Set the correct direction on the newly created root node
   1616    if (mTextEditor->IsRightToLeft()) {
   1617      editorRootAnonymousDiv.SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
   1618                                     u"rtl"_ns, false);
   1619    } else if (mTextEditor->IsLeftToRight()) {
   1620      editorRootAnonymousDiv.SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
   1621                                     u"ltr"_ns, false);
   1622    } else {
   1623      // otherwise, inherit the content node's direction
   1624    }
   1625 
   1626    nsContentUtils::AddScriptRunner(
   1627        new PrepareEditorEvent(*this, mTextCtrlElement, currentValue));
   1628  }
   1629 
   1630  return NS_OK;
   1631 }
   1632 
   1633 struct MOZ_STACK_CLASS PreDestroyer {
   1634  void Init(TextEditor* aTextEditor) { mTextEditor = aTextEditor; }
   1635  ~PreDestroyer() {
   1636    if (mTextEditor) {
   1637      // In this case, we don't need to restore the unmasked range of password
   1638      // editor.
   1639      UniquePtr<PasswordMaskData> passwordMaskData = mTextEditor->PreDestroy();
   1640    }
   1641  }
   1642  void Swap(RefPtr<TextEditor>& aTextEditor) {
   1643    return mTextEditor.swap(aTextEditor);
   1644  }
   1645 
   1646 private:
   1647  RefPtr<TextEditor> mTextEditor;
   1648 };
   1649 
   1650 nsresult TextControlState::PrepareEditor(const nsAString* aValue) {
   1651  if (!mBoundFrame) {
   1652    // Cannot create an editor without a bound frame.
   1653    // Don't return a failure code, because js callers can't handle that.
   1654    return NS_OK;
   1655  }
   1656 
   1657  if (mEditorInitialized) {
   1658    // Do not initialize the editor multiple times.
   1659    return NS_OK;
   1660  }
   1661 
   1662  AutoHideSelectionChanges hideSelectionChanges(GetIndependentFrameSelection());
   1663 
   1664  if (mHandlingState) {
   1665    // Don't attempt to initialize recursively!
   1666    if (mHandlingState->IsHandling(TextControlAction::PrepareEditor)) {
   1667      return NS_ERROR_NOT_INITIALIZED;
   1668    }
   1669    // Reschedule creating editor later if we're setting value.
   1670    if (mHandlingState->IsHandling(TextControlAction::SetValue)) {
   1671      mHandlingState->PrepareEditorLater();
   1672      return NS_ERROR_NOT_INITIALIZED;
   1673    }
   1674  }
   1675 
   1676  MOZ_ASSERT(mTextCtrlElement);
   1677 
   1678  AutoTextControlHandlingState preparingEditor(
   1679      *this, TextControlAction::PrepareEditor);
   1680 
   1681  // Note that we don't check mTextEditor here, because we might already have
   1682  // one around, in which case we don't create a new one, and we'll just tie
   1683  // the required machinery to it.
   1684 
   1685  nsPresContext* presContext = mBoundFrame->PresContext();
   1686  PresShell* presShell = presContext->GetPresShell();
   1687 
   1688  // Setup the editor flags
   1689 
   1690  // Spell check is diabled at creation time. It is enabled once
   1691  // the editor comes into focus.
   1692  uint32_t editorFlags = nsIEditor::eEditorSkipSpellCheck;
   1693 
   1694  if (IsSingleLineTextControl()) {
   1695    editorFlags |= nsIEditor::eEditorSingleLineMask;
   1696  }
   1697  if (IsPasswordTextControl()) {
   1698    editorFlags |= nsIEditor::eEditorPasswordMask;
   1699  }
   1700 
   1701  bool shouldInitializeEditor = false;
   1702  RefPtr<TextEditor> newTextEditor;  // the editor that we might create
   1703  PreDestroyer preDestroyer;
   1704  if (!mTextEditor) {
   1705    shouldInitializeEditor = true;
   1706 
   1707    // Create an editor
   1708    newTextEditor = new TextEditor();
   1709    preDestroyer.Init(newTextEditor);
   1710  } else {
   1711    newTextEditor = mTextEditor;  // just pretend that we have a new editor!
   1712 
   1713    // Don't lose application flags in the process.
   1714    if (newTextEditor->IsMailEditor()) {
   1715      editorFlags |= nsIEditor::eEditorMailMask;
   1716    }
   1717  }
   1718 
   1719  // Get the current value of the textfield from the content.
   1720  // Note that if we've created a new editor, mTextEditor is null at this stage,
   1721  // so we will get the real value from the content.
   1722  nsAutoString defaultValue;
   1723  if (aValue) {
   1724    defaultValue = *aValue;
   1725  } else {
   1726    GetValue(defaultValue, /* aForDisplay = */ true);
   1727  }
   1728 
   1729  if (!mEditorInitialized) {
   1730    // Now initialize the editor.
   1731    //
   1732    // NOTE: Conversion of '\n' to <BR> happens inside the
   1733    //       editor's Init() call.
   1734 
   1735    // Get the DOM document
   1736    nsCOMPtr<Document> doc = presShell->GetDocument();
   1737    if (NS_WARN_IF(!doc)) {
   1738      return NS_ERROR_FAILURE;
   1739    }
   1740 
   1741    // What follows is a bit of a hack.  The editor uses the public DOM APIs
   1742    // for its content manipulations, and it causes it to fail some security
   1743    // checks deep inside when initializing. So we explicitly make it clear that
   1744    // we're native code.
   1745    // Note that any script that's directly trying to access our value
   1746    // has to be going through some scriptable object to do that and that
   1747    // already does the relevant security checks.
   1748    AutoNoJSAPI nojsapi;
   1749 
   1750    RefPtr<Element> anonymousDivElement = GetRootNode();
   1751    if (NS_WARN_IF(!anonymousDivElement) || NS_WARN_IF(!mSelCon)) {
   1752      return NS_ERROR_FAILURE;
   1753    }
   1754    OwningNonNull<TextInputSelectionController> selectionController(*mSelCon);
   1755    UniquePtr<PasswordMaskData> passwordMaskData;
   1756    if (editorFlags & nsIEditor::eEditorPasswordMask) {
   1757      if (mPasswordMaskData) {
   1758        passwordMaskData = std::move(mPasswordMaskData);
   1759      } else {
   1760        passwordMaskData = MakeUnique<PasswordMaskData>();
   1761      }
   1762    } else {
   1763      mPasswordMaskData = nullptr;
   1764    }
   1765    nsresult rv =
   1766        newTextEditor->Init(*doc, *anonymousDivElement, selectionController,
   1767                            editorFlags, std::move(passwordMaskData));
   1768    if (NS_FAILED(rv)) {
   1769      NS_WARNING("TextEditor::Init() failed");
   1770      return rv;
   1771    }
   1772  }
   1773 
   1774  // Initialize the controller for the editor
   1775 
   1776  nsresult rv = NS_OK;
   1777  if (!SuppressEventHandlers(presContext)) {
   1778    nsCOMPtr<nsIControllers> controllers;
   1779    if (auto* inputElement = HTMLInputElement::FromNode(mTextCtrlElement)) {
   1780      nsresult rv = inputElement->GetControllers(getter_AddRefs(controllers));
   1781      if (NS_WARN_IF(NS_FAILED(rv))) {
   1782        return rv;
   1783      }
   1784    } else {
   1785      auto* textAreaElement = HTMLTextAreaElement::FromNode(mTextCtrlElement);
   1786      if (!textAreaElement) {
   1787        return NS_ERROR_FAILURE;
   1788      }
   1789 
   1790      nsresult rv =
   1791          textAreaElement->GetControllers(getter_AddRefs(controllers));
   1792      if (NS_WARN_IF(NS_FAILED(rv))) {
   1793        return rv;
   1794      }
   1795    }
   1796 
   1797    if (controllers) {
   1798      // XXX Oddly, nsresult value is overwritten in the following loop, and
   1799      //     only the last result or `found` decides the value.
   1800      uint32_t numControllers;
   1801      bool found = false;
   1802      rv = controllers->GetControllerCount(&numControllers);
   1803      for (uint32_t i = 0; i < numControllers; i++) {
   1804        nsCOMPtr<nsIController> controller;
   1805        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
   1806        if (NS_SUCCEEDED(rv) && controller) {
   1807          nsCOMPtr<nsBaseCommandController> baseController =
   1808              do_QueryInterface(controller);
   1809          if (baseController) {
   1810            baseController->SetContext(newTextEditor);
   1811            found = true;
   1812          }
   1813        }
   1814      }
   1815      if (!found) {
   1816        rv = NS_ERROR_FAILURE;
   1817      }
   1818    }
   1819  }
   1820 
   1821  // Initialize the plaintext editor
   1822  if (shouldInitializeEditor) {
   1823    const int32_t wrapCols = GetWrapCols();
   1824    MOZ_ASSERT(wrapCols >= 0);
   1825    newTextEditor->SetWrapColumn(wrapCols);
   1826  }
   1827 
   1828  // Set max text field length
   1829  newTextEditor->SetMaxTextLength(mTextCtrlElement->UsedMaxLength());
   1830 
   1831  editorFlags = newTextEditor->Flags();
   1832 
   1833  // Check if the readonly/disabled attributes are set.
   1834  if (mTextCtrlElement->IsDisabledOrReadOnly()) {
   1835    editorFlags |= nsIEditor::eEditorReadonlyMask;
   1836  }
   1837 
   1838  SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
   1839 
   1840  if (shouldInitializeEditor) {
   1841    // Hold on to the newly created editor
   1842    preDestroyer.Swap(mTextEditor);
   1843  }
   1844 
   1845  // If we have a default value, insert it under the div we created
   1846  // above, but be sure to use the editor so that '*' characters get
   1847  // displayed for password fields, etc. SetValue() will call the
   1848  // editor for us.
   1849 
   1850  if (!defaultValue.IsEmpty()) {
   1851    // XXX rv may store error code which indicates there is no controller.
   1852    //     However, we overwrite it only in this case.
   1853    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
   1854    if (NS_WARN_IF(NS_FAILED(rv))) {
   1855      return rv;
   1856    }
   1857 
   1858    // Now call SetValue() which will make the necessary editor calls to set
   1859    // the default value.  Make sure to turn off undo before setting the default
   1860    // value, and turn it back on afterwards. This will make sure we can't undo
   1861    // past the default value.
   1862    // So, we use ValueSetterOption::ByInternalAPI only that it will turn off
   1863    // undo.
   1864 
   1865    if (NS_WARN_IF(!SetValue(defaultValue, ValueSetterOption::ByInternalAPI))) {
   1866      return NS_ERROR_OUT_OF_MEMORY;
   1867    }
   1868 
   1869    // Now restore the original editor flags.
   1870    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
   1871    if (NS_WARN_IF(NS_FAILED(rv))) {
   1872      return rv;
   1873    }
   1874  }
   1875  // When the default value is empty, we don't call SetValue().  That means that
   1876  // we have not notified IMEContentObserver of the empty value when the
   1877  // <textarea> is not dirty (i.e., the default value is mirrored into the
   1878  // anonymous subtree asynchronously) and the value was changed during a
   1879  // reframe (i.e., while IMEContentObserver was not observing the mutation of
   1880  // the anonymous subtree).  Therefore, we notify IMEContentObserver here in
   1881  // that case.
   1882  else if (mTextCtrlElement && mTextCtrlElement->IsTextArea() &&
   1883           !mTextCtrlElement->ValueChanged()) {
   1884    MOZ_ASSERT(defaultValue.IsEmpty());
   1885    IMEContentObserver* observer = GetIMEContentObserver();
   1886    if (observer && observer->WasInitializedWith(*newTextEditor)) {
   1887      observer->OnTextControlValueChangedWhileNotObservable(defaultValue);
   1888    }
   1889  }
   1890 
   1891  DebugOnly<bool> enabledUndoRedo =
   1892      newTextEditor->EnableUndoRedo(TextControlElement::DEFAULT_UNDO_CAP);
   1893  NS_WARNING_ASSERTION(enabledUndoRedo,
   1894                       "Failed to enable undo/redo transaction");
   1895 
   1896  if (!mEditorInitialized) {
   1897    newTextEditor->PostCreate();
   1898    mEverInited = true;
   1899    mEditorInitialized = true;
   1900  }
   1901 
   1902  if (mTextListener) {
   1903    newTextEditor->SetTextInputListener(mTextListener);
   1904  }
   1905 
   1906  // Restore our selection after being bound to a new frame
   1907  if (mSelectionCached) {
   1908    if (mRestoringSelection) {  // paranoia
   1909      mRestoringSelection->Revoke();
   1910    }
   1911    mRestoringSelection = new RestoreSelectionState(this, mBoundFrame);
   1912    if (mRestoringSelection) {
   1913      nsContentUtils::AddScriptRunner(mRestoringSelection);
   1914    }
   1915  }
   1916 
   1917  // The selection cache is no longer going to be valid.
   1918  //
   1919  // XXXbz Shouldn't we do this at the point when we're actually about to
   1920  // restore the properties or something?  As things stand, if UnbindFromFrame
   1921  // happens before our RestoreSelectionState runs, it looks like we'll lose our
   1922  // selection info, because we will think we don't have it cached and try to
   1923  // read it from the selection controller, which will not have it yet.
   1924  mSelectionCached = false;
   1925 
   1926  return preparingEditor.IsTextControlStateDestroyed()
   1927             ? NS_ERROR_NOT_INITIALIZED
   1928             : rv;
   1929 }
   1930 
   1931 void TextControlState::FinishedRestoringSelection() {
   1932  mRestoringSelection = nullptr;
   1933 }
   1934 
   1935 void TextControlState::SyncUpSelectionPropertiesBeforeDestruction() {
   1936  if (mBoundFrame) {
   1937    UnbindFromFrame(mBoundFrame);
   1938  }
   1939 }
   1940 
   1941 void TextControlState::SetSelectionProperties(
   1942    TextControlState::SelectionProperties& aProps) {
   1943  if (mBoundFrame) {
   1944    mBoundFrame->SetSelectionRange(aProps.GetStart(), aProps.GetEnd(),
   1945                                   aProps.GetDirection());
   1946    // The instance may have already been deleted here.
   1947  } else {
   1948    mSelectionProperties = aProps;
   1949  }
   1950 }
   1951 
   1952 void TextControlState::GetSelectionRange(uint32_t* aSelectionStart,
   1953                                         uint32_t* aSelectionEnd,
   1954                                         ErrorResult& aRv) {
   1955  MOZ_ASSERT(aSelectionStart);
   1956  MOZ_ASSERT(aSelectionEnd);
   1957  MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
   1958             "How can we not have a cached selection if we have no selection "
   1959             "controller?");
   1960 
   1961  // Note that we may have both IsSelectionCached() _and_
   1962  // GetSelectionController() if we haven't initialized our editor yet.
   1963  if (IsSelectionCached()) {
   1964    const SelectionProperties& props = GetSelectionProperties();
   1965    *aSelectionStart = props.GetStart();
   1966    *aSelectionEnd = props.GetEnd();
   1967    return;
   1968  }
   1969 
   1970  Selection* sel = mSelCon->GetSelection(SelectionType::eNormal);
   1971  if (NS_WARN_IF(!sel)) {
   1972    aRv.Throw(NS_ERROR_FAILURE);
   1973    return;
   1974  }
   1975 
   1976  Element* root = GetRootNode();
   1977  if (NS_WARN_IF(!root)) {
   1978    aRv.Throw(NS_ERROR_UNEXPECTED);
   1979    return;
   1980  }
   1981  nsContentUtils::GetSelectionInTextControl(sel, root, *aSelectionStart,
   1982                                            *aSelectionEnd);
   1983 }
   1984 
   1985 SelectionDirection TextControlState::GetSelectionDirection(ErrorResult& aRv) {
   1986  MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
   1987             "How can we not have a cached selection if we have no selection "
   1988             "controller?");
   1989 
   1990  // Note that we may have both IsSelectionCached() _and_
   1991  // GetSelectionController() if we haven't initialized our editor yet.
   1992  if (IsSelectionCached()) {
   1993    return GetSelectionProperties().GetDirection();
   1994  }
   1995 
   1996  Selection* sel = mSelCon->GetSelection(SelectionType::eNormal);
   1997  if (NS_WARN_IF(!sel)) {
   1998    aRv.Throw(NS_ERROR_FAILURE);
   1999    return SelectionDirection::Forward;
   2000  }
   2001 
   2002  nsDirection direction = sel->GetDirection();
   2003  if (direction == eDirNext) {
   2004    return SelectionDirection::Forward;
   2005  }
   2006 
   2007  MOZ_ASSERT(direction == eDirPrevious);
   2008  return SelectionDirection::Backward;
   2009 }
   2010 
   2011 void TextControlState::SetSelectionRange(uint32_t aStart, uint32_t aEnd,
   2012                                         SelectionDirection aDirection,
   2013                                         ErrorResult& aRv,
   2014                                         ScrollAfterSelection aScroll) {
   2015  MOZ_ASSERT(IsSelectionCached() || mBoundFrame,
   2016             "How can we have a non-cached selection but no frame?");
   2017 
   2018  AutoTextControlHandlingState handlingSetSelectionRange(
   2019      *this, TextControlAction::SetSelectionRange);
   2020 
   2021  if (aStart > aEnd) {
   2022    aStart = aEnd;
   2023  }
   2024 
   2025  if (!IsSelectionCached()) {
   2026    MOZ_ASSERT(mBoundFrame, "Our frame should still be valid");
   2027    aRv = mBoundFrame->SetSelectionRange(aStart, aEnd, aDirection);
   2028    if (aRv.Failed() ||
   2029        handlingSetSelectionRange.IsTextControlStateDestroyed()) {
   2030      return;
   2031    }
   2032    if (aScroll == ScrollAfterSelection::Yes && mBoundFrame) {
   2033      // mBoundFrame could be gone if selection listeners flushed layout for
   2034      // example.
   2035      mBoundFrame->ScrollSelectionIntoViewAsync();
   2036    }
   2037    return;
   2038  }
   2039 
   2040  SelectionProperties& props = GetSelectionProperties();
   2041  if (!props.HasMaxLength()) {
   2042    // A clone without a dirty value flag may not have a max length yet
   2043    nsAutoString value;
   2044    GetValue(value, /* aForDisplay = */ true);
   2045    props.SetMaxLength(value.Length());
   2046  }
   2047 
   2048  bool changed = props.SetStart(aStart);
   2049  changed |= props.SetEnd(aEnd);
   2050  changed |= props.SetDirection(aDirection);
   2051 
   2052  if (!changed) {
   2053    return;
   2054  }
   2055 
   2056  // It sure would be nice if we had an existing Element* or so to work with.
   2057  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   2058      new AsyncEventDispatcher(mTextCtrlElement, eFormSelect, CanBubble::eYes);
   2059  asyncDispatcher->PostDOMEvent();
   2060 
   2061  // SelectionChangeEventDispatcher covers this when !IsSelectionCached().
   2062  // XXX(krosylight): Shouldn't it fire before select event?
   2063  // Currently Gecko and Blink both fire selectionchange after select.
   2064  if (IsSelectionCached() &&
   2065      StaticPrefs::dom_select_events_textcontrols_selectionchange_enabled() &&
   2066      !mTextCtrlElement->HasScheduledSelectionChangeEvent()) {
   2067    mTextCtrlElement->SetHasScheduledSelectionChangeEvent();
   2068    asyncDispatcher = new AsyncSelectionChangeEventDispatcher(
   2069        mTextCtrlElement, eSelectionChange, CanBubble::eYes);
   2070    asyncDispatcher->PostDOMEvent();
   2071  }
   2072 }
   2073 
   2074 void TextControlState::SetSelectionStart(const Nullable<uint32_t>& aStart,
   2075                                         ErrorResult& aRv) {
   2076  uint32_t start = 0;
   2077  if (!aStart.IsNull()) {
   2078    start = aStart.Value();
   2079  }
   2080 
   2081  uint32_t ignored, end;
   2082  GetSelectionRange(&ignored, &end, aRv);
   2083  if (aRv.Failed()) {
   2084    return;
   2085  }
   2086 
   2087  SelectionDirection dir = GetSelectionDirection(aRv);
   2088  if (aRv.Failed()) {
   2089    return;
   2090  }
   2091 
   2092  if (end < start) {
   2093    end = start;
   2094  }
   2095 
   2096  SetSelectionRange(start, end, dir, aRv);
   2097  // The instance may have already been deleted here.
   2098 }
   2099 
   2100 void TextControlState::SetSelectionEnd(const Nullable<uint32_t>& aEnd,
   2101                                       ErrorResult& aRv) {
   2102  uint32_t end = 0;
   2103  if (!aEnd.IsNull()) {
   2104    end = aEnd.Value();
   2105  }
   2106 
   2107  uint32_t start, ignored;
   2108  GetSelectionRange(&start, &ignored, aRv);
   2109  if (aRv.Failed()) {
   2110    return;
   2111  }
   2112 
   2113  SelectionDirection dir = GetSelectionDirection(aRv);
   2114  if (aRv.Failed()) {
   2115    return;
   2116  }
   2117 
   2118  SetSelectionRange(start, end, dir, aRv);
   2119  // The instance may have already been deleted here.
   2120 }
   2121 
   2122 static void DirectionToName(SelectionDirection dir, nsAString& aDirection) {
   2123  switch (dir) {
   2124    case SelectionDirection::None:
   2125      // TODO(mbrodesser): this should be supported, see
   2126      // https://bugzilla.mozilla.org/show_bug.cgi?id=1541454.
   2127      NS_WARNING("We don't actually support this... how did we get it?");
   2128      return aDirection.AssignLiteral("none");
   2129    case SelectionDirection::Forward:
   2130      return aDirection.AssignLiteral("forward");
   2131    case SelectionDirection::Backward:
   2132      return aDirection.AssignLiteral("backward");
   2133  }
   2134  MOZ_ASSERT_UNREACHABLE("Invalid SelectionDirection value");
   2135 }
   2136 
   2137 void TextControlState::GetSelectionDirectionString(nsAString& aDirection,
   2138                                                   ErrorResult& aRv) {
   2139  SelectionDirection dir = GetSelectionDirection(aRv);
   2140  if (aRv.Failed()) {
   2141    return;
   2142  }
   2143  DirectionToName(dir, aDirection);
   2144 }
   2145 
   2146 static SelectionDirection DirectionStringToSelectionDirection(
   2147    const nsAString& aDirection) {
   2148  if (aDirection.EqualsLiteral("backward")) {
   2149    return SelectionDirection::Backward;
   2150  }
   2151  // We don't support directionless selections, see bug 1541454.
   2152  return SelectionDirection::Forward;
   2153 }
   2154 
   2155 void TextControlState::SetSelectionDirection(const nsAString& aDirection,
   2156                                             ErrorResult& aRv) {
   2157  SelectionDirection dir = DirectionStringToSelectionDirection(aDirection);
   2158 
   2159  uint32_t start, end;
   2160  GetSelectionRange(&start, &end, aRv);
   2161  if (aRv.Failed()) {
   2162    return;
   2163  }
   2164 
   2165  SetSelectionRange(start, end, dir, aRv);
   2166  // The instance may have already been deleted here.
   2167 }
   2168 
   2169 static SelectionDirection DirectionStringToSelectionDirection(
   2170    const Optional<nsAString>& aDirection) {
   2171  if (!aDirection.WasPassed()) {
   2172    // We don't support directionless selections.
   2173    return SelectionDirection::Forward;
   2174  }
   2175 
   2176  return DirectionStringToSelectionDirection(aDirection.Value());
   2177 }
   2178 
   2179 void TextControlState::SetSelectionRange(uint32_t aSelectionStart,
   2180                                         uint32_t aSelectionEnd,
   2181                                         const Optional<nsAString>& aDirection,
   2182                                         ErrorResult& aRv,
   2183                                         ScrollAfterSelection aScroll) {
   2184  SelectionDirection dir = DirectionStringToSelectionDirection(aDirection);
   2185 
   2186  SetSelectionRange(aSelectionStart, aSelectionEnd, dir, aRv, aScroll);
   2187  // The instance may have already been deleted here.
   2188 }
   2189 
   2190 void TextControlState::SetRangeText(const nsAString& aReplacement,
   2191                                    ErrorResult& aRv) {
   2192  uint32_t start, end;
   2193  GetSelectionRange(&start, &end, aRv);
   2194  if (aRv.Failed()) {
   2195    return;
   2196  }
   2197 
   2198  SetRangeText(aReplacement, start, end, SelectionMode::Preserve, aRv,
   2199               Some(start), Some(end));
   2200  // The instance may have already been deleted here.
   2201 }
   2202 
   2203 void TextControlState::SetRangeText(const nsAString& aReplacement,
   2204                                    uint32_t aStart, uint32_t aEnd,
   2205                                    SelectionMode aSelectMode, ErrorResult& aRv,
   2206                                    const Maybe<uint32_t>& aSelectionStart,
   2207                                    const Maybe<uint32_t>& aSelectionEnd) {
   2208  if (aStart > aEnd) {
   2209    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   2210    return;
   2211  }
   2212 
   2213  AutoTextControlHandlingState handlingSetRangeText(
   2214      *this, TextControlAction::SetRangeText);
   2215 
   2216  nsAutoString value;
   2217  mTextCtrlElement->GetValueFromSetRangeText(value);
   2218  uint32_t inputValueLength = value.Length();
   2219 
   2220  if (aStart > inputValueLength) {
   2221    aStart = inputValueLength;
   2222  }
   2223 
   2224  if (aEnd > inputValueLength) {
   2225    aEnd = inputValueLength;
   2226  }
   2227 
   2228  uint32_t selectionStart, selectionEnd;
   2229  if (!aSelectionStart) {
   2230    MOZ_ASSERT(!aSelectionEnd);
   2231    GetSelectionRange(&selectionStart, &selectionEnd, aRv);
   2232    if (aRv.Failed()) {
   2233      return;
   2234    }
   2235  } else {
   2236    MOZ_ASSERT(aSelectionEnd);
   2237    selectionStart = *aSelectionStart;
   2238    selectionEnd = *aSelectionEnd;
   2239  }
   2240 
   2241  // Batch selectionchanges from SetValueFromSetRangeText and SetSelectionRange
   2242  Selection* selection =
   2243      mSelCon ? mSelCon->GetSelection(SelectionType::eNormal) : nullptr;
   2244  SelectionBatcher selectionBatcher(
   2245      // `selection` will be grabbed by selectionBatcher itself.  Thus, we don't
   2246      // need to grab it by ourselves.
   2247      MOZ_KnownLive(selection), __FUNCTION__,
   2248      nsISelectionListener::JS_REASON);  // no-op if nullptr
   2249 
   2250  MOZ_ASSERT(aStart <= aEnd);
   2251  value.Replace(aStart, aEnd - aStart, aReplacement);
   2252  nsresult rv =
   2253      MOZ_KnownLive(mTextCtrlElement)->SetValueFromSetRangeText(value);
   2254  if (NS_FAILED(rv)) {
   2255    aRv.Throw(rv);
   2256    return;
   2257  }
   2258 
   2259  uint32_t newEnd = aStart + aReplacement.Length();
   2260  int32_t delta = aReplacement.Length() - (aEnd - aStart);
   2261 
   2262  switch (aSelectMode) {
   2263    case SelectionMode::Select:
   2264      selectionStart = aStart;
   2265      selectionEnd = newEnd;
   2266      break;
   2267    case SelectionMode::Start:
   2268      selectionStart = selectionEnd = aStart;
   2269      break;
   2270    case SelectionMode::End:
   2271      selectionStart = selectionEnd = newEnd;
   2272      break;
   2273    case SelectionMode::Preserve:
   2274      if (selectionStart > aEnd) {
   2275        selectionStart += delta;
   2276      } else if (selectionStart > aStart) {
   2277        selectionStart = aStart;
   2278      }
   2279 
   2280      if (selectionEnd > aEnd) {
   2281        selectionEnd += delta;
   2282      } else if (selectionEnd > aStart) {
   2283        selectionEnd = newEnd;
   2284      }
   2285      break;
   2286    default:
   2287      MOZ_ASSERT_UNREACHABLE("Unknown mode!");
   2288  }
   2289 
   2290  SetSelectionRange(selectionStart, selectionEnd, Optional<nsAString>(), aRv);
   2291  if (IsSelectionCached()) {
   2292    // SetValueFromSetRangeText skipped SetMaxLength, set it here properly
   2293    GetSelectionProperties().SetMaxLength(value.Length());
   2294  }
   2295 }
   2296 
   2297 void TextControlState::DestroyEditor() {
   2298  // notify the editor that we are going away
   2299  if (mEditorInitialized) {
   2300    // FYI: TextEditor checks whether it's destroyed or not immediately after
   2301    //      changes the DOM tree or selection so that it's safe to call
   2302    //      PreDestroy() here even while we're handling actions with
   2303    //      mTextEditor.
   2304    MOZ_ASSERT(!mPasswordMaskData);
   2305    RefPtr<TextEditor> textEditor = mTextEditor;
   2306    mPasswordMaskData = textEditor->PreDestroy();
   2307    MOZ_ASSERT_IF(mPasswordMaskData, !mPasswordMaskData->mTimer);
   2308    mEditorInitialized = false;
   2309  }
   2310 }
   2311 
   2312 void TextControlState::UnbindFromFrame(nsTextControlFrame* aFrame) {
   2313  if (NS_WARN_IF(!mBoundFrame)) {
   2314    return;
   2315  }
   2316 
   2317  // If it was, however, it should be unbounded from the same frame.
   2318  MOZ_ASSERT(aFrame == mBoundFrame, "Unbinding from the wrong frame");
   2319  if (aFrame && aFrame != mBoundFrame) {
   2320    return;
   2321  }
   2322 
   2323  AutoTextControlHandlingState handlingUnbindFromFrame(
   2324      *this, TextControlAction::UnbindFromFrame);
   2325 
   2326  if (mSelCon) {
   2327    mSelCon->SelectionWillLoseFocus();
   2328  }
   2329 
   2330  // We need to start storing the value outside of the editor if we're not
   2331  // going to use it anymore, so retrieve it for now.
   2332  nsAutoString value;
   2333  GetValue(value, /* aForDisplay = */ false);
   2334 
   2335  if (mRestoringSelection) {
   2336    mRestoringSelection->Revoke();
   2337    mRestoringSelection = nullptr;
   2338  }
   2339 
   2340  // Save our selection state if needed.
   2341  // Note that GetSelectionRange will attempt to work with our selection
   2342  // controller, so we should make sure we do it before we start doing things
   2343  // like destroying our editor (if we have one), tearing down the selection
   2344  // controller, and so forth.
   2345  if (!IsSelectionCached()) {
   2346    // Go ahead and cache it now.
   2347    uint32_t start = 0, end = 0;
   2348    GetSelectionRange(&start, &end, IgnoreErrors());
   2349 
   2350    SelectionDirection direction = GetSelectionDirection(IgnoreErrors());
   2351 
   2352    SelectionProperties& props = GetSelectionProperties();
   2353    props.SetMaxLength(value.Length());
   2354    props.SetStart(start);
   2355    props.SetEnd(end);
   2356    props.SetDirection(direction);
   2357    mSelectionCached = true;
   2358  }
   2359 
   2360  // Destroy our editor
   2361  DestroyEditor();
   2362 
   2363  // Clean up the controllers if they exist.
   2364  if (!SuppressEventHandlers(mBoundFrame->PresContext())) {
   2365    const nsCOMPtr<nsIControllers> controllers = [&]() -> nsIControllers* {
   2366      if (const auto* const inputElement =
   2367              HTMLInputElement::FromNode(mTextCtrlElement)) {
   2368        return inputElement->GetExtantControllers();
   2369      }
   2370      if (const auto* const textAreaElement =
   2371              HTMLTextAreaElement::FromNode(mTextCtrlElement)) {
   2372        return textAreaElement->GetExtantControllers();
   2373      }
   2374      return nullptr;
   2375    }();
   2376 
   2377    if (controllers) {
   2378      uint32_t numControllers;
   2379      nsresult rv = controllers->GetControllerCount(&numControllers);
   2380      NS_ASSERTION((NS_SUCCEEDED(rv)),
   2381                   "bad result in gfx text control destructor");
   2382      for (uint32_t i = 0; i < numControllers; i++) {
   2383        nsCOMPtr<nsIController> controller;
   2384        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
   2385        if (NS_SUCCEEDED(rv) && controller) {
   2386          nsCOMPtr<nsBaseCommandController> editController =
   2387              do_QueryInterface(controller);
   2388          if (editController) {
   2389            editController->SetContext(nullptr);
   2390          }
   2391        }
   2392      }
   2393    }
   2394  }
   2395 
   2396  if (mSelCon) {
   2397    if (mTextListener) {
   2398      mTextListener->EndListeningToSelectionChange();
   2399    }
   2400 
   2401    mSelCon->SetScrollContainerFrame(nullptr);
   2402    mSelCon = nullptr;
   2403  }
   2404 
   2405  if (mTextListener) {
   2406    mTextListener->SetFrame(nullptr);
   2407 
   2408    EventListenerManager* manager =
   2409        mTextCtrlElement->GetExistingListenerManager();
   2410    if (manager) {
   2411      manager->RemoveEventListenerByType(mTextListener, u"keydown"_ns,
   2412                                         TrustedEventsAtSystemGroupBubble());
   2413      manager->RemoveEventListenerByType(mTextListener, u"keypress"_ns,
   2414                                         TrustedEventsAtSystemGroupBubble());
   2415      manager->RemoveEventListenerByType(mTextListener, u"keyup"_ns,
   2416                                         TrustedEventsAtSystemGroupBubble());
   2417    }
   2418 
   2419    mTextListener = nullptr;
   2420  }
   2421 
   2422  mBoundFrame = nullptr;
   2423 
   2424  // Now that we don't have a frame any more, store the value in the text
   2425  // buffer. The only case where we don't do this is if a value transfer is in
   2426  // progress.
   2427  if (!mValueTransferInProgress) {
   2428    DebugOnly<bool> ok = SetValue(value, ValueSetterOption::ByInternalAPI);
   2429    // TODO Find something better to do if this fails...
   2430    NS_WARNING_ASSERTION(ok, "SetValue() couldn't allocate memory");
   2431    // And mark the selection as dirty to make sure the selection will be
   2432    // restored properly in RestoreSelectionState. See bug 1993351.
   2433    if (IsSelectionCached()) {
   2434      SelectionProperties& props = GetSelectionProperties();
   2435      props.SetIsDirty();
   2436    }
   2437  }
   2438 }
   2439 
   2440 void TextControlState::GetValue(nsAString& aValue, bool aForDisplay) const {
   2441  // While SetValue() is being called and requesting to commit composition to
   2442  // IME, GetValue() may be called for appending text or something.  Then, we
   2443  // need to return the latest aValue of SetValue() since the value hasn't
   2444  // been set to the editor yet.
   2445  // XXX After implementing "beforeinput" event, this becomes wrong.  The
   2446  //     value should be modified immediately after "beforeinput" event for
   2447  //     "insertReplacementText".
   2448  if (mHandlingState &&
   2449      mHandlingState->IsHandling(TextControlAction::CommitComposition)) {
   2450    aValue = mHandlingState->GetSettingValue();
   2451    MOZ_ASSERT(aValue.FindChar(u'\r') == -1);
   2452    return;
   2453  }
   2454 
   2455  if (mTextEditor && mBoundFrame &&
   2456      (mEditorInitialized || !IsSingleLineTextControl())) {
   2457    if (!mBoundFrame->CachedValue().IsVoid()) {
   2458      aValue = mBoundFrame->CachedValue();
   2459      MOZ_ASSERT(aValue.FindChar(u'\r') == -1);
   2460      return;
   2461    }
   2462 
   2463    aValue.Truncate();  // initialize out param
   2464    if (mEditorInitialized) {
   2465      DebugOnly<nsresult> rv = mTextEditor->ComputeTextValue(aValue);
   2466      MOZ_ASSERT(aValue.FindChar(u'\r') == -1);
   2467      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get value");
   2468    }
   2469    mBoundFrame->CacheValue(aValue);
   2470  } else if (!mTextCtrlElement->ValueChanged() || mValue.IsVoid()) {
   2471    // Use nsString to avoid copying string buffer at setting aValue.
   2472    nsString value;
   2473    mTextCtrlElement->GetDefaultValueFromContent(value, aForDisplay);
   2474    // TODO: We should make default value not include \r.
   2475    nsContentUtils::PlatformToDOMLineBreaks(value);
   2476    aValue = std::move(value);
   2477  } else {
   2478    aValue = mValue;
   2479    MOZ_ASSERT(aValue.FindChar(u'\r') == -1);
   2480  }
   2481 }
   2482 
   2483 bool TextControlState::ValueEquals(const nsAString& aValue) const {
   2484  nsAutoString value;
   2485  GetValue(value, /* aForDisplay = */ true);
   2486  return aValue.Equals(value);
   2487 }
   2488 
   2489 #ifdef DEBUG
   2490 // @param aOptions TextControlState::ValueSetterOptions
   2491 bool AreFlagsNotDemandingContradictingMovements(
   2492    const ValueSetterOptions& aOptions) {
   2493  return !aOptions.contains(
   2494      {ValueSetterOption::MoveCursorToBeginSetSelectionDirectionForward,
   2495       ValueSetterOption::MoveCursorToEndIfValueChanged});
   2496 }
   2497 #endif  // DEBUG
   2498 
   2499 bool TextControlState::SetValue(const nsAString& aValue,
   2500                                const nsAString* aOldValue,
   2501                                const ValueSetterOptions& aOptions) {
   2502  if (mHandlingState &&
   2503      mHandlingState->IsHandling(TextControlAction::CommitComposition)) {
   2504    // GetValue doesn't return current text frame's content during committing.
   2505    // So we cannot trust this old value
   2506    aOldValue = nullptr;
   2507  }
   2508 
   2509  if (mPasswordMaskData) {
   2510    if (mHandlingState &&
   2511        mHandlingState->Is(TextControlAction::UnbindFromFrame)) {
   2512      // If we're called by UnbindFromFrame, we shouldn't reset unmasked range.
   2513    } else {
   2514      // Otherwise, we should mask the new password, even if it's same value
   2515      // since the same value may be one for different web app's.
   2516      mPasswordMaskData->Reset();
   2517    }
   2518  }
   2519 
   2520  const bool wasHandlingSetValue =
   2521      mHandlingState && mHandlingState->IsHandling(TextControlAction::SetValue);
   2522 
   2523  ErrorResult error;
   2524  AutoTextControlHandlingState handlingSetValue(
   2525      *this, TextControlAction::SetValue, aValue, aOldValue, aOptions, error);
   2526  if (error.Failed()) {
   2527    MOZ_ASSERT(error.ErrorCodeIs(NS_ERROR_OUT_OF_MEMORY));
   2528    error.SuppressException();
   2529    return false;
   2530  }
   2531 
   2532  const auto changeKind = [&] {
   2533    if (aOptions.contains(ValueSetterOption::ByInternalAPI)) {
   2534      return ValueChangeKind::Internal;
   2535    }
   2536    if (aOptions.contains(ValueSetterOption::BySetUserInputAPI)) {
   2537      return ValueChangeKind::UserInteraction;
   2538    }
   2539    return ValueChangeKind::Script;
   2540  }();
   2541 
   2542  if (changeKind == ValueChangeKind::Script) {
   2543    // This value change will not be interactive. If we're an input that was
   2544    // interactively edited, save the last interactive value now before it goes
   2545    // away.
   2546    if (auto* input = HTMLInputElement::FromNode(mTextCtrlElement)) {
   2547      if (input->LastValueChangeWasInteractive()) {
   2548        GetValue(mLastInteractiveValue, /* aForDisplay = */ true);
   2549      }
   2550    }
   2551  }
   2552 
   2553  // Note that if this may be called during reframe of the editor.  In such
   2554  // case, we shouldn't commit composition.  Therefore, when this is called
   2555  // for internal processing, we shouldn't commit the composition.
   2556  // TODO: In strictly speaking, we should move committing composition into
   2557  //       editor because if "beforeinput" for this setting value is canceled,
   2558  //       we shouldn't commit composition.  However, in Firefox, we never
   2559  //       call this via `setUserInput` during composition.  Therefore, the
   2560  //       bug must not be reproducible actually.
   2561  if (aOptions.contains(ValueSetterOption::BySetUserInputAPI) ||
   2562      aOptions.contains(ValueSetterOption::ByContentAPI)) {
   2563    RefPtr<TextComposition> compositionInEditor =
   2564        mTextEditor ? mTextEditor->GetComposition() : nullptr;
   2565    if (compositionInEditor && compositionInEditor->IsComposing()) {
   2566      // When this is called recursively, there shouldn't be composition.
   2567      if (handlingSetValue.IsHandling(TextControlAction::CommitComposition)) {
   2568        // Don't request to commit composition again.  But if it occurs,
   2569        // we should skip to set the new value to the editor here.  It should
   2570        // be set later with the newest value.
   2571        return true;
   2572      }
   2573      if (NS_WARN_IF(!mBoundFrame)) {
   2574        // We're not sure if this case is possible.
   2575      } else {
   2576        // If setting value won't change current value, we shouldn't commit
   2577        // composition for compatibility with the other browsers.
   2578        MOZ_ASSERT(!aOldValue || ValueEquals(*aOldValue));
   2579        bool isSameAsCurrentValue =
   2580            aOldValue ? aOldValue->Equals(handlingSetValue.GetSettingValue())
   2581                      : ValueEquals(handlingSetValue.GetSettingValue());
   2582        if (isSameAsCurrentValue) {
   2583          // Note that in this case, we shouldn't fire any events with setting
   2584          // value because event handlers may try to set value recursively but
   2585          // we cannot commit composition at that time due to unsafe to run
   2586          // script (see below).
   2587          return true;
   2588        }
   2589      }
   2590      // If there is composition, need to commit composition first because
   2591      // other browsers do that.
   2592      // NOTE: We don't need to block nested calls of this because input nor
   2593      //       other events won't be fired by setting values and script blocker
   2594      //       is used during setting the value to the editor.  IE also allows
   2595      //       to set the editor value on the input event which is caused by
   2596      //       forcibly committing composition.
   2597      AutoTextControlHandlingState handlingCommitComposition(
   2598          *this, TextControlAction::CommitComposition);
   2599      if (nsContentUtils::IsSafeToRunScript()) {
   2600        // While we're committing composition, we don't want TextEditor
   2601        // dispatches nested `beforeinput`/`input` events if this is called by a
   2602        // `beforeinput`/`input` event listener since the commit value will be
   2603        // completely overwritten by the new value soon and the web app do not
   2604        // need to handle the temporary input caused by committing composition
   2605        // which is caused by updating the value by the web app itself  Note
   2606        // that `input` event listener may be async function and setting value
   2607        // may occur after the editor ends dispatching `input` event. Even in
   2608        // this case, to avoid nest call of the async `input` event listener, we
   2609        // need to suppress `input` events caused by committing composition.  On
   2610        // the other hand, we need to dispatch `input` event when the value is
   2611        // set by a `compositionupdate` event listener because once we suppress
   2612        // `input` event for it, the composition change won't cause dispatching
   2613        // `input` event.  Therefore, we should not suppress `input` events
   2614        // before the editor starts handling the composition change, but we need
   2615        // to suppress `input` events even after the editor ends handling the
   2616        // change.
   2617        // FYI: Even if we suppress `input` event dispatching,
   2618        // `compositionupdate` and `compositionend` caused by the committing
   2619        // composition will be fired.  Therefore, everything could occur during
   2620        // a the following call.  I.e., the document may be unloaded by the web
   2621        // app itself.
   2622        Maybe<AutoInputEventSuppresser> preventInputEventsDuringCommit;
   2623        if (mTextEditor->IsDispatchingInputEvent() ||
   2624            compositionInEditor->EditorHasHandledLatestChange()) {
   2625          preventInputEventsDuringCommit.emplace(mTextEditor);
   2626        }
   2627        OwningNonNull<TextEditor> textEditor(*mTextEditor);
   2628        nsresult rv = textEditor->CommitComposition();
   2629        if (handlingCommitComposition.IsTextControlStateDestroyed()) {
   2630          return true;
   2631        }
   2632        if (NS_FAILED(rv)) {
   2633          NS_WARNING("TextControlState failed to commit composition");
   2634          return true;
   2635        }
   2636        // Note that if a composition event listener sets editor value again,
   2637        // we should use the new value here.  The new value is stored in
   2638        // handlingSetValue right now.
   2639      } else {
   2640        NS_WARNING(
   2641            "SetValue() is called when there is composition but "
   2642            "it's not safe to request to commit the composition");
   2643      }
   2644    }
   2645  }
   2646 
   2647  if (mTextEditor && mBoundFrame) {
   2648    if (!SetValueWithTextEditor(handlingSetValue)) {
   2649      return false;
   2650    }
   2651  } else if (!SetValueWithoutTextEditor(handlingSetValue)) {
   2652    return false;
   2653  }
   2654 
   2655  // If we were handling SetValue() before, don't update the DOM state twice,
   2656  // just let the outer call do so.
   2657  if (!wasHandlingSetValue) {
   2658    handlingSetValue.GetTextControlElement()->OnValueChanged(
   2659        changeKind, handlingSetValue.GetSettingValue());
   2660  }
   2661  return true;
   2662 }
   2663 
   2664 bool TextControlState::SetValueWithTextEditor(
   2665    AutoTextControlHandlingState& aHandlingSetValue) {
   2666  MOZ_ASSERT(aHandlingSetValue.Is(TextControlAction::SetValue));
   2667  MOZ_ASSERT(mTextEditor);
   2668  MOZ_ASSERT(mBoundFrame);
   2669  NS_WARNING_ASSERTION(!EditorHasComposition(),
   2670                       "Failed to commit composition before setting value.  "
   2671                       "Investigate the cause!");
   2672 
   2673 #ifdef DEBUG
   2674  if (IsSingleLineTextControl()) {
   2675    NS_ASSERTION(mEditorInitialized || aHandlingSetValue.IsHandling(
   2676                                           TextControlAction::PrepareEditor),
   2677                 "We should never try to use the editor if we're not "
   2678                 "initialized unless we're being initialized");
   2679  }
   2680 #endif
   2681 
   2682  MOZ_ASSERT(!aHandlingSetValue.GetOldValue() ||
   2683             ValueEquals(*aHandlingSetValue.GetOldValue()));
   2684  const bool isSameAsCurrentValue =
   2685      aHandlingSetValue.GetOldValue()
   2686          ? aHandlingSetValue.GetOldValue()->Equals(
   2687                aHandlingSetValue.GetSettingValue())
   2688          : ValueEquals(aHandlingSetValue.GetSettingValue());
   2689 
   2690  // this is necessary to avoid infinite recursion
   2691  if (isSameAsCurrentValue) {
   2692    return true;
   2693  }
   2694 
   2695  RefPtr<TextEditor> textEditor = mTextEditor;
   2696 
   2697  nsCOMPtr<Document> document = textEditor->GetDocument();
   2698  if (NS_WARN_IF(!document)) {
   2699    return true;
   2700  }
   2701 
   2702  // Time to mess with our security context... See comments in GetValue()
   2703  // for why this is needed.  Note that we have to do this up here, because
   2704  // otherwise SelectAll() will fail.
   2705  AutoNoJSAPI nojsapi;
   2706 
   2707  // FYI: It's safe to use raw pointer for selection here because
   2708  //      SelectionBatcher will grab it with RefPtr.
   2709  Selection* selection = mSelCon->GetSelection(SelectionType::eNormal);
   2710  SelectionBatcher selectionBatcher(
   2711      // `selection` will be grabbed by selectionBatcher itself.  Thus, we don't
   2712      // need to grab it by ourselves.
   2713      MOZ_KnownLive(selection), __FUNCTION__);
   2714 
   2715  // get the flags, remove readonly, disabled and max-length,
   2716  // set the value, restore flags
   2717  AutoRestoreEditorState restoreState(textEditor);
   2718 
   2719  aHandlingSetValue.WillSetValueWithTextEditor();
   2720 
   2721  if (aHandlingSetValue.ValueSetterOptionsRef().contains(
   2722          ValueSetterOption::BySetUserInputAPI)) {
   2723    // If the caller inserts text as part of user input, for example,
   2724    // autocomplete, we need to replace the text as "insert string"
   2725    // because undo should cancel only this operation (i.e., previous
   2726    // transactions typed by user shouldn't be merged with this).
   2727    // In this case, we need to dispatch "input" event because
   2728    // web apps may need to know the user's operation.
   2729    // In this case, we need to dispatch "beforeinput" events since
   2730    // we're emulating the user's input.  Passing nullptr as
   2731    // nsIPrincipal means that that may be user's input.  So, let's
   2732    // do it.
   2733    nsresult rv = textEditor->ReplaceTextAsAction(
   2734        aHandlingSetValue.GetSettingValue(), nullptr,
   2735        StaticPrefs::dom_input_event_allow_to_cancel_set_user_input()
   2736            ? TextEditor::AllowBeforeInputEventCancelable::Yes
   2737            : TextEditor::AllowBeforeInputEventCancelable::No);
   2738    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2739                         "EditorBase::ReplaceTextAsAction() failed");
   2740    return rv != NS_ERROR_OUT_OF_MEMORY;
   2741  }
   2742 
   2743  // Don't dispatch "beforeinput" event nor "input" event for setting value
   2744  // by script.
   2745  AutoInputEventSuppresser suppressInputEventDispatching(textEditor);
   2746 
   2747  // On <input> or <textarea>, we shouldn't preserve existing undo
   2748  // transactions because other browsers do not preserve them too
   2749  // and not preserving transactions makes setting value faster.
   2750  //
   2751  // (Except if chrome opts into this behavior).
   2752  Maybe<AutoDisableUndo> disableUndo;
   2753  if (!aHandlingSetValue.ValueSetterOptionsRef().contains(
   2754          ValueSetterOption::PreserveUndoHistory)) {
   2755    disableUndo.emplace(textEditor);
   2756  }
   2757 
   2758  if (selection) {
   2759    // Since we don't use undo transaction, we don't need to store
   2760    // selection state.  SetText will set selection to tail.
   2761    IgnoredErrorResult ignoredError;
   2762    MOZ_KnownLive(selection)->RemoveAllRanges(ignoredError);
   2763    NS_WARNING_ASSERTION(!ignoredError.Failed(),
   2764                         "Selection::RemoveAllRanges() failed, but ignored");
   2765  }
   2766 
   2767  // In this case, we makes the editor stop dispatching "input"
   2768  // event so that passing nullptr as nsIPrincipal is safe for now.
   2769  nsresult rv = textEditor->SetTextAsAction(
   2770      aHandlingSetValue.GetSettingValue(),
   2771      aHandlingSetValue.ValueSetterOptionsRef().contains(
   2772          ValueSetterOption::BySetUserInputAPI) &&
   2773              !StaticPrefs::dom_input_event_allow_to_cancel_set_user_input()
   2774          ? TextEditor::AllowBeforeInputEventCancelable::No
   2775          : TextEditor::AllowBeforeInputEventCancelable::Yes,
   2776      nullptr);
   2777  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2778                       "TextEditor::SetTextAsAction() failed");
   2779 
   2780  // Call the listener's OnEditActionHandled() callback manually if
   2781  // OnEditActionHandled() hasn't been called yet since TextEditor don't use
   2782  // the transaction manager in this path and it could be that the editor
   2783  // would bypass calling the listener for that reason.
   2784  if (!aHandlingSetValue.HasEditActionHandled()) {
   2785    nsresult rvOnEditActionHandled =
   2786        MOZ_KnownLive(aHandlingSetValue.GetTextInputListener())
   2787            ->OnEditActionHandled(*textEditor);
   2788    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvOnEditActionHandled),
   2789                         "TextInputListener::OnEditActionHandled() failed");
   2790    if (rv != NS_ERROR_OUT_OF_MEMORY) {
   2791      rv = rvOnEditActionHandled;
   2792    }
   2793  }
   2794 
   2795  // When the <textarea> is not dirty, the default value is mirrored into the
   2796  // anonymous subtree asynchronously.  This may occur during a reframe.
   2797  // Therefore, if IMEContentObserver was initialized with our editor but our
   2798  // editor is being initialized, it has not been observing the new anonymous
   2799  // subtree.  In this case, we need to notify IMEContentObserver of the default
   2800  // value change.
   2801  if (mTextCtrlElement && mTextCtrlElement->IsTextArea() &&
   2802      !mTextCtrlElement->ValueChanged() && textEditor->IsBeingInitialized() &&
   2803      !textEditor->Destroyed()) {
   2804    IMEContentObserver* observer = GetIMEContentObserver();
   2805    if (observer && observer->WasInitializedWith(*textEditor)) {
   2806      nsAutoString currentValue;
   2807      textEditor->ComputeTextValue(currentValue);
   2808      observer->OnTextControlValueChangedWhileNotObservable(currentValue);
   2809    }
   2810  }
   2811 
   2812  return rv != NS_ERROR_OUT_OF_MEMORY;
   2813 }
   2814 
   2815 bool TextControlState::SetValueWithoutTextEditor(
   2816    AutoTextControlHandlingState& aHandlingSetValue) {
   2817  MOZ_ASSERT(aHandlingSetValue.Is(TextControlAction::SetValue));
   2818  MOZ_ASSERT(!mTextEditor || !mBoundFrame);
   2819  NS_WARNING_ASSERTION(!EditorHasComposition(),
   2820                       "Failed to commit composition before setting value.  "
   2821                       "Investigate the cause!");
   2822 
   2823  if (mValue.IsVoid()) {
   2824    mValue.SetIsVoid(false);
   2825  }
   2826 
   2827  // We can't just early-return here, because OnValueChanged below still need to
   2828  // be called.
   2829  if (!mValue.Equals(aHandlingSetValue.GetSettingValue())) {
   2830    bool handleSettingValue = true;
   2831    // If `SetValue()` call is nested, `GetSettingValue()` result will be
   2832    // modified.  So, we need to store input event data value before
   2833    // dispatching beforeinput event.
   2834    nsString inputEventData(aHandlingSetValue.GetSettingValue());
   2835    if (aHandlingSetValue.ValueSetterOptionsRef().contains(
   2836            ValueSetterOption::BySetUserInputAPI) &&
   2837        !aHandlingSetValue.HasBeforeInputEventDispatched()) {
   2838      // This probably occurs when session restorer sets the old value with
   2839      // `setUserInput`.  If so, we need to dispatch "beforeinput" event of
   2840      // "insertReplacementText" for conforming to the spec.  However, the
   2841      // spec does NOT treat the session restoring case.  Therefore, if this
   2842      // breaks session restorere in a lot of web apps, we should probably
   2843      // stop dispatching it or make it non-cancelable.
   2844      MOZ_ASSERT(aHandlingSetValue.GetTextControlElement());
   2845      MOZ_ASSERT(!aHandlingSetValue.GetSettingValue().IsVoid());
   2846      aHandlingSetValue.WillDispatchBeforeInputEvent();
   2847      nsEventStatus status = nsEventStatus_eIgnore;
   2848      DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(
   2849          MOZ_KnownLive(aHandlingSetValue.GetTextControlElement()),
   2850          eEditorBeforeInput, EditorInputType::eInsertReplacementText, nullptr,
   2851          InputEventOptions(
   2852              inputEventData,
   2853              StaticPrefs::dom_input_event_allow_to_cancel_set_user_input()
   2854                  ? InputEventOptions::NeverCancelable::No
   2855                  : InputEventOptions::NeverCancelable::Yes),
   2856          &status);
   2857      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2858                           "Failed to dispatch beforeinput event");
   2859      if (status == nsEventStatus_eConsumeNoDefault) {
   2860        return true;  // "beforeinput" event was canceled.
   2861      }
   2862      // If we were destroyed by "beforeinput" event listeners, probably, we
   2863      // don't need to keep handling it.
   2864      if (aHandlingSetValue.IsTextControlStateDestroyed()) {
   2865        return true;
   2866      }
   2867      // Even if "beforeinput" event was not canceled, its listeners may do
   2868      // something.  If it causes creating `TextEditor` and bind this to a
   2869      // frame, we need to use the path, but `TextEditor` shouldn't fire
   2870      // "beforeinput" event again.  Therefore, we need to prevent editor
   2871      // to dispatch it.
   2872      if (mTextEditor && mBoundFrame) {
   2873        AutoInputEventSuppresser suppressInputEvent(mTextEditor);
   2874        if (!SetValueWithTextEditor(aHandlingSetValue)) {
   2875          return false;
   2876        }
   2877        // If we were destroyed by "beforeinput" event listeners, probably, we
   2878        // don't need to keep handling it.
   2879        if (aHandlingSetValue.IsTextControlStateDestroyed()) {
   2880          return true;
   2881        }
   2882        handleSettingValue = false;
   2883      }
   2884    }
   2885 
   2886    if (handleSettingValue) {
   2887      if (!mValue.Assign(aHandlingSetValue.GetSettingValue(), fallible)) {
   2888        return false;
   2889      }
   2890 
   2891      // Since we have no editor we presumably have cached selection state.
   2892      if (IsSelectionCached()) {
   2893        MOZ_ASSERT(AreFlagsNotDemandingContradictingMovements(
   2894            aHandlingSetValue.ValueSetterOptionsRef()));
   2895 
   2896        SelectionProperties& props = GetSelectionProperties();
   2897        // Setting a max length and thus capping selection range early prevents
   2898        // selection change detection in setRangeText. Temporarily disable
   2899        // capping here with UINT32_MAX, and set it later in ::SetRangeText().
   2900        props.SetMaxLength(aHandlingSetValue.ValueSetterOptionsRef().contains(
   2901                               ValueSetterOption::BySetRangeTextAPI)
   2902                               ? UINT32_MAX
   2903                               : aHandlingSetValue.GetSettingValue().Length());
   2904        if (aHandlingSetValue.ValueSetterOptionsRef().contains(
   2905                ValueSetterOption::MoveCursorToEndIfValueChanged)) {
   2906          props.SetStart(aHandlingSetValue.GetSettingValue().Length());
   2907          props.SetEnd(aHandlingSetValue.GetSettingValue().Length());
   2908          props.SetDirection(SelectionDirection::Forward);
   2909        } else if (aHandlingSetValue.ValueSetterOptionsRef().contains(
   2910                       ValueSetterOption::
   2911                           MoveCursorToBeginSetSelectionDirectionForward)) {
   2912          props.SetStart(0);
   2913          props.SetEnd(0);
   2914          props.SetDirection(SelectionDirection::Forward);
   2915        }
   2916      }
   2917 
   2918      // Update the frame display if needed
   2919      if (mBoundFrame) {
   2920        mBoundFrame->UpdateValueDisplay(true);
   2921      }
   2922 
   2923      // If the text control element has focus, IMEContentObserver is not
   2924      // observing the content changes due to no bound frame or no TextEditor.
   2925      // Therefore, we need to let IMEContentObserver know all values are being
   2926      // replaced.
   2927      if (IMEContentObserver* observer = GetIMEContentObserver()) {
   2928        observer->OnTextControlValueChangedWhileNotObservable(mValue);
   2929      }
   2930    }
   2931 
   2932    // If this is called as part of user input, we need to dispatch "input"
   2933    // event with "insertReplacementText" since web apps may want to know
   2934    // the user operation which changes editor value with a built-in function
   2935    // like autocomplete, password manager, session restore, etc.
   2936    // XXX Should we stop dispatching `input` event if the text control
   2937    //     element has already removed from the DOM tree by a `beforeinput`
   2938    //     event listener?
   2939    if (aHandlingSetValue.ValueSetterOptionsRef().contains(
   2940            ValueSetterOption::BySetUserInputAPI)) {
   2941      MOZ_ASSERT(aHandlingSetValue.GetTextControlElement());
   2942 
   2943      // Update validity state before dispatching "input" event for its
   2944      // listeners like `EditorBase::NotifyEditorObservers()`.
   2945      aHandlingSetValue.GetTextControlElement()->OnValueChanged(
   2946          ValueChangeKind::UserInteraction,
   2947          aHandlingSetValue.GetSettingValue());
   2948 
   2949      ClearLastInteractiveValue();
   2950 
   2951      MOZ_ASSERT(!aHandlingSetValue.GetSettingValue().IsVoid());
   2952      DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(
   2953          MOZ_KnownLive(aHandlingSetValue.GetTextControlElement()),
   2954          eEditorInput, EditorInputType::eInsertReplacementText, nullptr,
   2955          InputEventOptions(inputEventData,
   2956                            InputEventOptions::NeverCancelable::No));
   2957      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2958                           "Failed to dispatch input event");
   2959    }
   2960  } else {
   2961    // Even if our value is not actually changing, apparently we need to mark
   2962    // our SelectionProperties dirty to make accessibility tests happy.
   2963    // Probably because they depend on the SetSelectionRange() call we make on
   2964    // our frame in RestoreSelectionState, but I have no idea why they do.
   2965    if (IsSelectionCached()) {
   2966      SelectionProperties& props = GetSelectionProperties();
   2967      props.SetIsDirty();
   2968    }
   2969  }
   2970 
   2971  return true;
   2972 }
   2973 
   2974 void TextControlState::InitializeKeyboardEventListeners() {
   2975  // register key listeners
   2976  EventListenerManager* manager =
   2977      mTextCtrlElement->GetOrCreateListenerManager();
   2978  if (manager) {
   2979    manager->AddEventListenerByType(mTextListener, u"keydown"_ns,
   2980                                    TrustedEventsAtSystemGroupBubble());
   2981    manager->AddEventListenerByType(mTextListener, u"keypress"_ns,
   2982                                    TrustedEventsAtSystemGroupBubble());
   2983    manager->AddEventListenerByType(mTextListener, u"keyup"_ns,
   2984                                    TrustedEventsAtSystemGroupBubble());
   2985  }
   2986 
   2987  mSelCon->SetScrollContainerFrame(mBoundFrame->GetScrollTargetFrame());
   2988 }
   2989 
   2990 void TextControlState::SetPreviewText(const nsAString& aValue, bool aNotify) {
   2991  // If we don't have a preview div, there's nothing to do.
   2992  Element* previewDiv = GetPreviewNode();
   2993  if (!previewDiv) {
   2994    return;
   2995  }
   2996 
   2997  nsAutoString previewValue(aValue);
   2998 
   2999  nsContentUtils::RemoveNewlines(previewValue);
   3000  MOZ_ASSERT(previewDiv->GetFirstChild(), "preview div has no child");
   3001  previewDiv->GetFirstChild()->AsText()->SetText(previewValue, aNotify);
   3002 }
   3003 
   3004 void TextControlState::GetPreviewText(nsAString& aValue) {
   3005  // If we don't have a preview div, there's nothing to do.
   3006  Element* previewDiv = GetPreviewNode();
   3007  if (!previewDiv) {
   3008    return;
   3009  }
   3010 
   3011  MOZ_ASSERT(previewDiv->GetFirstChild(), "preview div has no child");
   3012  const CharacterDataBuffer* characterDataBuffer =
   3013      previewDiv->GetFirstChild()->GetCharacterDataBuffer();
   3014 
   3015  aValue.Truncate();
   3016  characterDataBuffer->AppendTo(aValue);
   3017 }
   3018 
   3019 bool TextControlState::EditorHasComposition() {
   3020  return mTextEditor && mTextEditor->IsIMEComposing();
   3021 }
   3022 
   3023 IMEContentObserver* TextControlState::GetIMEContentObserver() const {
   3024  if (NS_WARN_IF(!mTextCtrlElement) ||
   3025      mTextCtrlElement != IMEStateManager::GetFocusedElement()) {
   3026    return nullptr;
   3027  }
   3028  IMEContentObserver* observer = IMEStateManager::GetActiveContentObserver();
   3029  // The text control element may be an editing host.  In this case, the
   3030  // observer does not observe the anonymous nodes under mTextCtrlElement.
   3031  // So, it means that the observer is not for ours.
   3032  return observer && observer->EditorIsTextEditor() ? observer : nullptr;
   3033 }
   3034 
   3035 }  // namespace mozilla