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