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