LCOV - code coverage report
Current view: top level - widget - TextEventDispatcher.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 398 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 30 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "mozilla/Preferences.h"
       7             : #include "mozilla/TextEvents.h"
       8             : #include "mozilla/TextEventDispatcher.h"
       9             : #include "nsIDocShell.h"
      10             : #include "nsIFrame.h"
      11             : #include "nsIPresShell.h"
      12             : #include "nsIWidget.h"
      13             : #include "nsPIDOMWindow.h"
      14             : #include "nsView.h"
      15             : 
      16             : namespace mozilla {
      17             : namespace widget {
      18             : 
      19             : /******************************************************************************
      20             :  * TextEventDispatcher
      21             :  *****************************************************************************/
      22             : 
      23             : bool TextEventDispatcher::sDispatchKeyEventsDuringComposition = false;
      24             : 
      25           0 : TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
      26             :   : mWidget(aWidget)
      27             :   , mDispatchingEvent(0)
      28             :   , mInputTransactionType(eNoInputTransaction)
      29             :   , mIsComposing(false)
      30           0 :   , mHasFocus(false)
      31             : {
      32           0 :   MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
      33             : 
      34             :   static bool sInitialized = false;
      35           0 :   if (!sInitialized) {
      36             :     Preferences::AddBoolVarCache(
      37             :       &sDispatchKeyEventsDuringComposition,
      38             :       "dom.keyboardevent.dispatch_during_composition",
      39           0 :       false);
      40           0 :     sInitialized = true;
      41             :   }
      42             : 
      43           0 :   ClearNotificationRequests();
      44           0 : }
      45             : 
      46             : nsresult
      47           0 : TextEventDispatcher::BeginInputTransaction(
      48             :                        TextEventDispatcherListener* aListener)
      49             : {
      50             :   return BeginInputTransactionInternal(aListener,
      51           0 :                                        eSameProcessSyncInputTransaction);
      52             : }
      53             : 
      54             : nsresult
      55           0 : TextEventDispatcher::BeginTestInputTransaction(
      56             :                        TextEventDispatcherListener* aListener,
      57             :                        bool aIsAPZAware)
      58             : {
      59           0 :   return BeginInputTransactionInternal(aListener,
      60             :            aIsAPZAware ? eAsyncTestInputTransaction :
      61           0 :                          eSameProcessSyncTestInputTransaction);
      62             : }
      63             : 
      64             : nsresult
      65           0 : TextEventDispatcher::BeginNativeInputTransaction()
      66             : {
      67           0 :   if (NS_WARN_IF(!mWidget)) {
      68           0 :     return NS_ERROR_FAILURE;
      69             :   }
      70             :   RefPtr<TextEventDispatcherListener> listener =
      71           0 :     mWidget->GetNativeTextEventDispatcherListener();
      72           0 :   if (NS_WARN_IF(!listener)) {
      73           0 :     return NS_ERROR_FAILURE;
      74             :   }
      75           0 :   return BeginInputTransactionInternal(listener, eNativeInputTransaction);
      76             : }
      77             : 
      78             : nsresult
      79           0 : TextEventDispatcher::BeginInputTransactionInternal(
      80             :                        TextEventDispatcherListener* aListener,
      81             :                        InputTransactionType aType)
      82             : {
      83           0 :   if (NS_WARN_IF(!aListener)) {
      84           0 :     return NS_ERROR_INVALID_ARG;
      85             :   }
      86           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
      87           0 :   if (listener) {
      88           0 :     if (listener == aListener && mInputTransactionType == aType) {
      89           0 :       UpdateNotificationRequests();
      90           0 :       return NS_OK;
      91             :     }
      92             :     // If this has composition or is dispatching an event, any other listener
      93             :     // can steal ownership.  Especially, if the latter case is allowed,
      94             :     // nobody cannot begin input transaction with this if a modal dialog is
      95             :     // opened during dispatching an event.
      96           0 :     if (IsComposing() || IsDispatchingEvent()) {
      97           0 :       return NS_ERROR_ALREADY_INITIALIZED;
      98             :     }
      99             :   }
     100           0 :   mListener = do_GetWeakReference(aListener);
     101           0 :   mInputTransactionType = aType;
     102           0 :   if (listener && listener != aListener) {
     103           0 :     listener->OnRemovedFrom(this);
     104             :   }
     105           0 :   UpdateNotificationRequests();
     106           0 :   return NS_OK;
     107             : }
     108             : 
     109             : void
     110           0 : TextEventDispatcher::EndInputTransaction(TextEventDispatcherListener* aListener)
     111             : {
     112           0 :   if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
     113           0 :     return;
     114             :   }
     115             : 
     116           0 :   mInputTransactionType = eNoInputTransaction;
     117             : 
     118           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
     119           0 :   if (NS_WARN_IF(!listener)) {
     120           0 :     return;
     121             :   }
     122             : 
     123           0 :   if (NS_WARN_IF(listener != aListener)) {
     124           0 :     return;
     125             :   }
     126             : 
     127           0 :   mListener = nullptr;
     128           0 :   listener->OnRemovedFrom(this);
     129           0 :   UpdateNotificationRequests();
     130             : }
     131             : 
     132             : void
     133           0 : TextEventDispatcher::OnDestroyWidget()
     134             : {
     135           0 :   mWidget = nullptr;
     136           0 :   mHasFocus = false;
     137           0 :   ClearNotificationRequests();
     138           0 :   mPendingComposition.Clear();
     139           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
     140           0 :   mListener = nullptr;
     141           0 :   mInputTransactionType = eNoInputTransaction;
     142           0 :   if (listener) {
     143           0 :     listener->OnRemovedFrom(this);
     144             :   }
     145           0 : }
     146             : 
     147             : nsresult
     148           0 : TextEventDispatcher::GetState() const
     149             : {
     150           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
     151           0 :   if (!listener) {
     152           0 :     return NS_ERROR_NOT_INITIALIZED;
     153             :   }
     154           0 :   if (!mWidget || mWidget->Destroyed()) {
     155           0 :     return NS_ERROR_NOT_AVAILABLE;
     156             :   }
     157           0 :   return NS_OK;
     158             : }
     159             : 
     160             : void
     161           0 : TextEventDispatcher::InitEvent(WidgetGUIEvent& aEvent) const
     162             : {
     163           0 :   aEvent.mTime = PR_IntervalNow();
     164           0 :   aEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
     165           0 :   aEvent.mFlags.mIsSynthesizedForTests = IsForTests();
     166           0 :   if (aEvent.mClass != eCompositionEventClass) {
     167           0 :     return;
     168             :   }
     169           0 :   void* pseudoIMEContext = GetPseudoIMEContext();
     170           0 :   if (pseudoIMEContext) {
     171           0 :     aEvent.AsCompositionEvent()->mNativeIMEContext.
     172           0 :       InitWithRawNativeIMEContext(pseudoIMEContext);
     173             :   }
     174             : #ifdef DEBUG
     175             :   else {
     176           0 :     MOZ_ASSERT(!XRE_IsContentProcess(),
     177             :       "Why did the content process start native event transaction?");
     178           0 :     MOZ_ASSERT(aEvent.AsCompositionEvent()->mNativeIMEContext.IsValid(),
     179             :       "Native IME context shouldn't be invalid");
     180             :   }
     181             : #endif // #ifdef DEBUG
     182             : }
     183             : 
     184             : nsresult
     185           0 : TextEventDispatcher::DispatchEvent(nsIWidget* aWidget,
     186             :                                    WidgetGUIEvent& aEvent,
     187             :                                    nsEventStatus& aStatus)
     188             : {
     189           0 :   MOZ_ASSERT(!aEvent.AsInputEvent(), "Use DispatchInputEvent()");
     190             : 
     191           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
     192           0 :   nsCOMPtr<nsIWidget> widget(aWidget);
     193           0 :   mDispatchingEvent++;
     194           0 :   nsresult rv = widget->DispatchEvent(&aEvent, aStatus);
     195           0 :   mDispatchingEvent--;
     196           0 :   return rv;
     197             : }
     198             : 
     199             : nsresult
     200           0 : TextEventDispatcher::DispatchInputEvent(nsIWidget* aWidget,
     201             :                                         WidgetInputEvent& aEvent,
     202             :                                         nsEventStatus& aStatus)
     203             : {
     204           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
     205           0 :   nsCOMPtr<nsIWidget> widget(aWidget);
     206           0 :   mDispatchingEvent++;
     207             : 
     208             :   // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
     209             :   // sends the event to the parent process first since APZ needs to handle it
     210             :   // first.  However, some callers (e.g., keyboard apps on B2G and tests
     211             :   // expecting synchronous dispatch) don't want this to do that.
     212           0 :   nsresult rv = NS_OK;
     213           0 :   if (ShouldSendInputEventToAPZ()) {
     214           0 :     aStatus = widget->DispatchInputEvent(&aEvent);
     215             :   } else {
     216           0 :     rv = widget->DispatchEvent(&aEvent, aStatus);
     217             :   }
     218             : 
     219           0 :   mDispatchingEvent--;
     220           0 :   return rv;
     221             : }
     222             : 
     223             : nsresult
     224           0 : TextEventDispatcher::StartComposition(nsEventStatus& aStatus,
     225             :                                       const WidgetEventTime* aEventTime)
     226             : {
     227           0 :   aStatus = nsEventStatus_eIgnore;
     228             : 
     229           0 :   nsresult rv = GetState();
     230           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     231           0 :     return rv;
     232             :   }
     233             : 
     234           0 :   if (NS_WARN_IF(mIsComposing)) {
     235           0 :     return NS_ERROR_FAILURE;
     236             :   }
     237             : 
     238           0 :   mIsComposing = true;
     239             :   WidgetCompositionEvent compositionStartEvent(true, eCompositionStart,
     240           0 :                                                mWidget);
     241           0 :   InitEvent(compositionStartEvent);
     242           0 :   if (aEventTime) {
     243           0 :     compositionStartEvent.AssignEventTime(*aEventTime);
     244             :   }
     245           0 :   rv = DispatchEvent(mWidget, compositionStartEvent, aStatus);
     246           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     247           0 :     return rv;
     248             :   }
     249             : 
     250           0 :   return NS_OK;
     251             : }
     252             : 
     253             : nsresult
     254           0 : TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
     255             :                        nsEventStatus& aStatus,
     256             :                        const WidgetEventTime* aEventTime)
     257             : {
     258           0 :   if (IsComposing()) {
     259           0 :     return NS_OK;
     260             :   }
     261             : 
     262           0 :   nsresult rv = StartComposition(aStatus, aEventTime);
     263           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     264           0 :     return rv;
     265             :   }
     266             : 
     267             :   // If started composition has already been committed, we shouldn't dispatch
     268             :   // the compositionchange event.
     269           0 :   if (!IsComposing()) {
     270           0 :     aStatus = nsEventStatus_eConsumeNoDefault;
     271           0 :     return NS_OK;
     272             :   }
     273             : 
     274             :   // Note that the widget might be destroyed during a call of
     275             :   // StartComposition().  In such case, we shouldn't keep dispatching next
     276             :   // event.
     277           0 :   rv = GetState();
     278           0 :   if (NS_FAILED(rv)) {
     279           0 :     MOZ_ASSERT(rv != NS_ERROR_NOT_INITIALIZED,
     280             :                "aDispatcher must still be initialized in this case");
     281           0 :     aStatus = nsEventStatus_eConsumeNoDefault;
     282           0 :     return NS_OK; // Don't throw exception in this case
     283             :   }
     284             : 
     285           0 :   aStatus = nsEventStatus_eIgnore;
     286           0 :   return NS_OK;
     287             : }
     288             : 
     289             : nsresult
     290           0 : TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
     291             :                                        const nsAString* aCommitString,
     292             :                                        const WidgetEventTime* aEventTime)
     293             : {
     294           0 :   aStatus = nsEventStatus_eIgnore;
     295             : 
     296           0 :   nsresult rv = GetState();
     297           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     298           0 :     return rv;
     299             :   }
     300             : 
     301             :   // When there is no composition, caller shouldn't try to commit composition
     302             :   // with non-existing composition string nor commit composition with empty
     303             :   // string.
     304           0 :   if (NS_WARN_IF(!IsComposing() &&
     305             :                  (!aCommitString || aCommitString->IsEmpty()))) {
     306           0 :     return NS_ERROR_FAILURE;
     307             :   }
     308             : 
     309           0 :   nsCOMPtr<nsIWidget> widget(mWidget);
     310           0 :   rv = StartCompositionAutomaticallyIfNecessary(aStatus, aEventTime);
     311           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     312           0 :     return rv;
     313             :   }
     314           0 :   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     315           0 :     return NS_OK;
     316             :   }
     317             : 
     318             :   // End current composition and make this free for other IMEs.
     319           0 :   mIsComposing = false;
     320             : 
     321           0 :   EventMessage message = aCommitString ? eCompositionCommit :
     322           0 :                                          eCompositionCommitAsIs;
     323           0 :   WidgetCompositionEvent compositionCommitEvent(true, message, widget);
     324           0 :   InitEvent(compositionCommitEvent);
     325           0 :   if (aEventTime) {
     326           0 :     compositionCommitEvent.AssignEventTime(*aEventTime);
     327             :   }
     328           0 :   if (message == eCompositionCommit) {
     329           0 :     compositionCommitEvent.mData = *aCommitString;
     330             :     // Don't send CRLF nor CR, replace it with LF here.
     331           0 :     compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
     332           0 :                                                   NS_LITERAL_STRING("\n"));
     333           0 :     compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r"),
     334           0 :                                                   NS_LITERAL_STRING("\n"));
     335             :   }
     336           0 :   rv = DispatchEvent(widget, compositionCommitEvent, aStatus);
     337           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     338           0 :     return rv;
     339             :   }
     340             : 
     341           0 :   return NS_OK;
     342             : }
     343             : 
     344             : nsresult
     345           0 : TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
     346             : {
     347           0 :   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
     348             : 
     349           0 :   if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
     350           0 :     mHasFocus = false;
     351           0 :     ClearNotificationRequests();
     352             :   }
     353             : 
     354             : 
     355             :   // First, send the notification to current input transaction's listener.
     356           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
     357           0 :   if (listener) {
     358           0 :     rv = listener->NotifyIME(this, aIMENotification);
     359             :   }
     360             : 
     361           0 :   if (!mWidget) {
     362           0 :     return rv;
     363             :   }
     364             : 
     365             :   // If current input transaction isn't for native event handler, we should
     366             :   // send the notification to the native text event dispatcher listener
     367             :   // since native event handler may need to do something from
     368             :   // TextEventDispatcherListener::NotifyIME() even before there is no
     369             :   // input transaction yet.  For example, native IME handler may need to
     370             :   // create new context at receiving NOTIFY_IME_OF_FOCUS.  In this case,
     371             :   // mListener may not be initialized since input transaction should be
     372             :   // initialized immediately before dispatching every WidgetKeyboardEvent
     373             :   // and WidgetCompositionEvent (dispatching events always occurs after
     374             :   // focus move).
     375             :   nsCOMPtr<TextEventDispatcherListener> nativeListener =
     376           0 :     mWidget->GetNativeTextEventDispatcherListener();
     377           0 :   if (listener != nativeListener && nativeListener) {
     378           0 :     switch (aIMENotification.mMessage) {
     379             :       case REQUEST_TO_COMMIT_COMPOSITION:
     380             :       case REQUEST_TO_CANCEL_COMPOSITION:
     381             :         // It's not necessary to notify native IME of requests.
     382           0 :         break;
     383             :       default: {
     384             :         // Even if current input transaction's listener returns NS_OK or
     385             :         // something, we need to notify native IME of notifications because
     386             :         // when user typing after TIP does something, the changed information
     387             :         // is necessary for them.
     388             :         nsresult rv2 =
     389           0 :           nativeListener->NotifyIME(this, aIMENotification);
     390             :         // But return the result from current listener except when the
     391             :         // notification isn't handled.
     392           0 :         if (rv == NS_ERROR_NOT_IMPLEMENTED) {
     393           0 :           rv = rv2;
     394             :         }
     395           0 :         break;
     396             :       }
     397             :     }
     398             :   }
     399             : 
     400           0 :   if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
     401           0 :     mHasFocus = true;
     402           0 :     UpdateNotificationRequests();
     403             :   }
     404             : 
     405           0 :   return rv;
     406             : }
     407             : 
     408             : void
     409           0 : TextEventDispatcher::ClearNotificationRequests()
     410             : {
     411           0 :   mIMENotificationRequests = IMENotificationRequests();
     412           0 : }
     413             : 
     414             : void
     415           0 : TextEventDispatcher::UpdateNotificationRequests()
     416             : {
     417           0 :   ClearNotificationRequests();
     418             : 
     419             :   // If it doesn't has focus, no notifications are available.
     420           0 :   if (!mHasFocus || !mWidget) {
     421           0 :     return;
     422             :   }
     423             : 
     424             :   // If there is a listener, its requests are necessary.
     425           0 :   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
     426           0 :   if (listener) {
     427           0 :     mIMENotificationRequests = listener->GetIMENotificationRequests();
     428             :   }
     429             : 
     430             :   // Even if this is in non-native input transaction, native IME needs
     431             :   // requests.  So, add native IME requests too.
     432           0 :   if (!IsInNativeInputTransaction()) {
     433             :     nsCOMPtr<TextEventDispatcherListener> nativeListener =
     434           0 :       mWidget->GetNativeTextEventDispatcherListener();
     435           0 :     if (nativeListener) {
     436           0 :       mIMENotificationRequests |= nativeListener->GetIMENotificationRequests();
     437             :     }
     438             :   }
     439             : }
     440             : 
     441             : bool
     442           0 : TextEventDispatcher::DispatchKeyboardEvent(
     443             :                        EventMessage aMessage,
     444             :                        const WidgetKeyboardEvent& aKeyboardEvent,
     445             :                        nsEventStatus& aStatus,
     446             :                        void* aData)
     447             : {
     448           0 :   return DispatchKeyboardEventInternal(aMessage, aKeyboardEvent, aStatus,
     449           0 :                                        aData);
     450             : }
     451             : 
     452             : bool
     453           0 : TextEventDispatcher::DispatchKeyboardEventInternal(
     454             :                        EventMessage aMessage,
     455             :                        const WidgetKeyboardEvent& aKeyboardEvent,
     456             :                        nsEventStatus& aStatus,
     457             :                        void* aData,
     458             :                        uint32_t aIndexOfKeypress,
     459             :                        bool aNeedsCallback)
     460             : {
     461             :   // Note that this method is also used for dispatching key events on a plugin
     462             :   // because key events on a plugin should be dispatched same as normal key
     463             :   // events.  Then, only some handlers which need to intercept key events
     464             :   // before the focused plugin (e.g., reserved shortcut key handlers) can
     465             :   // consume the events.
     466           0 :   MOZ_ASSERT(WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
     467             :              WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage) ||
     468             :              aMessage == eKeyPress, "Invalid aMessage value");
     469           0 :   nsresult rv = GetState();
     470           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     471           0 :     return false;
     472             :   }
     473             : 
     474             :   // If the key shouldn't cause keypress events, don't this patch them.
     475           0 :   if (aMessage == eKeyPress && !aKeyboardEvent.ShouldCauseKeypressEvents()) {
     476           0 :     return false;
     477             :   }
     478             : 
     479             :   // Basically, key events shouldn't be dispatched during composition.
     480             :   // Note that plugin process has different IME context.  Therefore, we don't
     481             :   // need to check our composition state when the key event is fired on a
     482             :   // plugin.
     483           0 :   if (IsComposing() && !WidgetKeyboardEvent::IsKeyEventOnPlugin(aMessage)) {
     484             :     // However, if we need to behave like other browsers, we need the keydown
     485             :     // and keyup events.  Note that this behavior is also allowed by D3E spec.
     486             :     // FYI: keypress events must not be fired during composition.
     487           0 :     if (!sDispatchKeyEventsDuringComposition || aMessage == eKeyPress) {
     488           0 :       return false;
     489             :     }
     490             :     // XXX If there was mOnlyContentDispatch for this case, it might be useful
     491             :     //     because our chrome doesn't assume that key events are fired during
     492             :     //     composition.
     493             :   }
     494             : 
     495           0 :   WidgetKeyboardEvent keyEvent(true, aMessage, mWidget);
     496           0 :   InitEvent(keyEvent);
     497           0 :   keyEvent.AssignKeyEventData(aKeyboardEvent, false);
     498             : 
     499           0 :   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     500             :     // If the key event should be dispatched as consumed event, marking it here.
     501             :     // This is useful to prevent double action.  E.g., when the key was already
     502             :     // handled by system, our chrome shouldn't handle it.
     503           0 :     keyEvent.PreventDefaultBeforeDispatch();
     504             :   }
     505             : 
     506             :   // Corrects each member for the specific key event type.
     507           0 :   if (keyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
     508           0 :     MOZ_ASSERT(!aIndexOfKeypress,
     509             :       "aIndexOfKeypress must be 0 for non-printable key");
     510             :     // If the keyboard event isn't caused by printable key, its charCode should
     511             :     // be 0.
     512           0 :     keyEvent.SetCharCode(0);
     513             :   } else {
     514           0 :     if (WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
     515           0 :         WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
     516           0 :       MOZ_RELEASE_ASSERT(!aIndexOfKeypress,
     517             :         "aIndexOfKeypress must be 0 for either eKeyDown or eKeyUp");
     518             :     } else {
     519           0 :       MOZ_RELEASE_ASSERT(
     520             :         !aIndexOfKeypress || aIndexOfKeypress < keyEvent.mKeyValue.Length(),
     521             :         "aIndexOfKeypress must be 0 - mKeyValue.Length() - 1");
     522             :     }
     523             :     wchar_t ch =
     524           0 :       keyEvent.mKeyValue.IsEmpty() ? 0 : keyEvent.mKeyValue[aIndexOfKeypress];
     525           0 :     keyEvent.SetCharCode(static_cast<uint32_t>(ch));
     526           0 :     if (aMessage == eKeyPress) {
     527             :       // keyCode of eKeyPress events of printable keys should be always 0.
     528           0 :       keyEvent.mKeyCode = 0;
     529             :       // eKeyPress events are dispatched for every character.
     530             :       // So, each key value of eKeyPress events should be a character.
     531           0 :       if (ch) {
     532           0 :         keyEvent.mKeyValue.Assign(ch);
     533             :       } else {
     534           0 :         keyEvent.mKeyValue.Truncate();
     535             :       }
     536             :     }
     537             :   }
     538           0 :   if (WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
     539             :     // mIsRepeat of keyup event must be false.
     540           0 :     keyEvent.mIsRepeat = false;
     541             :   }
     542             :   // mIsComposing should be initialized later.
     543           0 :   keyEvent.mIsComposing = false;
     544           0 :   if (mInputTransactionType == eNativeInputTransaction) {
     545             :     // Copy mNativeKeyEvent here because for safety for other users of
     546             :     // AssignKeyEventData(), it doesn't copy this.
     547           0 :     keyEvent.mNativeKeyEvent = aKeyboardEvent.mNativeKeyEvent;
     548             :   } else {
     549             :     // If it's not a keyboard event for native key event, we should ensure that
     550             :     // mNativeKeyEvent and mPluginEvent are null/empty.
     551           0 :     keyEvent.mNativeKeyEvent = nullptr;
     552           0 :     keyEvent.mPluginEvent.Clear();
     553             :   }
     554             :   // TODO: Manage mUniqueId here.
     555             : 
     556             :   // Request the alternative char codes for the key event.
     557             :   // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
     558             :   // needs to check if a following keypress event is reserved by chrome for
     559             :   // stopping propagation of its preceding keydown event.
     560           0 :   keyEvent.mAlternativeCharCodes.Clear();
     561           0 :   if ((WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
     562           0 :        aMessage == eKeyPress) &&
     563           0 :       (aNeedsCallback || keyEvent.IsControl() || keyEvent.IsAlt() ||
     564           0 :        keyEvent.IsMeta() || keyEvent.IsOS())) {
     565             :     nsCOMPtr<TextEventDispatcherListener> listener =
     566           0 :       do_QueryReferent(mListener);
     567           0 :     if (listener) {
     568           0 :       DebugOnly<WidgetKeyboardEvent> original(keyEvent);
     569           0 :       listener->WillDispatchKeyboardEvent(this, keyEvent, aIndexOfKeypress,
     570           0 :                                           aData);
     571           0 :       MOZ_ASSERT(keyEvent.mMessage ==
     572             :                    static_cast<WidgetKeyboardEvent&>(original).mMessage);
     573           0 :       MOZ_ASSERT(keyEvent.mKeyCode ==
     574             :                    static_cast<WidgetKeyboardEvent&>(original).mKeyCode);
     575           0 :       MOZ_ASSERT(keyEvent.mLocation ==
     576             :                    static_cast<WidgetKeyboardEvent&>(original).mLocation);
     577           0 :       MOZ_ASSERT(keyEvent.mIsRepeat ==
     578             :                    static_cast<WidgetKeyboardEvent&>(original).mIsRepeat);
     579           0 :       MOZ_ASSERT(keyEvent.mIsComposing ==
     580             :                    static_cast<WidgetKeyboardEvent&>(original).mIsComposing);
     581           0 :       MOZ_ASSERT(keyEvent.mKeyNameIndex ==
     582             :                    static_cast<WidgetKeyboardEvent&>(original).mKeyNameIndex);
     583           0 :       MOZ_ASSERT(keyEvent.mCodeNameIndex ==
     584             :                    static_cast<WidgetKeyboardEvent&>(original).mCodeNameIndex);
     585           0 :       MOZ_ASSERT(keyEvent.mKeyValue ==
     586             :                    static_cast<WidgetKeyboardEvent&>(original).mKeyValue);
     587           0 :       MOZ_ASSERT(keyEvent.mCodeValue ==
     588             :                    static_cast<WidgetKeyboardEvent&>(original).mCodeValue);
     589             :     }
     590             :   }
     591             : 
     592           0 :   DispatchInputEvent(mWidget, keyEvent, aStatus);
     593           0 :   return true;
     594             : }
     595             : 
     596             : bool
     597           0 : TextEventDispatcher::MaybeDispatchKeypressEvents(
     598             :                        const WidgetKeyboardEvent& aKeyboardEvent,
     599             :                        nsEventStatus& aStatus,
     600             :                        void* aData,
     601             :                        bool aNeedsCallback)
     602             : {
     603             :   // If the key event was consumed, keypress event shouldn't be fired.
     604           0 :   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     605           0 :     return false;
     606             :   }
     607             : 
     608             :   // If the key shouldn't cause keypress events, don't fire them.
     609           0 :   if (!aKeyboardEvent.ShouldCauseKeypressEvents()) {
     610           0 :     return false;
     611             :   }
     612             : 
     613             :   // If the key isn't a printable key or just inputting one character or
     614             :   // no character, we should dispatch only one keypress.  Otherwise, i.e.,
     615             :   // if the key is a printable key and inputs multiple characters, keypress
     616             :   // event should be dispatched the count of inputting characters times.
     617             :   size_t keypressCount =
     618           0 :     aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
     619           0 :       1 : std::max(static_cast<nsAString::size_type>(1),
     620           0 :                    aKeyboardEvent.mKeyValue.Length());
     621           0 :   bool isDispatched = false;
     622           0 :   bool consumed = false;
     623           0 :   for (size_t i = 0; i < keypressCount; i++) {
     624           0 :     aStatus = nsEventStatus_eIgnore;
     625           0 :     if (!DispatchKeyboardEventInternal(eKeyPress, aKeyboardEvent,
     626             :                                        aStatus, aData, i, aNeedsCallback)) {
     627             :       // The widget must have been gone.
     628           0 :       break;
     629             :     }
     630           0 :     isDispatched = true;
     631           0 :     if (!consumed) {
     632           0 :       consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
     633             :     }
     634             :   }
     635             : 
     636             :   // If one of the keypress event was consumed, return ConsumeNoDefault.
     637           0 :   if (consumed) {
     638           0 :     aStatus = nsEventStatus_eConsumeNoDefault;
     639             :   }
     640             : 
     641           0 :   return isDispatched;
     642             : }
     643             : 
     644             : /******************************************************************************
     645             :  * TextEventDispatcher::PendingComposition
     646             :  *****************************************************************************/
     647             : 
     648           0 : TextEventDispatcher::PendingComposition::PendingComposition()
     649             : {
     650           0 :   Clear();
     651           0 : }
     652             : 
     653             : void
     654           0 : TextEventDispatcher::PendingComposition::Clear()
     655             : {
     656           0 :   mString.Truncate();
     657           0 :   mClauses = nullptr;
     658           0 :   mCaret.mRangeType = TextRangeType::eUninitialized;
     659           0 :   mReplacedNativeLineBreakers = false;
     660           0 : }
     661             : 
     662             : void
     663           0 : TextEventDispatcher::PendingComposition::EnsureClauseArray()
     664             : {
     665           0 :   if (mClauses) {
     666           0 :     return;
     667             :   }
     668           0 :   mClauses = new TextRangeArray();
     669             : }
     670             : 
     671             : nsresult
     672           0 : TextEventDispatcher::PendingComposition::SetString(const nsAString& aString)
     673             : {
     674           0 :   MOZ_ASSERT(!mReplacedNativeLineBreakers);
     675           0 :   mString = aString;
     676           0 :   return NS_OK;
     677             : }
     678             : 
     679             : nsresult
     680           0 : TextEventDispatcher::PendingComposition::AppendClause(
     681             :                                            uint32_t aLength,
     682             :                                            TextRangeType aTextRangeType)
     683             : {
     684           0 :   MOZ_ASSERT(!mReplacedNativeLineBreakers);
     685             : 
     686           0 :   if (NS_WARN_IF(!aLength)) {
     687           0 :     return NS_ERROR_INVALID_ARG;
     688             :   }
     689             : 
     690           0 :   switch (aTextRangeType) {
     691             :     case TextRangeType::eRawClause:
     692             :     case TextRangeType::eSelectedRawClause:
     693             :     case TextRangeType::eConvertedClause:
     694             :     case TextRangeType::eSelectedClause: {
     695           0 :       EnsureClauseArray();
     696           0 :       TextRange textRange;
     697           0 :       textRange.mStartOffset =
     698           0 :         mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset;
     699           0 :       textRange.mEndOffset = textRange.mStartOffset + aLength;
     700           0 :       textRange.mRangeType = aTextRangeType;
     701           0 :       mClauses->AppendElement(textRange);
     702           0 :       return NS_OK;
     703             :     }
     704             :     default:
     705           0 :       return NS_ERROR_INVALID_ARG;
     706             :   }
     707             : }
     708             : 
     709             : nsresult
     710           0 : TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset,
     711             :                                                   uint32_t aLength)
     712             : {
     713           0 :   MOZ_ASSERT(!mReplacedNativeLineBreakers);
     714             : 
     715           0 :   mCaret.mStartOffset = aOffset;
     716           0 :   mCaret.mEndOffset = mCaret.mStartOffset + aLength;
     717           0 :   mCaret.mRangeType = TextRangeType::eCaret;
     718           0 :   return NS_OK;
     719             : }
     720             : 
     721             : nsresult
     722           0 : TextEventDispatcher::PendingComposition::Set(const nsAString& aString,
     723             :                                              const TextRangeArray* aRanges)
     724             : {
     725           0 :   Clear();
     726             : 
     727           0 :   nsresult rv = SetString(aString);
     728           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     729           0 :     return rv;
     730             :   }
     731             : 
     732           0 :   if (!aRanges || aRanges->IsEmpty()) {
     733             :     // Create dummy range if mString isn't empty.
     734           0 :     if (!mString.IsEmpty()) {
     735           0 :       rv = AppendClause(mString.Length(), TextRangeType::eRawClause);
     736           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     737           0 :         return rv;
     738             :       }
     739           0 :       ReplaceNativeLineBreakers();
     740             :     }
     741           0 :     return NS_OK;
     742             :   }
     743             : 
     744             :   // Adjust offsets in the ranges for XP linefeed character (only \n).
     745           0 :   for (uint32_t i = 0; i < aRanges->Length(); ++i) {
     746           0 :     TextRange range = aRanges->ElementAt(i);
     747           0 :     if (range.mRangeType == TextRangeType::eCaret) {
     748           0 :       mCaret = range;
     749             :     } else {
     750           0 :       EnsureClauseArray();
     751           0 :       mClauses->AppendElement(range);
     752             :     }
     753             :   }
     754           0 :   ReplaceNativeLineBreakers();
     755           0 :   return NS_OK;
     756             : }
     757             : 
     758             : void
     759           0 : TextEventDispatcher::PendingComposition::ReplaceNativeLineBreakers()
     760             : {
     761           0 :   mReplacedNativeLineBreakers = true;
     762             : 
     763             :   // If the composition string is empty, we don't need to do anything.
     764           0 :   if (mString.IsEmpty()) {
     765           0 :     return;
     766             :   }
     767             : 
     768           0 :   nsAutoString nativeString(mString);
     769             :   // Don't expose CRLF nor CR to web contents, instead, use LF.
     770           0 :   mString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n"));
     771           0 :   mString.ReplaceSubstring(NS_LITERAL_STRING("\r"), NS_LITERAL_STRING("\n"));
     772             : 
     773             :   // If the length isn't changed, we don't need to adjust any offset and length
     774             :   // of mClauses nor mCaret.
     775           0 :   if (nativeString.Length() == mString.Length()) {
     776           0 :     return;
     777             :   }
     778             : 
     779           0 :   if (mClauses) {
     780           0 :     for (TextRange& clause : *mClauses) {
     781           0 :       AdjustRange(clause, nativeString);
     782             :     }
     783             :   }
     784           0 :   if (mCaret.mRangeType == TextRangeType::eCaret) {
     785           0 :     AdjustRange(mCaret, nativeString);
     786             :   }
     787             : }
     788             : 
     789             : // static
     790             : void
     791           0 : TextEventDispatcher::PendingComposition::AdjustRange(
     792             :                                            TextRange& aRange,
     793             :                                            const nsAString& aNativeString)
     794             : {
     795           0 :   TextRange nativeRange = aRange;
     796             :   // XXX Following code wastes runtime cost because this causes computing
     797             :   //     mStartOffset for each clause from the start of composition string.
     798             :   //     If we'd make TextRange have only its length, we don't need to do
     799             :   //     this.  However, this must not be so serious problem because
     800             :   //     composition string is usually short and separated as a few clauses.
     801           0 :   if (nativeRange.mStartOffset > 0) {
     802             :     nsAutoString preText(
     803           0 :       Substring(aNativeString, 0, nativeRange.mStartOffset));
     804           0 :     preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
     805           0 :                              NS_LITERAL_STRING("\n"));
     806           0 :     aRange.mStartOffset = preText.Length();
     807             :   }
     808           0 :   if (nativeRange.Length() == 0) {
     809           0 :     aRange.mEndOffset = aRange.mStartOffset;
     810             :   } else {
     811             :     nsAutoString clause(
     812           0 :       Substring(aNativeString, nativeRange.mStartOffset, nativeRange.Length()));
     813           0 :     clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
     814           0 :                             NS_LITERAL_STRING("\n"));
     815           0 :     aRange.mEndOffset = aRange.mStartOffset + clause.Length();
     816             :   }
     817           0 : }
     818             : 
     819             : nsresult
     820           0 : TextEventDispatcher::PendingComposition::Flush(
     821             :                                            TextEventDispatcher* aDispatcher,
     822             :                                            nsEventStatus& aStatus,
     823             :                                            const WidgetEventTime* aEventTime)
     824             : {
     825           0 :   aStatus = nsEventStatus_eIgnore;
     826             : 
     827           0 :   nsresult rv = aDispatcher->GetState();
     828           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     829           0 :     return rv;
     830             :   }
     831             : 
     832           0 :   if (mClauses && !mClauses->IsEmpty() &&
     833           0 :       mClauses->LastElement().mEndOffset != mString.Length()) {
     834             :     NS_WARNING("Sum of length of the all clauses must be same as the string "
     835           0 :                "length");
     836           0 :     Clear();
     837           0 :     return NS_ERROR_ILLEGAL_VALUE;
     838             :   }
     839           0 :   if (mCaret.mRangeType == TextRangeType::eCaret) {
     840           0 :     if (mCaret.mEndOffset > mString.Length()) {
     841           0 :       NS_WARNING("Caret position is out of the composition string");
     842           0 :       Clear();
     843           0 :       return NS_ERROR_ILLEGAL_VALUE;
     844             :     }
     845           0 :     EnsureClauseArray();
     846           0 :     mClauses->AppendElement(mCaret);
     847             :   }
     848             : 
     849             :   // If the composition string is set without Set(), we need to replace native
     850             :   // line breakers in the composition string with XP line breaker.
     851           0 :   if (!mReplacedNativeLineBreakers) {
     852           0 :     ReplaceNativeLineBreakers();
     853             :   }
     854             : 
     855           0 :   RefPtr<TextEventDispatcher> kungFuDeathGrip(aDispatcher);
     856           0 :   nsCOMPtr<nsIWidget> widget(aDispatcher->mWidget);
     857           0 :   WidgetCompositionEvent compChangeEvent(true, eCompositionChange, widget);
     858           0 :   aDispatcher->InitEvent(compChangeEvent);
     859           0 :   if (aEventTime) {
     860           0 :     compChangeEvent.AssignEventTime(*aEventTime);
     861             :   }
     862           0 :   compChangeEvent.mData = mString;
     863           0 :   if (mClauses) {
     864           0 :     MOZ_ASSERT(!mClauses->IsEmpty(),
     865             :                "mClauses must be non-empty array when it's not nullptr");
     866           0 :     compChangeEvent.mRanges = mClauses;
     867             :   }
     868             : 
     869             :   // While this method dispatches a composition event, some other event handler
     870             :   // cause more clauses to be added.  So, we should clear pending composition
     871             :   // before dispatching the event.
     872           0 :   Clear();
     873             : 
     874             :   rv = aDispatcher->StartCompositionAutomaticallyIfNecessary(aStatus,
     875           0 :                                                              aEventTime);
     876           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     877           0 :     return rv;
     878             :   }
     879           0 :   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     880           0 :     return NS_OK;
     881             :   }
     882           0 :   rv = aDispatcher->DispatchEvent(widget, compChangeEvent, aStatus);
     883           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     884           0 :     return rv;
     885             :   }
     886             : 
     887           0 :   return NS_OK;
     888             : }
     889             : 
     890             : } // namespace widget
     891             : } // namespace mozilla

Generated by: LCOV version 1.13