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