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