LCOV - code coverage report
Current view: top level - dom/html - nsTextEditorState.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 719 1285 56.0 %
Date: 2017-07-14 16:53:18 Functions: 88 145 60.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13