LCOV - code coverage report
Current view: top level - dom/base - TextInputProcessor.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 621 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 70 0.0 %
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 "gfxPrefs.h"
       8             : #include "mozilla/dom/Event.h"
       9             : #include "mozilla/EventForwards.h"
      10             : #include "mozilla/TextEventDispatcher.h"
      11             : #include "mozilla/TextEvents.h"
      12             : #include "mozilla/TextInputProcessor.h"
      13             : #include "mozilla/widget/IMEData.h"
      14             : #include "nsContentUtils.h"
      15             : #include "nsIDocShell.h"
      16             : #include "nsIWidget.h"
      17             : #include "nsPIDOMWindow.h"
      18             : #include "nsPresContext.h"
      19             : 
      20             : using namespace mozilla::widget;
      21             : 
      22             : namespace mozilla {
      23             : 
      24             : /******************************************************************************
      25             :  * TextInputProcessorNotification
      26             :  ******************************************************************************/
      27             : 
      28             : class TextInputProcessorNotification final :
      29             :         public nsITextInputProcessorNotification
      30             : {
      31             :   typedef IMENotification::SelectionChangeData SelectionChangeData;
      32             :   typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
      33             :   typedef IMENotification::TextChangeData TextChangeData;
      34             :   typedef IMENotification::TextChangeDataBase TextChangeDataBase;
      35             : 
      36             : public:
      37           0 :   explicit TextInputProcessorNotification(const char* aType)
      38           0 :     : mType(aType)
      39             :   {
      40           0 :   }
      41             : 
      42           0 :   explicit TextInputProcessorNotification(
      43             :              const TextChangeDataBase& aTextChangeData)
      44           0 :     : mType("notify-text-change")
      45           0 :     , mTextChangeData(aTextChangeData)
      46             :   {
      47           0 :   }
      48             : 
      49           0 :   explicit TextInputProcessorNotification(
      50             :              const SelectionChangeDataBase& aSelectionChangeData)
      51           0 :     : mType("notify-selection-change")
      52           0 :     , mSelectionChangeData(aSelectionChangeData)
      53             :   {
      54             :     // SelectionChangeDataBase::mString still refers nsString instance owned
      55             :     // by aSelectionChangeData.  So, this needs to copy the instance.
      56           0 :     nsString* string = new nsString(aSelectionChangeData.String());
      57           0 :     mSelectionChangeData.mString = string;
      58           0 :   }
      59             : 
      60             :   NS_DECL_ISUPPORTS
      61             : 
      62           0 :   NS_IMETHOD GetType(nsACString& aType) override final
      63             :   {
      64           0 :     aType = mType;
      65           0 :     return NS_OK;
      66             :   }
      67             : 
      68             :   // "notify-text-change" and "notify-selection-change"
      69           0 :   NS_IMETHOD GetOffset(uint32_t* aOffset) override final
      70             :   {
      71           0 :     if (NS_WARN_IF(!aOffset)) {
      72           0 :       return NS_ERROR_INVALID_ARG;
      73             :     }
      74           0 :     if (IsSelectionChange()) {
      75           0 :       *aOffset = mSelectionChangeData.mOffset;
      76           0 :       return NS_OK;
      77             :     }
      78           0 :     if (IsTextChange()) {
      79           0 :       *aOffset = mTextChangeData.mStartOffset;
      80           0 :       return NS_OK;
      81             :     }
      82           0 :     return NS_ERROR_NOT_AVAILABLE;
      83             :   }
      84             : 
      85             :   // "notify-selection-change"
      86           0 :   NS_IMETHOD GetText(nsAString& aText) override final
      87             :   {
      88           0 :     if (IsSelectionChange()) {
      89           0 :       aText = mSelectionChangeData.String();
      90           0 :       return NS_OK;
      91             :     }
      92           0 :     return NS_ERROR_NOT_AVAILABLE;
      93             :   }
      94             : 
      95           0 :   NS_IMETHOD GetCollapsed(bool* aCollapsed) override final
      96             :   {
      97           0 :     if (NS_WARN_IF(!aCollapsed)) {
      98           0 :       return NS_ERROR_INVALID_ARG;
      99             :     }
     100           0 :     if (IsSelectionChange()) {
     101           0 :       *aCollapsed = mSelectionChangeData.IsCollapsed();
     102           0 :       return NS_OK;
     103             :     }
     104           0 :     return NS_ERROR_NOT_AVAILABLE;
     105             :   }
     106             : 
     107           0 :   NS_IMETHOD GetLength(uint32_t* aLength) override final
     108             :   {
     109           0 :     if (NS_WARN_IF(!aLength)) {
     110           0 :       return NS_ERROR_INVALID_ARG;
     111             :     }
     112           0 :     if (IsSelectionChange()) {
     113           0 :       *aLength = mSelectionChangeData.Length();
     114           0 :       return NS_OK;
     115             :     }
     116           0 :     return NS_ERROR_NOT_AVAILABLE;
     117             :   }
     118             : 
     119           0 :   NS_IMETHOD GetReversed(bool* aReversed) override final
     120             :   {
     121           0 :     if (NS_WARN_IF(!aReversed)) {
     122           0 :       return NS_ERROR_INVALID_ARG;
     123             :     }
     124           0 :     if (IsSelectionChange()) {
     125           0 :       *aReversed = mSelectionChangeData.mReversed;
     126           0 :       return NS_OK;
     127             :     }
     128           0 :     return NS_ERROR_NOT_AVAILABLE;
     129             :   }
     130             : 
     131           0 :   NS_IMETHOD GetWritingMode(nsACString& aWritingMode) override final
     132             :   {
     133           0 :     if (IsSelectionChange()) {
     134           0 :       WritingMode writingMode = mSelectionChangeData.GetWritingMode();
     135           0 :       if (!writingMode.IsVertical()) {
     136           0 :         aWritingMode.AssignLiteral("horizontal-tb");
     137           0 :       } else if (writingMode.IsVerticalLR()) {
     138           0 :         aWritingMode.AssignLiteral("vertical-lr");
     139             :       } else {
     140           0 :         aWritingMode.AssignLiteral("vertical-rl");
     141             :       }
     142           0 :       return NS_OK;
     143             :     }
     144           0 :     return NS_ERROR_NOT_AVAILABLE;
     145             :   }
     146             : 
     147           0 :   NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) override final
     148             :   {
     149           0 :     if (NS_WARN_IF(!aCausedByComposition)) {
     150           0 :       return NS_ERROR_INVALID_ARG;
     151             :     }
     152           0 :     if (IsSelectionChange()) {
     153           0 :       *aCausedByComposition = mSelectionChangeData.mCausedByComposition;
     154           0 :       return NS_OK;
     155             :     }
     156           0 :     return NS_ERROR_NOT_AVAILABLE;
     157             :   }
     158             : 
     159           0 :   NS_IMETHOD GetCausedBySelectionEvent(
     160             :                bool* aCausedBySelectionEvent) override final
     161             :   {
     162           0 :     if (NS_WARN_IF(!aCausedBySelectionEvent)) {
     163           0 :       return NS_ERROR_INVALID_ARG;
     164             :     }
     165           0 :     if (IsSelectionChange()) {
     166           0 :       *aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent;
     167           0 :       return NS_OK;
     168             :     }
     169           0 :     return NS_ERROR_NOT_AVAILABLE;
     170             :   }
     171             : 
     172           0 :   NS_IMETHOD GetOccurredDuringComposition(
     173             :                bool* aOccurredDuringComposition) override final
     174             :   {
     175           0 :     if (NS_WARN_IF(!aOccurredDuringComposition)) {
     176           0 :       return NS_ERROR_INVALID_ARG;
     177             :     }
     178           0 :     if (IsSelectionChange()) {
     179           0 :       *aOccurredDuringComposition =
     180           0 :         mSelectionChangeData.mOccurredDuringComposition;
     181           0 :       return NS_OK;
     182             :     }
     183           0 :     return NS_ERROR_NOT_AVAILABLE;
     184             :   }
     185             : 
     186             :   // "notify-text-change"
     187           0 :   NS_IMETHOD GetRemovedLength(uint32_t* aLength) override final
     188             :   {
     189           0 :     if (NS_WARN_IF(!aLength)) {
     190           0 :       return NS_ERROR_INVALID_ARG;
     191             :     }
     192           0 :     if (IsTextChange()) {
     193           0 :       *aLength = mTextChangeData.OldLength();
     194           0 :       return NS_OK;
     195             :     }
     196           0 :     return NS_ERROR_NOT_AVAILABLE;
     197             :   }
     198             : 
     199           0 :   NS_IMETHOD GetAddedLength(uint32_t* aLength) override final
     200             :   {
     201           0 :     if (NS_WARN_IF(!aLength)) {
     202           0 :       return NS_ERROR_INVALID_ARG;
     203             :     }
     204           0 :     if (IsTextChange()) {
     205           0 :       *aLength = mTextChangeData.NewLength();
     206           0 :       return NS_OK;
     207             :     }
     208           0 :     return NS_ERROR_NOT_AVAILABLE;
     209             :   }
     210             : 
     211           0 :   NS_IMETHOD GetCausedOnlyByComposition(
     212             :                bool* aCausedOnlyByComposition) override final
     213             :   {
     214           0 :     if (NS_WARN_IF(!aCausedOnlyByComposition)) {
     215           0 :       return NS_ERROR_INVALID_ARG;
     216             :     }
     217           0 :     if (IsTextChange()) {
     218           0 :       *aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition;
     219           0 :       return NS_OK;
     220             :     }
     221           0 :     return NS_ERROR_NOT_AVAILABLE;
     222             :   }
     223             : 
     224           0 :   NS_IMETHOD GetIncludingChangesDuringComposition(
     225             :                bool* aIncludingChangesDuringComposition) override final
     226             :   {
     227           0 :     if (NS_WARN_IF(!aIncludingChangesDuringComposition)) {
     228           0 :       return NS_ERROR_INVALID_ARG;
     229             :     }
     230           0 :     if (IsTextChange()) {
     231           0 :       *aIncludingChangesDuringComposition =
     232           0 :         mTextChangeData.mIncludingChangesDuringComposition;
     233           0 :       return NS_OK;
     234             :     }
     235           0 :     return NS_ERROR_NOT_AVAILABLE;
     236             :   }
     237             : 
     238           0 :   NS_IMETHOD GetIncludingChangesWithoutComposition(
     239             :                bool* aIncludingChangesWithoutComposition) override final
     240             :   {
     241           0 :     if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) {
     242           0 :       return NS_ERROR_INVALID_ARG;
     243             :     }
     244           0 :     if (IsTextChange()) {
     245           0 :       *aIncludingChangesWithoutComposition =
     246           0 :         mTextChangeData.mIncludingChangesWithoutComposition;
     247           0 :       return NS_OK;
     248             :     }
     249           0 :     return NS_ERROR_NOT_AVAILABLE;
     250             :   }
     251             : 
     252             : protected:
     253           0 :   virtual ~TextInputProcessorNotification()
     254           0 :   {
     255           0 :     if (IsSelectionChange()) {
     256           0 :       delete mSelectionChangeData.mString;
     257           0 :       mSelectionChangeData.mString = nullptr;
     258             :     }
     259           0 :   }
     260             : 
     261           0 :   bool IsTextChange() const
     262             :   {
     263           0 :     return mType.EqualsLiteral("notify-text-change");
     264             :   }
     265             : 
     266           0 :   bool IsSelectionChange() const
     267             :   {
     268           0 :     return mType.EqualsLiteral("notify-selection-change");
     269             :   }
     270             : 
     271             : private:
     272             :   nsAutoCString mType;
     273             :   union
     274             :   {
     275             :     TextChangeDataBase mTextChangeData;
     276             :     SelectionChangeDataBase mSelectionChangeData;
     277             :   };
     278             : 
     279             :   TextInputProcessorNotification() { }
     280             : };
     281             : 
     282           0 : NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
     283             :                   nsITextInputProcessorNotification)
     284             : 
     285             : /******************************************************************************
     286             :  * TextInputProcessor
     287             :  ******************************************************************************/
     288             : 
     289           0 : NS_IMPL_ISUPPORTS(TextInputProcessor,
     290             :                   nsITextInputProcessor,
     291             :                   TextEventDispatcherListener,
     292             :                   nsISupportsWeakReference)
     293             : 
     294           0 : TextInputProcessor::TextInputProcessor()
     295             :   : mDispatcher(nullptr)
     296           0 :   , mForTests(false)
     297             : {
     298           0 : }
     299             : 
     300           0 : TextInputProcessor::~TextInputProcessor()
     301             : {
     302           0 :   if (mDispatcher && mDispatcher->IsComposing()) {
     303             :     // If this is composing and not canceling the composition, nobody can steal
     304             :     // the rights of TextEventDispatcher from this instance.  Therefore, this
     305             :     // needs to cancel the composition here.
     306           0 :     if (NS_SUCCEEDED(IsValidStateForComposition())) {
     307           0 :       RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     308           0 :       nsEventStatus status = nsEventStatus_eIgnore;
     309           0 :       kungFuDeathGrip->CommitComposition(status, &EmptyString());
     310             :     }
     311             :   }
     312           0 : }
     313             : 
     314             : bool
     315           0 : TextInputProcessor::IsComposing() const
     316             : {
     317           0 :   return mDispatcher && mDispatcher->IsComposing();
     318             : }
     319             : 
     320             : NS_IMETHODIMP
     321           0 : TextInputProcessor::GetHasComposition(bool* aHasComposition)
     322             : {
     323           0 :   MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
     324           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     325           0 :   *aHasComposition = IsComposing();
     326           0 :   return NS_OK;
     327             : }
     328             : 
     329             : NS_IMETHODIMP
     330           0 : TextInputProcessor::BeginInputTransaction(
     331             :                       mozIDOMWindow* aWindow,
     332             :                       nsITextInputProcessorCallback* aCallback,
     333             :                       bool* aSucceeded)
     334             : {
     335           0 :   MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
     336           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     337           0 :   if (NS_WARN_IF(!aCallback)) {
     338           0 :     *aSucceeded = false;
     339           0 :     return NS_ERROR_INVALID_ARG;
     340             :   }
     341           0 :   return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
     342             : }
     343             : 
     344             : NS_IMETHODIMP
     345           0 : TextInputProcessor::BeginInputTransactionForTests(
     346             :                       mozIDOMWindow* aWindow,
     347             :                       nsITextInputProcessorCallback* aCallback,
     348             :                       uint8_t aOptionalArgc,
     349             :                       bool* aSucceeded)
     350             : {
     351           0 :   MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
     352           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     353             :   nsITextInputProcessorCallback* callback =
     354           0 :     aOptionalArgc >= 1 ? aCallback : nullptr;
     355           0 :   return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded);
     356             : }
     357             : 
     358             : nsresult
     359           0 : TextInputProcessor::BeginInputTransactionInternal(
     360             :                       mozIDOMWindow* aWindow,
     361             :                       nsITextInputProcessorCallback* aCallback,
     362             :                       bool aForTests,
     363             :                       bool& aSucceeded)
     364             : {
     365           0 :   aSucceeded = false;
     366           0 :   if (NS_WARN_IF(!aWindow)) {
     367           0 :     return NS_ERROR_INVALID_ARG;
     368             :   }
     369           0 :   nsCOMPtr<nsPIDOMWindowInner> pWindow = nsPIDOMWindowInner::From(aWindow);
     370           0 :   if (NS_WARN_IF(!pWindow)) {
     371           0 :     return NS_ERROR_INVALID_ARG;
     372             :   }
     373           0 :   nsCOMPtr<nsIDocShell> docShell(pWindow->GetDocShell());
     374           0 :   if (NS_WARN_IF(!docShell)) {
     375           0 :     return NS_ERROR_FAILURE;
     376             :   }
     377           0 :   RefPtr<nsPresContext> presContext;
     378           0 :   nsresult rv = docShell->GetPresContext(getter_AddRefs(presContext));
     379           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     380           0 :     return rv;
     381             :   }
     382           0 :   if (NS_WARN_IF(!presContext)) {
     383           0 :     return NS_ERROR_FAILURE;
     384             :   }
     385           0 :   nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
     386           0 :   if (NS_WARN_IF(!widget)) {
     387           0 :     return NS_ERROR_FAILURE;
     388             :   }
     389             : 
     390           0 :   RefPtr<TextEventDispatcher> dispatcher = widget->GetTextEventDispatcher();
     391           0 :   MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null");
     392             : 
     393             :   // If the instance was initialized and is being initialized for same
     394             :   // dispatcher and same purpose, we don't need to initialize the dispatcher
     395             :   // again.
     396           0 :   if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
     397           0 :       aForTests == mForTests) {
     398           0 :     aSucceeded = true;
     399           0 :     return NS_OK;
     400             :   }
     401             : 
     402             :   // If this instance is composing or dispatching an event, don't allow to
     403             :   // initialize again.  Especially, if we allow to begin input transaction with
     404             :   // another TextEventDispatcher during dispatching an event, it may cause that
     405             :   // nobody cannot begin input transaction with it if the last event causes
     406             :   // opening modal dialog.
     407           0 :   if (mDispatcher &&
     408           0 :       (mDispatcher->IsComposing() || mDispatcher->IsDispatchingEvent())) {
     409           0 :     return NS_ERROR_ALREADY_INITIALIZED;
     410             :   }
     411             : 
     412             :   // And also if another instance is composing with the new dispatcher or
     413             :   // dispatching an event, it'll fail to steal its ownership.  Then, we should
     414             :   // not throw an exception, just return false.
     415           0 :   if (dispatcher->IsComposing() || dispatcher->IsDispatchingEvent()) {
     416           0 :     return NS_OK;
     417             :   }
     418             : 
     419             :   // This instance has finished preparing to link to the dispatcher.  Therefore,
     420             :   // let's forget the old dispatcher and purpose.
     421           0 :   if (mDispatcher) {
     422           0 :     mDispatcher->EndInputTransaction(this);
     423           0 :     if (NS_WARN_IF(mDispatcher)) {
     424             :       // Forcibly initialize the members if we failed to end the input
     425             :       // transaction.
     426           0 :       UnlinkFromTextEventDispatcher();
     427             :     }
     428             :   }
     429             : 
     430           0 :   if (aForTests) {
     431           0 :     bool isAPZAware = gfxPrefs::TestEventsAsyncEnabled();
     432           0 :     rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
     433             :   } else {
     434           0 :     rv = dispatcher->BeginInputTransaction(this);
     435             :   }
     436             : 
     437           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     438           0 :     return rv;
     439             :   }
     440             : 
     441           0 :   mDispatcher = dispatcher;
     442           0 :   mCallback = aCallback;
     443           0 :   mForTests = aForTests;
     444           0 :   aSucceeded = true;
     445           0 :   return NS_OK;
     446             : }
     447             : 
     448             : void
     449           0 : TextInputProcessor::UnlinkFromTextEventDispatcher()
     450             : {
     451           0 :   mDispatcher = nullptr;
     452           0 :   mForTests = false;
     453           0 :   if (mCallback) {
     454           0 :     nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
     455           0 :     mCallback = nullptr;
     456             : 
     457             :     RefPtr<TextInputProcessorNotification> notification =
     458           0 :       new TextInputProcessorNotification("notify-end-input-transaction");
     459           0 :     bool result = false;
     460           0 :     callback->OnNotify(this, notification, &result);
     461             :   }
     462           0 : }
     463             : 
     464             : nsresult
     465           0 : TextInputProcessor::IsValidStateForComposition()
     466             : {
     467           0 :   if (NS_WARN_IF(!mDispatcher)) {
     468           0 :     return NS_ERROR_NOT_INITIALIZED;
     469             :   }
     470             : 
     471           0 :   nsresult rv = mDispatcher->GetState();
     472           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     473           0 :     return rv;
     474             :   }
     475             : 
     476           0 :   return NS_OK;
     477             : }
     478             : 
     479             : bool
     480           0 : TextInputProcessor::IsValidEventTypeForComposition(
     481             :                       const WidgetKeyboardEvent& aKeyboardEvent) const
     482             : {
     483             :   // The key event type of composition methods must be "" or "keydown".
     484           0 :   if (aKeyboardEvent.mMessage == eKeyDown) {
     485           0 :     return true;
     486             :   }
     487           0 :   if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
     488           0 :       aKeyboardEvent.mSpecifiedEventType &&
     489           0 :       nsDependentAtomString(
     490           0 :         aKeyboardEvent.mSpecifiedEventType).EqualsLiteral("on")) {
     491           0 :     return true;
     492             :   }
     493           0 :   return false;
     494             : }
     495             : 
     496             : TextInputProcessor::EventDispatcherResult
     497           0 : TextInputProcessor::MaybeDispatchKeydownForComposition(
     498             :                       const WidgetKeyboardEvent* aKeyboardEvent,
     499             :                       uint32_t aKeyFlags)
     500             : {
     501           0 :   EventDispatcherResult result;
     502             : 
     503           0 :   result.mResult = IsValidStateForComposition();
     504           0 :   if (NS_WARN_IF(NS_FAILED(result.mResult))) {
     505           0 :     result.mCanContinue = false;
     506           0 :     return result;
     507             :   }
     508             : 
     509           0 :   if (!aKeyboardEvent) {
     510           0 :     return result;
     511             :   }
     512             : 
     513             :   // Modifier keys are not allowed because managing modifier state in this
     514             :   // method makes this messy.
     515           0 :   if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
     516           0 :     result.mResult = NS_ERROR_INVALID_ARG;
     517           0 :     result.mCanContinue = false;
     518           0 :     return result;
     519             :   }
     520             : 
     521           0 :   uint32_t consumedFlags = 0;
     522             : 
     523           0 :   result.mResult = KeydownInternal(*aKeyboardEvent, aKeyFlags, false,
     524             :                                    consumedFlags);
     525           0 :   result.mDoDefault = !consumedFlags;
     526           0 :   if (NS_WARN_IF(NS_FAILED(result.mResult))) {
     527           0 :     result.mCanContinue = false;
     528           0 :     return result;
     529             :   }
     530             : 
     531           0 :   result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
     532           0 :   return result;
     533             : }
     534             : 
     535             : TextInputProcessor::EventDispatcherResult
     536           0 : TextInputProcessor::MaybeDispatchKeyupForComposition(
     537             :                       const WidgetKeyboardEvent* aKeyboardEvent,
     538             :                       uint32_t aKeyFlags)
     539             : {
     540           0 :   EventDispatcherResult result;
     541             : 
     542           0 :   if (!aKeyboardEvent) {
     543           0 :     return result;
     544             :   }
     545             : 
     546             :   // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
     547             :   // keyup event.
     548           0 :   if (aKeyboardEvent->mMessage == eKeyDown) {
     549           0 :     return result;
     550             :   }
     551             : 
     552             :   // If the widget has been destroyed, we can do nothing here.
     553           0 :   result.mResult = IsValidStateForComposition();
     554           0 :   if (NS_FAILED(result.mResult)) {
     555           0 :     result.mCanContinue = false;
     556           0 :     return result;
     557             :   }
     558             : 
     559           0 :   result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
     560           0 :   if (NS_WARN_IF(NS_FAILED(result.mResult))) {
     561           0 :     result.mCanContinue = false;
     562           0 :     return result;
     563             :   }
     564             : 
     565           0 :   result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
     566           0 :   return result;
     567             : }
     568             : 
     569             : nsresult
     570           0 : TextInputProcessor::PrepareKeyboardEventForComposition(
     571             :                       nsIDOMKeyEvent* aDOMKeyEvent,
     572             :                       uint32_t& aKeyFlags,
     573             :                       uint8_t aOptionalArgc,
     574             :                       WidgetKeyboardEvent*& aKeyboardEvent)
     575             : {
     576           0 :   aKeyboardEvent = nullptr;
     577             : 
     578           0 :   aKeyboardEvent =
     579           0 :     aOptionalArgc && aDOMKeyEvent ?
     580           0 :       aDOMKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent() : nullptr;
     581           0 :   if (!aKeyboardEvent || aOptionalArgc < 2) {
     582           0 :     aKeyFlags = 0;
     583             :   }
     584             : 
     585           0 :   if (!aKeyboardEvent) {
     586           0 :     return NS_OK;
     587             :   }
     588             : 
     589           0 :   if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
     590           0 :     return NS_ERROR_INVALID_ARG;
     591             :   }
     592             : 
     593           0 :   return NS_OK;
     594             : }
     595             : 
     596             : NS_IMETHODIMP
     597           0 : TextInputProcessor::StartComposition(nsIDOMKeyEvent* aDOMKeyEvent,
     598             :                                      uint32_t aKeyFlags,
     599             :                                      uint8_t aOptionalArgc,
     600             :                                      bool* aSucceeded)
     601             : {
     602           0 :   MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
     603           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     604           0 :   *aSucceeded = false;
     605             : 
     606           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     607             : 
     608             :   WidgetKeyboardEvent* keyboardEvent;
     609             :   nsresult rv =
     610           0 :     PrepareKeyboardEventForComposition(aDOMKeyEvent, aKeyFlags, aOptionalArgc,
     611           0 :                                        keyboardEvent);
     612           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     613           0 :     return rv;
     614             :   }
     615             : 
     616             :   EventDispatcherResult dispatcherResult =
     617           0 :     MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
     618           0 :   if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
     619           0 :       !dispatcherResult.mCanContinue) {
     620           0 :     return dispatcherResult.mResult;
     621             :   }
     622             : 
     623           0 :   if (dispatcherResult.mDoDefault) {
     624           0 :     nsEventStatus status = nsEventStatus_eIgnore;
     625           0 :     rv = kungFuDeathGrip->StartComposition(status);
     626           0 :     *aSucceeded = status != nsEventStatus_eConsumeNoDefault &&
     627           0 :                     kungFuDeathGrip && kungFuDeathGrip->IsComposing();
     628             :   }
     629             : 
     630           0 :   MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
     631             : 
     632           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     633           0 :     return rv;
     634             :   }
     635           0 :   return NS_OK;
     636             : }
     637             : 
     638             : NS_IMETHODIMP
     639           0 : TextInputProcessor::SetPendingCompositionString(const nsAString& aString)
     640             : {
     641           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     642           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     643           0 :   nsresult rv = IsValidStateForComposition();
     644           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     645           0 :     return rv;
     646             :   }
     647           0 :   return kungFuDeathGrip->SetPendingCompositionString(aString);
     648             : }
     649             : 
     650             : NS_IMETHODIMP
     651           0 : TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength,
     652             :                                                      uint32_t aAttribute)
     653             : {
     654           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     655           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     656             :   TextRangeType textRangeType;
     657           0 :   switch (aAttribute) {
     658             :     case ATTR_RAW_CLAUSE:
     659             :     case ATTR_SELECTED_RAW_CLAUSE:
     660             :     case ATTR_CONVERTED_CLAUSE:
     661             :     case ATTR_SELECTED_CLAUSE:
     662           0 :       textRangeType = ToTextRangeType(aAttribute);
     663           0 :       break;
     664             :     default:
     665           0 :       return NS_ERROR_INVALID_ARG;
     666             :   }
     667           0 :   nsresult rv = IsValidStateForComposition();
     668           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     669           0 :     return rv;
     670             :   }
     671           0 :   return kungFuDeathGrip->AppendClauseToPendingComposition(aLength, textRangeType);
     672             : }
     673             : 
     674             : NS_IMETHODIMP
     675           0 : TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset)
     676             : {
     677           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     678           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     679           0 :   nsresult rv = IsValidStateForComposition();
     680           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     681           0 :     return rv;
     682             :   }
     683           0 :   return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
     684             : }
     685             : 
     686             : NS_IMETHODIMP
     687           0 : TextInputProcessor::FlushPendingComposition(nsIDOMKeyEvent* aDOMKeyEvent,
     688             :                                             uint32_t aKeyFlags,
     689             :                                             uint8_t aOptionalArgc,
     690             :                                             bool* aSucceeded)
     691             : {
     692           0 :   MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
     693           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     694             : 
     695             :   // Even if this doesn't flush pending composition actually, we need to reset
     696             :   // pending composition for starting next composition with new user input.
     697           0 :   AutoPendingCompositionResetter resetter(this);
     698             : 
     699           0 :   *aSucceeded = false;
     700           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     701           0 :   bool wasComposing = IsComposing();
     702             : 
     703             :   WidgetKeyboardEvent* keyboardEvent;
     704             :   nsresult rv =
     705           0 :     PrepareKeyboardEventForComposition(aDOMKeyEvent, aKeyFlags, aOptionalArgc,
     706           0 :                                        keyboardEvent);
     707           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     708           0 :     return rv;
     709             :   }
     710             : 
     711             :   EventDispatcherResult dispatcherResult =
     712           0 :     MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
     713           0 :   if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
     714           0 :       !dispatcherResult.mCanContinue) {
     715           0 :     return dispatcherResult.mResult;
     716             :   }
     717             : 
     718             :   // Even if the preceding keydown event was consumed, if the composition
     719             :   // was already started, we shouldn't prevent the change of composition.
     720           0 :   if (dispatcherResult.mDoDefault || wasComposing) {
     721             :     // Preceding keydown event may cause destroying the widget.
     722           0 :     if (NS_FAILED(IsValidStateForComposition())) {
     723           0 :       return NS_OK;
     724             :     }
     725           0 :     nsEventStatus status = nsEventStatus_eIgnore;
     726           0 :     rv = kungFuDeathGrip->FlushPendingComposition(status);
     727           0 :     *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
     728             :   }
     729             : 
     730           0 :   MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
     731             : 
     732           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     733           0 :     return rv;
     734             :   }
     735           0 :   return NS_OK;
     736             : }
     737             : 
     738             : NS_IMETHODIMP
     739           0 : TextInputProcessor::CommitComposition(nsIDOMKeyEvent* aDOMKeyEvent,
     740             :                                       uint32_t aKeyFlags,
     741             :                                       uint8_t aOptionalArgc)
     742             : {
     743           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     744             : 
     745             :   WidgetKeyboardEvent* keyboardEvent;
     746             :   nsresult rv =
     747           0 :     PrepareKeyboardEventForComposition(aDOMKeyEvent, aKeyFlags, aOptionalArgc,
     748           0 :                                        keyboardEvent);
     749           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     750           0 :     return rv;
     751             :   }
     752             : 
     753           0 :   return CommitCompositionInternal(keyboardEvent, aKeyFlags);
     754             : }
     755             : 
     756             : NS_IMETHODIMP
     757           0 : TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
     758             :                                           nsIDOMKeyEvent* aDOMKeyEvent,
     759             :                                           uint32_t aKeyFlags,
     760             :                                           uint8_t aOptionalArgc,
     761             :                                           bool* aSucceeded)
     762             : {
     763           0 :   MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
     764           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     765             : 
     766             :   WidgetKeyboardEvent* keyboardEvent;
     767             :   nsresult rv =
     768           0 :     PrepareKeyboardEventForComposition(aDOMKeyEvent, aKeyFlags, aOptionalArgc,
     769           0 :                                        keyboardEvent);
     770           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     771           0 :     return rv;
     772             :   }
     773             : 
     774           0 :   return CommitCompositionInternal(keyboardEvent, aKeyFlags,
     775           0 :                                    &aCommitString, aSucceeded);
     776             : }
     777             : 
     778             : nsresult
     779           0 : TextInputProcessor::CommitCompositionInternal(
     780             :                       const WidgetKeyboardEvent* aKeyboardEvent,
     781             :                       uint32_t aKeyFlags,
     782             :                       const nsAString* aCommitString,
     783             :                       bool* aSucceeded)
     784             : {
     785           0 :   if (aSucceeded) {
     786           0 :     *aSucceeded = false;
     787             :   }
     788           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     789           0 :   bool wasComposing = IsComposing();
     790             : 
     791             :   EventDispatcherResult dispatcherResult =
     792           0 :     MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
     793           0 :   if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
     794           0 :       !dispatcherResult.mCanContinue) {
     795           0 :     return dispatcherResult.mResult;
     796             :   }
     797             : 
     798             :   // Even if the preceding keydown event was consumed, if the composition
     799             :   // was already started, we shouldn't prevent the commit of composition.
     800           0 :   nsresult rv = NS_OK;
     801           0 :   if (dispatcherResult.mDoDefault || wasComposing) {
     802             :     // Preceding keydown event may cause destroying the widget.
     803           0 :     if (NS_FAILED(IsValidStateForComposition())) {
     804           0 :       return NS_OK;
     805             :     }
     806           0 :     nsEventStatus status = nsEventStatus_eIgnore;
     807           0 :     rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
     808           0 :     if (aSucceeded) {
     809           0 :       *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
     810             :     }
     811             :   }
     812             : 
     813           0 :   MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
     814             : 
     815           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     816           0 :     return rv;
     817             :   }
     818           0 :   return NS_OK;
     819             : }
     820             : 
     821             : NS_IMETHODIMP
     822           0 : TextInputProcessor::CancelComposition(nsIDOMKeyEvent* aDOMKeyEvent,
     823             :                                       uint32_t aKeyFlags,
     824             :                                       uint8_t aOptionalArgc)
     825             : {
     826           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
     827             : 
     828             :   WidgetKeyboardEvent* keyboardEvent;
     829             :   nsresult rv =
     830           0 :     PrepareKeyboardEventForComposition(aDOMKeyEvent, aKeyFlags, aOptionalArgc,
     831           0 :                                        keyboardEvent);
     832           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     833           0 :     return rv;
     834             :   }
     835             : 
     836           0 :   return CancelCompositionInternal(keyboardEvent, aKeyFlags);
     837             : }
     838             : 
     839             : nsresult
     840           0 : TextInputProcessor::CancelCompositionInternal(
     841             :                       const WidgetKeyboardEvent* aKeyboardEvent,
     842             :                       uint32_t aKeyFlags)
     843             : {
     844           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
     845             : 
     846             :   EventDispatcherResult dispatcherResult =
     847           0 :     MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
     848           0 :   if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
     849           0 :       !dispatcherResult.mCanContinue) {
     850           0 :     return dispatcherResult.mResult;
     851             :   }
     852             : 
     853           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     854           0 :   nsresult rv = kungFuDeathGrip->CommitComposition(status, &EmptyString());
     855             : 
     856           0 :   MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
     857             : 
     858           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     859           0 :     return rv;
     860             :   }
     861           0 :   return NS_OK;
     862             : }
     863             : 
     864             : NS_IMETHODIMP
     865           0 : TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
     866             :                               const IMENotification& aNotification)
     867             : {
     868             :   // If This is called while this is being initialized, ignore the call.
     869             :   // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
     870             :   // we can say, TextInputProcessor doesn't implement any handlers of the
     871             :   // requests and notifications.
     872           0 :   if (!mDispatcher) {
     873           0 :     return NS_ERROR_NOT_IMPLEMENTED;
     874             :   }
     875           0 :   MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
     876             :              "Wrong TextEventDispatcher notifies this");
     877           0 :   NS_ASSERTION(mForTests || mCallback,
     878             :                "mCallback can be null only when IME is initialized for tests");
     879           0 :   if (mCallback) {
     880           0 :     RefPtr<TextInputProcessorNotification> notification;
     881           0 :     switch (aNotification.mMessage) {
     882             :       case REQUEST_TO_COMMIT_COMPOSITION: {
     883           0 :         NS_ASSERTION(aTextEventDispatcher->IsComposing(),
     884             :                      "Why is this requested without composition?");
     885           0 :         notification = new TextInputProcessorNotification("request-to-commit");
     886           0 :         break;
     887             :       }
     888             :       case REQUEST_TO_CANCEL_COMPOSITION: {
     889           0 :         NS_ASSERTION(aTextEventDispatcher->IsComposing(),
     890             :                      "Why is this requested without composition?");
     891           0 :         notification = new TextInputProcessorNotification("request-to-cancel");
     892           0 :         break;
     893             :       }
     894             :       case NOTIFY_IME_OF_FOCUS:
     895           0 :         notification = new TextInputProcessorNotification("notify-focus");
     896           0 :         break;
     897             :       case NOTIFY_IME_OF_BLUR:
     898           0 :         notification = new TextInputProcessorNotification("notify-blur");
     899           0 :         break;
     900             :       case NOTIFY_IME_OF_TEXT_CHANGE:
     901             :         notification = new TextInputProcessorNotification(
     902           0 :                              aNotification.mTextChangeData);
     903           0 :         break;
     904             :       case NOTIFY_IME_OF_SELECTION_CHANGE:
     905             :         notification = new TextInputProcessorNotification(
     906           0 :                              aNotification.mSelectionChangeData);
     907           0 :         break;
     908             :       case NOTIFY_IME_OF_POSITION_CHANGE:
     909             :         notification = new TextInputProcessorNotification(
     910           0 :                              "notify-position-change");
     911           0 :         break;
     912             :       default:
     913           0 :         return NS_ERROR_NOT_IMPLEMENTED;
     914             :     }
     915           0 :     MOZ_RELEASE_ASSERT(notification);
     916           0 :     bool result = false;
     917           0 :     nsresult rv = mCallback->OnNotify(this, notification, &result);
     918           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     919           0 :       return rv;
     920             :     }
     921           0 :     return result ? NS_OK : NS_ERROR_FAILURE;
     922             :   }
     923             : 
     924           0 :   switch (aNotification.mMessage) {
     925             :     case REQUEST_TO_COMMIT_COMPOSITION: {
     926           0 :       NS_ASSERTION(aTextEventDispatcher->IsComposing(),
     927             :                    "Why is this requested without composition?");
     928           0 :       CommitCompositionInternal();
     929           0 :       return NS_OK;
     930             :     }
     931             :     case REQUEST_TO_CANCEL_COMPOSITION: {
     932           0 :       NS_ASSERTION(aTextEventDispatcher->IsComposing(),
     933             :                    "Why is this requested without composition?");
     934           0 :       CancelCompositionInternal();
     935           0 :       return NS_OK;
     936             :     }
     937             :     default:
     938           0 :       return NS_ERROR_NOT_IMPLEMENTED;
     939             :   }
     940             : }
     941             : 
     942             : NS_IMETHODIMP_(IMENotificationRequests)
     943           0 : TextInputProcessor::GetIMENotificationRequests()
     944             : {
     945             :   // TextInputProcessor should support all change notifications.
     946           0 :   return IMENotificationRequests(
     947             :            IMENotificationRequests::NOTIFY_TEXT_CHANGE |
     948           0 :            IMENotificationRequests::NOTIFY_POSITION_CHANGE);
     949             : }
     950             : 
     951             : NS_IMETHODIMP_(void)
     952           0 : TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
     953             : {
     954             :   // If This is called while this is being initialized, ignore the call.
     955           0 :   if (!mDispatcher) {
     956           0 :     return;
     957             :   }
     958           0 :   MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
     959             :              "Wrong TextEventDispatcher notifies this");
     960           0 :   UnlinkFromTextEventDispatcher();
     961             : }
     962             : 
     963             : NS_IMETHODIMP_(void)
     964           0 : TextInputProcessor::WillDispatchKeyboardEvent(
     965             :                       TextEventDispatcher* aTextEventDispatcher,
     966             :                       WidgetKeyboardEvent& aKeyboardEvent,
     967             :                       uint32_t aIndexOfKeypress,
     968             :                       void* aData)
     969             : {
     970             :   // TextInputProcessor doesn't set alternative char code nor modify charCode
     971             :   // even when Ctrl key is pressed.
     972           0 : }
     973             : 
     974             : nsresult
     975           0 : TextInputProcessor::PrepareKeyboardEventToDispatch(
     976             :                       WidgetKeyboardEvent& aKeyboardEvent,
     977             :                       uint32_t aKeyFlags)
     978             : {
     979           0 :   if (NS_WARN_IF(aKeyboardEvent.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
     980           0 :     return NS_ERROR_INVALID_ARG;
     981             :   }
     982           0 :   if ((aKeyFlags & KEY_NON_PRINTABLE_KEY) &&
     983           0 :       NS_WARN_IF(aKeyboardEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING)) {
     984           0 :     return NS_ERROR_INVALID_ARG;
     985             :   }
     986           0 :   if ((aKeyFlags & KEY_FORCE_PRINTABLE_KEY) &&
     987           0 :       aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
     988           0 :     aKeyboardEvent.GetDOMKeyName(aKeyboardEvent.mKeyValue);
     989           0 :     aKeyboardEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
     990             :   }
     991           0 :   if (aKeyFlags & KEY_KEEP_KEY_LOCATION_STANDARD) {
     992             :     // If .location is initialized with specific value, using
     993             :     // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
     994             :     // Let's throw an exception for notifying the developer of this bug.
     995           0 :     if (NS_WARN_IF(aKeyboardEvent.mLocation)) {
     996           0 :       return NS_ERROR_INVALID_ARG;
     997             :     }
     998           0 :   } else if (!aKeyboardEvent.mLocation) {
     999             :     // If KeyboardEvent.mLocation is 0, it may be uninitialized.  If so, we
    1000             :     // should compute proper mLocation value from its .code value.
    1001           0 :     aKeyboardEvent.mLocation =
    1002           0 :       WidgetKeyboardEvent::ComputeLocationFromCodeValue(
    1003           0 :         aKeyboardEvent.mCodeNameIndex);
    1004             :   }
    1005             : 
    1006           0 :   if (aKeyFlags & KEY_KEEP_KEYCODE_ZERO) {
    1007             :     // If .keyCode is initialized with specific value, using
    1008             :     // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller.  Let's throw an
    1009             :     // exception for notifying the developer of such bug.
    1010           0 :     if (NS_WARN_IF(aKeyboardEvent.mKeyCode)) {
    1011           0 :       return NS_ERROR_INVALID_ARG;
    1012             :     }
    1013           0 :   } else if (!aKeyboardEvent.mKeyCode &&
    1014           0 :              aKeyboardEvent.mKeyNameIndex > KEY_NAME_INDEX_Unidentified &&
    1015           0 :              aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) {
    1016             :     // If KeyboardEvent.keyCode is 0, it may be uninitialized.  If so, we may
    1017             :     // be able to decide a good .keyCode value if the .key value is a
    1018             :     // non-printable key.
    1019           0 :     aKeyboardEvent.mKeyCode =
    1020           0 :       WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
    1021           0 :         aKeyboardEvent.mKeyNameIndex);
    1022             :   }
    1023             : 
    1024           0 :   aKeyboardEvent.mIsSynthesizedByTIP = (mForTests)? false : true;
    1025             : 
    1026           0 :   return NS_OK;
    1027             : }
    1028             : 
    1029             : NS_IMETHODIMP
    1030           0 : TextInputProcessor::Keydown(nsIDOMKeyEvent* aDOMKeyEvent,
    1031             :                             uint32_t aKeyFlags,
    1032             :                             uint8_t aOptionalArgc,
    1033             :                             uint32_t* aConsumedFlags)
    1034             : {
    1035           0 :   MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
    1036           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    1037           0 :   if (!aOptionalArgc) {
    1038           0 :     aKeyFlags = 0;
    1039             :   }
    1040           0 :   if (NS_WARN_IF(!aDOMKeyEvent)) {
    1041           0 :     return NS_ERROR_INVALID_ARG;
    1042             :   }
    1043             :   WidgetKeyboardEvent* originalKeyEvent =
    1044           0 :     aDOMKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
    1045           0 :   if (NS_WARN_IF(!originalKeyEvent)) {
    1046           0 :     return NS_ERROR_INVALID_ARG;
    1047             :   }
    1048           0 :   return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
    1049             : }
    1050             : 
    1051             : nsresult
    1052           0 : TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
    1053             :                                     uint32_t aKeyFlags,
    1054             :                                     bool aAllowToDispatchKeypress,
    1055             :                                     uint32_t& aConsumedFlags)
    1056             : {
    1057           0 :   aConsumedFlags = KEYEVENT_NOT_CONSUMED;
    1058             : 
    1059             :   // We shouldn't modify the internal WidgetKeyboardEvent.
    1060           0 :   WidgetKeyboardEvent keyEvent(aKeyboardEvent);
    1061           0 :   nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
    1062           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1063           0 :     return rv;
    1064             :   }
    1065             : 
    1066           0 :   aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED :
    1067             :                                                          KEYEVENT_NOT_CONSUMED;
    1068             : 
    1069           0 :   if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
    1070           0 :     ModifierKeyData modifierKeyData(keyEvent);
    1071           0 :     if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
    1072             :       // If the modifier key is lockable modifier key such as CapsLock,
    1073             :       // let's toggle modifier key state at keydown.
    1074           0 :       ToggleModifierKey(modifierKeyData);
    1075             :     } else {
    1076             :       // Activate modifier flag before dispatching keydown event (i.e., keydown
    1077             :       // event should indicate the releasing modifier is active.
    1078           0 :       ActivateModifierKey(modifierKeyData);
    1079             :     }
    1080           0 :     if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
    1081           0 :       return NS_OK;
    1082             :     }
    1083           0 :   } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
    1084           0 :     return NS_ERROR_INVALID_ARG;
    1085             :   }
    1086           0 :   keyEvent.mModifiers = GetActiveModifiers();
    1087             : 
    1088           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    1089           0 :   rv = IsValidStateForComposition();
    1090           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1091           0 :     return rv;
    1092             :   }
    1093             : 
    1094           0 :   nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
    1095           0 :                                           nsEventStatus_eIgnore;
    1096           0 :   if (!kungFuDeathGrip->DispatchKeyboardEvent(eKeyDown, keyEvent, status)) {
    1097             :     // If keydown event isn't dispatched, we don't need to dispatch keypress
    1098             :     // events.
    1099           0 :     return NS_OK;
    1100             :   }
    1101             : 
    1102           0 :   aConsumedFlags |=
    1103           0 :     (status == nsEventStatus_eConsumeNoDefault) ? KEYDOWN_IS_CONSUMED :
    1104           0 :                                                   KEYEVENT_NOT_CONSUMED;
    1105             : 
    1106           0 :   if (aAllowToDispatchKeypress &&
    1107           0 :       kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
    1108           0 :     aConsumedFlags |=
    1109           0 :       (status == nsEventStatus_eConsumeNoDefault) ? KEYPRESS_IS_CONSUMED :
    1110           0 :                                                     KEYEVENT_NOT_CONSUMED;
    1111             :   }
    1112             : 
    1113           0 :   return NS_OK;
    1114             : }
    1115             : 
    1116             : NS_IMETHODIMP
    1117           0 : TextInputProcessor::Keyup(nsIDOMKeyEvent* aDOMKeyEvent,
    1118             :                           uint32_t aKeyFlags,
    1119             :                           uint8_t aOptionalArgc,
    1120             :                           bool* aDoDefault)
    1121             : {
    1122           0 :   MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
    1123           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    1124           0 :   if (!aOptionalArgc) {
    1125           0 :     aKeyFlags = 0;
    1126             :   }
    1127           0 :   if (NS_WARN_IF(!aDOMKeyEvent)) {
    1128           0 :     return NS_ERROR_INVALID_ARG;
    1129             :   }
    1130             :   WidgetKeyboardEvent* originalKeyEvent =
    1131           0 :     aDOMKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
    1132           0 :   if (NS_WARN_IF(!originalKeyEvent)) {
    1133           0 :     return NS_ERROR_INVALID_ARG;
    1134             :   }
    1135           0 :   return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault);
    1136             : }
    1137             : 
    1138             : nsresult
    1139           0 : TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
    1140             :                                   uint32_t aKeyFlags,
    1141             :                                   bool& aDoDefault)
    1142             : {
    1143           0 :   aDoDefault = false;
    1144             : 
    1145             :   // We shouldn't modify the internal WidgetKeyboardEvent.
    1146           0 :   WidgetKeyboardEvent keyEvent(aKeyboardEvent);
    1147           0 :   nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
    1148           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1149           0 :     return rv;
    1150             :   }
    1151             : 
    1152           0 :   aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
    1153             : 
    1154           0 :   if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
    1155           0 :     if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
    1156             :       // Inactivate modifier flag before dispatching keyup event (i.e., keyup
    1157             :       // event shouldn't indicate the releasing modifier is active.
    1158           0 :       InactivateModifierKey(ModifierKeyData(keyEvent));
    1159             :     }
    1160           0 :     if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
    1161           0 :       return NS_OK;
    1162             :     }
    1163           0 :   } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
    1164           0 :     return NS_ERROR_INVALID_ARG;
    1165             :   }
    1166           0 :   keyEvent.mModifiers = GetActiveModifiers();
    1167             : 
    1168           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    1169           0 :   rv = IsValidStateForComposition();
    1170           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1171           0 :     return rv;
    1172             :   }
    1173             : 
    1174           0 :   nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
    1175           0 :                                       nsEventStatus_eConsumeNoDefault;
    1176           0 :   kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
    1177           0 :   aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
    1178           0 :   return NS_OK;
    1179             : }
    1180             : 
    1181             : NS_IMETHODIMP
    1182           0 : TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
    1183             :                                      bool* aActive)
    1184             : {
    1185           0 :   MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
    1186           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    1187           0 :   if (!mModifierKeyDataArray) {
    1188           0 :     *aActive = false;
    1189           0 :     return NS_OK;
    1190             :   }
    1191           0 :   Modifiers activeModifiers = mModifierKeyDataArray->GetActiveModifiers();
    1192           0 :   Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
    1193           0 :   *aActive = ((activeModifiers & modifier) != 0);
    1194           0 :   return NS_OK;
    1195             : }
    1196             : 
    1197             : NS_IMETHODIMP
    1198           0 : TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther)
    1199             : {
    1200           0 :   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    1201           0 :   if (!aOther) {
    1202           0 :     mModifierKeyDataArray = nullptr;
    1203           0 :     return NS_OK;
    1204             :   }
    1205           0 :   TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
    1206           0 :   if (!other->mModifierKeyDataArray) {
    1207           0 :     other->mModifierKeyDataArray = new ModifierKeyDataArray();
    1208             :   }
    1209           0 :   mModifierKeyDataArray = other->mModifierKeyDataArray;
    1210           0 :   return NS_OK;
    1211             : }
    1212             : 
    1213             : /******************************************************************************
    1214             :  * TextInputProcessor::AutoPendingCompositionResetter
    1215             :  ******************************************************************************/
    1216           0 : TextInputProcessor::AutoPendingCompositionResetter::
    1217           0 :   AutoPendingCompositionResetter(TextInputProcessor* aTIP)
    1218           0 :   : mTIP(aTIP)
    1219             : {
    1220           0 :   MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
    1221           0 : }
    1222             : 
    1223           0 : TextInputProcessor::AutoPendingCompositionResetter::
    1224           0 :   ~AutoPendingCompositionResetter()
    1225             : {
    1226           0 :   if (mTIP->mDispatcher) {
    1227           0 :     mTIP->mDispatcher->ClearPendingComposition();
    1228             :   }
    1229           0 : }
    1230             : 
    1231             : /******************************************************************************
    1232             :  * TextInputProcessor::ModifierKeyData
    1233             :  ******************************************************************************/
    1234           0 : TextInputProcessor::ModifierKeyData::ModifierKeyData(
    1235           0 :   const WidgetKeyboardEvent& aKeyboardEvent)
    1236           0 :   : mKeyNameIndex(aKeyboardEvent.mKeyNameIndex)
    1237           0 :   , mCodeNameIndex(aKeyboardEvent.mCodeNameIndex)
    1238             : {
    1239           0 :   mModifier = WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex);
    1240           0 :   MOZ_ASSERT(mModifier, "mKeyNameIndex must be a modifier key name");
    1241           0 : }
    1242             : 
    1243             : /******************************************************************************
    1244             :  * TextInputProcessor::ModifierKeyDataArray
    1245             :  ******************************************************************************/
    1246             : Modifiers
    1247           0 : TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const
    1248             : {
    1249           0 :   Modifiers result = MODIFIER_NONE;
    1250           0 :   for (uint32_t i = 0; i < Length(); i++) {
    1251           0 :     result |= ElementAt(i).mModifier;
    1252             :   }
    1253           0 :   return result;
    1254             : }
    1255             : 
    1256             : void
    1257           0 : TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
    1258             :   const TextInputProcessor::ModifierKeyData& aModifierKeyData)
    1259             : {
    1260           0 :   if (Contains(aModifierKeyData)) {
    1261           0 :     return;
    1262             :   }
    1263           0 :   AppendElement(aModifierKeyData);
    1264             : }
    1265             : 
    1266             : void
    1267           0 : TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
    1268             :   const TextInputProcessor::ModifierKeyData& aModifierKeyData)
    1269             : {
    1270           0 :   RemoveElement(aModifierKeyData);
    1271           0 : }
    1272             : 
    1273             : void
    1274           0 : TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
    1275             :   const TextInputProcessor::ModifierKeyData& aModifierKeyData)
    1276             : {
    1277           0 :   auto index = IndexOf(aModifierKeyData);
    1278           0 :   if (index == NoIndex) {
    1279           0 :     AppendElement(aModifierKeyData);
    1280           0 :     return;
    1281             :   }
    1282           0 :   RemoveElementAt(index);
    1283             : }
    1284             : 
    1285             : } // namespace mozilla

Generated by: LCOV version 1.13