1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef TextInputHandler_h_
8 #define TextInputHandler_h_
9 
10 #include "nsCocoaUtils.h"
11 
12 #import <Carbon/Carbon.h>
13 #import <Cocoa/Cocoa.h>
14 #include "mozView.h"
15 #include "nsString.h"
16 #include "nsCOMPtr.h"
17 #include "nsITimer.h"
18 #include "nsTArray.h"
19 #include "mozilla/EventForwards.h"
20 #include "mozilla/TextEventDispatcherListener.h"
21 #include "WritingModes.h"
22 
23 class nsChildView;
24 
25 namespace mozilla {
26 namespace widget {
27 
28 // Key code constants
29 enum
30 {
31 #if !defined(MAC_OS_X_VERSION_10_12) || \
32   MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
33   kVK_RightCommand    = 0x36, // right command key
34 #endif
35 
36   kVK_PC_PrintScreen     = kVK_F13,
37   kVK_PC_ScrollLock      = kVK_F14,
38   kVK_PC_Pause           = kVK_F15,
39 
40   kVK_PC_Insert          = kVK_Help,
41   kVK_PC_Backspace       = kVK_Delete,
42   kVK_PC_Delete          = kVK_ForwardDelete,
43 
44   kVK_PC_ContextMenu     = 0x6E,
45 
46   kVK_Powerbook_KeypadEnter = 0x34  // Enter on Powerbook's keyboard is different
47 };
48 
49 /**
50  * TISInputSourceWrapper is a wrapper for the TISInputSourceRef.  If we get the
51  * TISInputSourceRef from InputSourceID, we need to release the CFArray instance
52  * which is returned by TISCreateInputSourceList.  However, when we release the
53  * list, we cannot access the TISInputSourceRef.  So, it's not usable, and it
54  * may cause the memory leak bugs.  nsTISInputSource automatically releases the
55  * list when the instance is destroyed.
56  */
57 class TISInputSourceWrapper
58 {
59 public:
60   static TISInputSourceWrapper& CurrentInputSource();
61   /**
62    * Shutdown() should be called when nobody doesn't need to use this class.
63    */
64   static void Shutdown();
65 
TISInputSourceWrapper()66   TISInputSourceWrapper()
67   {
68     mInputSourceList = nullptr;
69     Clear();
70   }
71 
TISInputSourceWrapper(const char * aID)72   explicit TISInputSourceWrapper(const char* aID)
73   {
74     mInputSourceList = nullptr;
75     InitByInputSourceID(aID);
76   }
77 
TISInputSourceWrapper(SInt32 aLayoutID)78   explicit TISInputSourceWrapper(SInt32 aLayoutID)
79   {
80     mInputSourceList = nullptr;
81     InitByLayoutID(aLayoutID);
82   }
83 
TISInputSourceWrapper(TISInputSourceRef aInputSource)84   explicit TISInputSourceWrapper(TISInputSourceRef aInputSource)
85   {
86     mInputSourceList = nullptr;
87     InitByTISInputSourceRef(aInputSource);
88   }
89 
~TISInputSourceWrapper()90   ~TISInputSourceWrapper() { Clear(); }
91 
92   void InitByInputSourceID(const char* aID);
93   void InitByInputSourceID(const nsAFlatString &aID);
94   void InitByInputSourceID(const CFStringRef aID);
95   /**
96    * InitByLayoutID() initializes the keyboard layout by the layout ID.
97    *
98    * @param aLayoutID             An ID of keyboard layout.
99    *                                0: US
100    *                                1: Greek
101    *                                2: German
102    *                                3: Swedish-Pro
103    *                                4: Dvorak-Qwerty Cmd
104    *                                5: Thai
105    *                                6: Arabic
106    *                                7: French
107    *                                8: Hebrew
108    *                                9: Lithuanian
109    *                               10: Norwegian
110    *                               11: Spanish
111    * @param aOverrideKeyboard     When testing set to TRUE, otherwise, set to
112    *                              FALSE.  When TRUE, we use an ANSI keyboard
113    *                              instead of the actual keyboard.
114    */
115   void InitByLayoutID(SInt32 aLayoutID, bool aOverrideKeyboard = false);
116   void InitByCurrentInputSource();
117   void InitByCurrentKeyboardLayout();
118   void InitByCurrentASCIICapableInputSource();
119   void InitByCurrentASCIICapableKeyboardLayout();
120   void InitByCurrentInputMethodKeyboardLayoutOverride();
121   void InitByTISInputSourceRef(TISInputSourceRef aInputSource);
122   void InitByLanguage(CFStringRef aLanguage);
123 
124   /**
125    * If the instance is initialized with a keyboard layout input source,
126    * returns it.
127    * If the instance is initialized with an IME mode input source, the result
128    * references the keyboard layout for the IME mode.  However, this can be
129    * initialized only when the IME mode is actually selected.  I.e, if IME mode
130    * input source is initialized with LayoutID or SourceID, this returns null.
131    */
GetKeyboardLayoutInputSource()132   TISInputSourceRef GetKeyboardLayoutInputSource() const
133   {
134     return mKeyboardLayout;
135   }
136   const UCKeyboardLayout* GetUCKeyboardLayout();
137 
138   bool IsOpenedIMEMode();
139   bool IsIMEMode();
140   bool IsKeyboardLayout();
141 
IsASCIICapable()142   bool IsASCIICapable()
143   {
144     NS_ENSURE_TRUE(mInputSource, false);
145     return GetBoolProperty(kTISPropertyInputSourceIsASCIICapable);
146   }
147 
IsEnabled()148   bool IsEnabled()
149   {
150     NS_ENSURE_TRUE(mInputSource, false);
151     return GetBoolProperty(kTISPropertyInputSourceIsEnabled);
152   }
153 
154   bool GetLanguageList(CFArrayRef &aLanguageList);
155   bool GetPrimaryLanguage(CFStringRef &aPrimaryLanguage);
156   bool GetPrimaryLanguage(nsAString &aPrimaryLanguage);
157 
GetLocalizedName(CFStringRef & aName)158   bool GetLocalizedName(CFStringRef &aName)
159   {
160     NS_ENSURE_TRUE(mInputSource, false);
161     return GetStringProperty(kTISPropertyLocalizedName, aName);
162   }
163 
GetLocalizedName(nsAString & aName)164   bool GetLocalizedName(nsAString &aName)
165   {
166     NS_ENSURE_TRUE(mInputSource, false);
167     return GetStringProperty(kTISPropertyLocalizedName, aName);
168   }
169 
GetInputSourceID(CFStringRef & aID)170   bool GetInputSourceID(CFStringRef &aID)
171   {
172     NS_ENSURE_TRUE(mInputSource, false);
173     return GetStringProperty(kTISPropertyInputSourceID, aID);
174   }
175 
GetInputSourceID(nsAString & aID)176   bool GetInputSourceID(nsAString &aID)
177   {
178     NS_ENSURE_TRUE(mInputSource, false);
179     return GetStringProperty(kTISPropertyInputSourceID, aID);
180   }
181 
GetBundleID(CFStringRef & aBundleID)182   bool GetBundleID(CFStringRef &aBundleID)
183   {
184     NS_ENSURE_TRUE(mInputSource, false);
185     return GetStringProperty(kTISPropertyBundleID, aBundleID);
186   }
187 
GetBundleID(nsAString & aBundleID)188   bool GetBundleID(nsAString &aBundleID)
189   {
190     NS_ENSURE_TRUE(mInputSource, false);
191     return GetStringProperty(kTISPropertyBundleID, aBundleID);
192   }
193 
GetInputSourceType(CFStringRef & aType)194   bool GetInputSourceType(CFStringRef &aType)
195   {
196     NS_ENSURE_TRUE(mInputSource, false);
197     return GetStringProperty(kTISPropertyInputSourceType, aType);
198   }
199 
GetInputSourceType(nsAString & aType)200   bool GetInputSourceType(nsAString &aType)
201   {
202     NS_ENSURE_TRUE(mInputSource, false);
203     return GetStringProperty(kTISPropertyInputSourceType, aType);
204   }
205 
206   bool IsForRTLLanguage();
207   bool IsInitializedByCurrentInputSource();
208 
209   enum {
210     // 40 is an actual result of the ::LMGetKbdType() when we connect an
211     // unknown keyboard and set the keyboard type to ANSI manually on the
212     // set up dialog.
213     eKbdType_ANSI = 40
214   };
215 
216   void Select();
217   void Clear();
218 
219   /**
220    * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent.
221    *
222    * @param aNativeKeyEvent       A native key event for which you want to
223    *                              dispatch a Gecko key event.
224    * @param aKeyEvent             The result -- a Gecko key event initialized
225    *                              from the native key event.
226    * @param aInsertString         If caller expects that the event will cause
227    *                              a character to be input (say in an editor),
228    *                              the caller should set this.  Otherwise,
229    *                              if caller sets null to this, this method will
230    *                              compute the character to be input from
231    *                              characters of aNativeKeyEvent.
232    */
233   void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent,
234                     const nsAString *aInsertString = nullptr);
235 
236   /**
237    * WillDispatchKeyboardEvent() computes aKeyEvent.mAlternativeCharCodes and
238    * recompute aKeyEvent.mCharCode if it's necessary.
239    *
240    * @param aNativeKeyEvent       A native key event for which you want to
241    *                              dispatch a Gecko key event.
242    * @param aInsertString         If caller expects that the event will cause
243    *                              a character to be input (say in an editor),
244    *                              the caller should set this.  Otherwise,
245    *                              if caller sets null to this, this method will
246    *                              compute the character to be input from
247    *                              characters of aNativeKeyEvent.
248    * @param aKeyEvent             The result -- a Gecko key event initialized
249    *                              from the native key event.  This must be
250    *                              eKeyPress event.
251    */
252   void WillDispatchKeyboardEvent(NSEvent* aNativeKeyEvent,
253                                  const nsAString* aInsertString,
254                                  WidgetKeyboardEvent& aKeyEvent);
255 
256   /**
257    * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current
258    * keyboard layout.
259    *
260    * @param aNativeKeyCode        A native keycode.
261    * @param aKbType               A native Keyboard Type value.  Typically,
262    *                              this is a result of ::LMGetKbdType().
263    * @param aCmdIsPressed         TRUE if Cmd key is pressed.  Otherwise, FALSE.
264    * @return                      The computed Gecko keycode.
265    */
266   uint32_t ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType,
267                                bool aCmdIsPressed);
268 
269   /**
270    * ComputeGeckoKeyNameIndex() returns Gecko key name index for the key.
271    *
272    * @param aNativeKeyCode        A native keycode.
273    */
274   static KeyNameIndex ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode);
275 
276   /**
277    * ComputeGeckoCodeNameIndex() returns Gecko code name index for the key.
278    *
279    * @param aNativeKeyCode        A native keycode.
280    */
281   static CodeNameIndex ComputeGeckoCodeNameIndex(UInt32 aNativeKeyCode);
282 
283 protected:
284   /**
285    * TranslateToString() computes the inputted text from the native keyCode,
286    * modifier flags and keyboard type.
287    *
288    * @param aKeyCode              A native keyCode.
289    * @param aModifiers            Combination of native modifier flags.
290    * @param aKbType               A native Keyboard Type value.  Typically,
291    *                              this is a result of ::LMGetKbdType().
292    * @param aStr                  Result, i.e., inputted text.
293    *                              The result can be two or more characters.
294    * @return                      If succeeded, TRUE.  Otherwise, FALSE.
295    *                              Even if TRUE, aStr can be empty string.
296    */
297   bool TranslateToString(UInt32 aKeyCode, UInt32 aModifiers,
298                            UInt32 aKbType, nsAString &aStr);
299 
300   /**
301    * TranslateToChar() computes the inputted character from the native keyCode,
302    * modifier flags and keyboard type.  If two or more characters would be
303    * input, this returns 0.
304    *
305    * @param aKeyCode              A native keyCode.
306    * @param aModifiers            Combination of native modifier flags.
307    * @param aKbType               A native Keyboard Type value.  Typically,
308    *                              this is a result of ::LMGetKbdType().
309    * @return                      If succeeded and the result is one character,
310    *                              returns the charCode of it.  Otherwise,
311    *                              returns 0.
312    */
313   uint32_t TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType);
314 
315   /**
316    * ComputeInsertString() computes string to be inserted with the key event.
317    *
318    * @param aNativeKeyEvent     The native key event which causes our keyboard
319    *                            event(s).
320    * @param aKeyEvent           A Gecko key event which was partially
321    *                            initialized with aNativeKeyEvent.
322    * @param aInsertString       The string to be inputting by aNativeKeyEvent.
323    *                            This should be specified by InsertText().
324    *                            In other words, if the key event doesn't cause
325    *                            a call of InsertText(), this can be nullptr.
326    * @param aResult             The string which should be set to charCode of
327    *                            keypress event(s).
328    */
329   void ComputeInsertStringForCharCode(NSEvent* aNativeKeyEvent,
330                                       const WidgetKeyboardEvent& aKeyEvent,
331                                       const nsAString* aInsertString,
332                                       nsAString& aResult);
333 
334   /**
335    * IsPrintableKeyEvent() returns true if aNativeKeyEvent is caused by
336    * a printable key.  Otherwise, returns false.
337    */
338   bool IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const;
339 
340   /**
341    * GetKbdType() returns physical keyboard type.
342    */
343   UInt32 GetKbdType() const;
344 
345   bool GetBoolProperty(const CFStringRef aKey);
346   bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr);
347   bool GetStringProperty(const CFStringRef aKey, nsAString &aStr);
348 
349   TISInputSourceRef mInputSource;
350   TISInputSourceRef mKeyboardLayout;
351   CFArrayRef mInputSourceList;
352   const UCKeyboardLayout* mUCKeyboardLayout;
353   int8_t mIsRTL;
354 
355   bool mOverrideKeyboard;
356 
357   static TISInputSourceWrapper* sCurrentInputSource;
358 };
359 
360 /**
361  * TextInputHandlerBase is a base class of IMEInputHandler and TextInputHandler.
362  * Utility methods should be implemented this level.
363  */
364 
365 class TextInputHandlerBase : public TextEventDispatcherListener
366 {
367 public:
368   /**
369    * Other TextEventDispatcherListener methods should be implemented in
370    * IMEInputHandler.
371    */
372   NS_DECL_ISUPPORTS
373 
374   /**
375    * DispatchEvent() dispatches aEvent on mWidget.
376    *
377    * @param aEvent                An event which you want to dispatch.
378    * @return                      TRUE if the event is consumed by web contents
379    *                              or chrome contents.  Otherwise, FALSE.
380    */
381   bool DispatchEvent(WidgetGUIEvent& aEvent);
382 
383   /**
384    * SetSelection() dispatches eSetSelection event for the aRange.
385    *
386    * @param aRange                The range which will be selected.
387    * @return                      TRUE if setting selection is succeeded and
388    *                              the widget hasn't been destroyed.
389    *                              Otherwise, FALSE.
390    */
391   bool SetSelection(NSRange& aRange);
392 
393   /**
394    * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent.
395    *
396    * @param aNativeKeyEvent       A native key event for which you want to
397    *                              dispatch a Gecko key event.
398    * @param aKeyEvent             The result -- a Gecko key event initialized
399    *                              from the native key event.
400    * @param aInsertString         If caller expects that the event will cause
401    *                              a character to be input (say in an editor),
402    *                              the caller should set this.  Otherwise,
403    *                              if caller sets null to this, this method will
404    *                              compute the character to be input from
405    *                              characters of aNativeKeyEvent.
406    */
407   void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent,
408                     const nsAString *aInsertString = nullptr);
409 
410   /**
411    * SynthesizeNativeKeyEvent() is an implementation of
412    * nsIWidget::SynthesizeNativeKeyEvent().  See the document in nsIWidget.h
413    * for the detail.
414    */
415   nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
416                                     int32_t aNativeKeyCode,
417                                     uint32_t aModifierFlags,
418                                     const nsAString& aCharacters,
419                                     const nsAString& aUnmodifiedCharacters);
420 
421   /**
422    * Utility method intended for testing. Attempts to construct a native key
423    * event that would have been generated during an actual key press. This
424    * *does not dispatch* the native event. Instead, it is attached to the
425    * |mNativeKeyEvent| field of the Gecko event that is passed in.
426    * @param aKeyEvent  Gecko key event to attach the native event to
427    */
428   NS_IMETHOD AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent);
429 
430   /**
431    * GetWindowLevel() returns the window level of current focused (in Gecko)
432    * window.  E.g., if an <input> element in XUL panel has focus, this returns
433    * the XUL panel's window level.
434    */
435   NSInteger GetWindowLevel();
436 
437   /**
438    * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special
439    * Gecko keyCode.  A key is "special" if it isn't used for text input.
440    *
441    * @param aNativeKeyCode        A native keycode.
442    * @return                      If the keycode is mapped to a special key,
443    *                              TRUE.  Otherwise, FALSE.
444    */
445   static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode);
446 
447 
448   /**
449    * EnableSecureEventInput() and DisableSecureEventInput() wrap the Carbon
450    * Event Manager APIs with the same names.  In addition they keep track of
451    * how many times we've called them (in the same process) -- unlike the
452    * Carbon Event Manager APIs, which only keep track of how many times they've
453    * been called from any and all processes.
454    *
455    * The Carbon Event Manager's IsSecureEventInputEnabled() returns whether
456    * secure event input mode is enabled (in any process).  This class's
457    * IsSecureEventInputEnabled() returns whether we've made any calls to
458    * EnableSecureEventInput() that are not (yet) offset by the calls we've
459    * made to DisableSecureEventInput().
460    */
461   static void EnableSecureEventInput();
462   static void DisableSecureEventInput();
463   static bool IsSecureEventInputEnabled();
464 
465   /**
466    * EnsureSecureEventInputDisabled() calls DisableSecureEventInput() until
467    * our call count becomes 0.
468    */
469   static void EnsureSecureEventInputDisabled();
470 
471 public:
472    /**
473    * mWidget must not be destroyed without OnDestroyWidget being called.
474    *
475    * @param aDestroyingWidget     Destroying widget.  This might not be mWidget.
476    * @return                      This result doesn't have any meaning for
477    *                              callers.  When aDstroyingWidget isn't the same
478    *                              as mWidget, FALSE.  Then, inherited methods in
479    *                              sub classes should return from this method
480    *                              without cleaning up.
481    */
482   virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget);
483 
484 protected:
485   // The creator of this instance, client and its text event dispatcher.
486   // These members must not be nullptr after initialized until
487   // OnDestroyWidget() is called.
488   nsChildView* mWidget; // [WEAK]
489   RefPtr<TextEventDispatcher> mDispatcher;
490 
491   // The native view for mWidget.
492   // This view handles the actual text inputting.
493   NSView<mozView>* mView; // [STRONG]
494 
495   TextInputHandlerBase(nsChildView* aWidget, NSView<mozView> *aNativeView);
496   virtual ~TextInputHandlerBase();
497 
Destroyed()498   bool Destroyed() { return !mWidget; }
499 
500   /**
501    * mCurrentKeyEvent indicates what key event we are handling.  While
502    * handling a native keydown event, we need to store the event for insertText,
503    * doCommandBySelector and various action message handlers of NSResponder
504    * such as [NSResponder insertNewline:sender].
505    */
506   struct KeyEventState
507   {
508     // Handling native key event
509     NSEvent* mKeyEvent;
510     // String specified by InsertText().  This is not null only during a
511     // call of InsertText().
512     nsAString* mInsertString;
513     // String which are included in [mKeyEvent characters] and already handled
514     // by InsertText() call(s).
515     nsString mInsertedString;
516     // Whether keydown event was consumed by web contents or chrome contents.
517     bool mKeyDownHandled;
518     // Whether keypress event was dispatched for mKeyEvent.
519     bool mKeyPressDispatched;
520     // Whether keypress event was consumed by web contents or chrome contents.
521     bool mKeyPressHandled;
522     // Whether the key event causes other key events via IME or something.
523     bool mCausedOtherKeyEvents;
524     // Whether the key event causes composition change or committing
525     // composition.  So, even if InsertText() is called, this may be false
526     // if it dispatches keypress event.
527     bool mCompositionDispatched;
528 
KeyEventStateKeyEventState529     KeyEventState() : mKeyEvent(nullptr)
530     {
531       Clear();
532     }
533 
KeyEventStateKeyEventState534     explicit KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nullptr)
535     {
536       Clear();
537       Set(aNativeKeyEvent);
538     }
539 
540     KeyEventState(const KeyEventState &aOther) = delete;
541 
~KeyEventStateKeyEventState542     ~KeyEventState()
543     {
544       Clear();
545     }
546 
SetKeyEventState547     void Set(NSEvent* aNativeKeyEvent)
548     {
549       NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL");
550       Clear();
551       mKeyEvent = [aNativeKeyEvent retain];
552     }
553 
ClearKeyEventState554     void Clear()
555     {
556       if (mKeyEvent) {
557         [mKeyEvent release];
558         mKeyEvent = nullptr;
559       }
560       mInsertString = nullptr;
561       mInsertedString.Truncate();
562       mKeyDownHandled = false;
563       mKeyPressDispatched = false;
564       mKeyPressHandled = false;
565       mCausedOtherKeyEvents = false;
566       mCompositionDispatched = false;
567     }
568 
IsDefaultPreventedKeyEventState569     bool IsDefaultPrevented() const
570     {
571       return mKeyDownHandled || mKeyPressHandled || mCausedOtherKeyEvents ||
572              mCompositionDispatched;
573     }
574 
CanDispatchKeyPressEventKeyEventState575     bool CanDispatchKeyPressEvent() const
576     {
577       return !mKeyPressDispatched && !IsDefaultPrevented();
578     }
579 
580     void InitKeyEvent(TextInputHandlerBase* aHandler,
581                       WidgetKeyboardEvent& aKeyEvent);
582 
583     /**
584      * GetUnhandledString() returns characters of the event which have not been
585      * handled with InsertText() yet. For example, if there is a composition
586      * caused by a dead key press like '`' and it's committed by some key
587      * combinations like |Cmd+v|, then, the |v|'s KeyDown event's |characters|
588      * is |`v|.  Then, after |`| is committed with a call of InsertString(),
589      * this returns only 'v'.
590      */
591     void GetUnhandledString(nsAString& aUnhandledString) const;
592   };
593 
594   /**
595    * Helper classes for guaranteeing cleaning mCurrentKeyEvent
596    */
597   class AutoKeyEventStateCleaner
598   {
599   public:
AutoKeyEventStateCleaner(TextInputHandlerBase * aHandler)600     explicit AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) :
601       mHandler(aHandler)
602     {
603     }
604 
~AutoKeyEventStateCleaner()605     ~AutoKeyEventStateCleaner()
606     {
607       mHandler->RemoveCurrentKeyEvent();
608     }
609   private:
610     RefPtr<TextInputHandlerBase> mHandler;
611   };
612 
613   class MOZ_STACK_CLASS AutoInsertStringClearer
614   {
615   public:
AutoInsertStringClearer(KeyEventState * aState)616     explicit AutoInsertStringClearer(KeyEventState* aState)
617       : mState(aState)
618     {
619     }
620     ~AutoInsertStringClearer();
621 
622   private:
623     KeyEventState* mState;
624   };
625 
626   /**
627    * mCurrentKeyEvents stores all key events which are being processed.
628    * When we call interpretKeyEvents, IME may generate other key events.
629    * mCurrentKeyEvents[0] is the latest key event.
630    */
631   nsTArray<KeyEventState*> mCurrentKeyEvents;
632 
633   /**
634    * mFirstKeyEvent must be used for first key event.  This member prevents
635    * memory fragmentation for most key events.
636    */
637   KeyEventState mFirstKeyEvent;
638 
639   /**
640    * PushKeyEvent() adds the current key event to mCurrentKeyEvents.
641    */
PushKeyEvent(NSEvent * aNativeKeyEvent)642   KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent)
643   {
644     uint32_t nestCount = mCurrentKeyEvents.Length();
645     for (uint32_t i = 0; i < nestCount; i++) {
646       // When the key event is caused by another key event, all key events
647       // which are being handled should be marked as "consumed".
648       mCurrentKeyEvents[i]->mCausedOtherKeyEvents = true;
649     }
650 
651     KeyEventState* keyEvent = nullptr;
652     if (nestCount == 0) {
653       mFirstKeyEvent.Set(aNativeKeyEvent);
654       keyEvent = &mFirstKeyEvent;
655     } else {
656       keyEvent = new KeyEventState(aNativeKeyEvent);
657     }
658     return *mCurrentKeyEvents.AppendElement(keyEvent);
659   }
660 
661   /**
662    * RemoveCurrentKeyEvent() removes the current key event from
663    * mCurrentKeyEvents.
664    */
RemoveCurrentKeyEvent()665   void RemoveCurrentKeyEvent()
666   {
667     NS_ASSERTION(mCurrentKeyEvents.Length() > 0,
668                  "RemoveCurrentKeyEvent() is called unexpectedly");
669     KeyEventState* keyEvent = GetCurrentKeyEvent();
670     mCurrentKeyEvents.RemoveElementAt(mCurrentKeyEvents.Length() - 1);
671     if (keyEvent == &mFirstKeyEvent) {
672       keyEvent->Clear();
673     } else {
674       delete keyEvent;
675     }
676   }
677 
678   /**
679    * GetCurrentKeyEvent() returns current processing key event.
680    */
GetCurrentKeyEvent()681   KeyEventState* GetCurrentKeyEvent()
682   {
683     if (mCurrentKeyEvents.Length() == 0) {
684       return nullptr;
685     }
686     return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1];
687   }
688 
689   struct KeyboardLayoutOverride final
690   {
691     int32_t mKeyboardLayout;
692     bool mOverrideEnabled;
693 
KeyboardLayoutOverridefinal694     KeyboardLayoutOverride() :
695       mKeyboardLayout(0), mOverrideEnabled(false)
696     {
697     }
698   };
699 
KeyboardLayoutOverrideRef()700   const KeyboardLayoutOverride& KeyboardLayoutOverrideRef() const
701   {
702     return mKeyboardOverride;
703   }
704 
705   /**
706    * IsPrintableChar() checks whether the unicode character is
707    * a non-printable ASCII character or not.  Note that this returns
708    * TRUE even if aChar is a non-printable UNICODE character.
709    *
710    * @param aChar                 A unicode character.
711    * @return                      TRUE if aChar is a printable ASCII character
712    *                              or a unicode character.  Otherwise, i.e,
713    *                              if aChar is a non-printable ASCII character,
714    *                              FALSE.
715    */
716   static bool IsPrintableChar(char16_t aChar);
717 
718   /**
719    * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input.
720    *
721    * @param aKeyEvent             A key event.
722    * @return                      TRUE if the key event causes text input.
723    *                              Otherwise, FALSE.
724    */
725   static bool IsNormalCharInputtingEvent(const WidgetKeyboardEvent& aKeyEvent);
726 
727   /**
728    * IsModifierKey() checks whether the native keyCode is for a modifier key.
729    *
730    * @param aNativeKeyCode        A native keyCode.
731    * @return                      TRUE if aNativeKeyCode is for a modifier key.
732    *                              Otherwise, FALSE.
733    */
734   static bool IsModifierKey(UInt32 aNativeKeyCode);
735 
736 private:
737   KeyboardLayoutOverride mKeyboardOverride;
738 
739   static int32_t sSecureEventInputCount;
740 };
741 
742 /**
743  * IMEInputHandler manages:
744  *   1. The IME/keyboard layout statement of nsChildView.
745  *   2. The IME composition statement of nsChildView.
746  * And also provides the methods which controls the current IME transaction of
747  * the instance.
748  *
749  * Note that an nsChildView handles one or more NSView's events.  E.g., even if
750  * a text editor on XUL panel element, the input events handled on the parent
751  * (or its ancestor) widget handles it (the native focus is set to it).  The
752  * actual focused view is notified by OnFocusChangeInGecko.
753  */
754 
755 class IMEInputHandler : public TextInputHandlerBase
756 {
757 public:
758   // TextEventDispatcherListener methods
759   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
760                        const IMENotification& aNotification) override;
761   NS_IMETHOD_(void) OnRemovedFrom(
762                       TextEventDispatcher* aTextEventDispatcher) override;
763   NS_IMETHOD_(void) WillDispatchKeyboardEvent(
764                       TextEventDispatcher* aTextEventDispatcher,
765                       WidgetKeyboardEvent& aKeyboardEvent,
766                       uint32_t aIndexOfKeypress,
767                       void* aData) override;
768 
769 public:
770   virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget) override;
771 
772   virtual void OnFocusChangeInGecko(bool aFocus);
773 
774   void OnSelectionChange(const IMENotification& aIMENotification);
775 
776   /**
777    * Call [NSTextInputContext handleEvent] for mouse event support of IME
778    */
779   bool OnHandleEvent(NSEvent* aEvent);
780 
781   /**
782    * SetMarkedText() is a handler of setMarkedText of NSTextInput.
783    *
784    * @param aAttrString           This mut be an instance of NSAttributedString.
785    *                              If the aString parameter to
786    *                              [ChildView setMarkedText:setSelectedRange:]
787    *                              isn't an instance of NSAttributedString,
788    *                              create an NSAttributedString from it and pass
789    *                              that instead.
790    * @param aSelectedRange        Current selected range (or caret position).
791    * @param aReplacementRange     The range which will be replaced with the
792    *                              aAttrString instead of current marked range.
793    */
794   void SetMarkedText(NSAttributedString* aAttrString,
795                      NSRange& aSelectedRange,
796                      NSRange* aReplacementRange = nullptr);
797 
798   /**
799    * GetAttributedSubstringFromRange() returns an NSAttributedString instance
800    * which is allocated as autorelease for aRange.
801    *
802    * @param aRange                The range of string which you want.
803    * @param aActualRange          The actual range of the result.
804    * @return                      The string in aRange.  If the string is empty,
805    *                              this returns nil.  If succeeded, this returns
806    *                              an instance which is allocated as autorelease.
807    *                              If this has some troubles, returns nil.
808    */
809   NSAttributedString* GetAttributedSubstringFromRange(
810                         NSRange& aRange,
811                         NSRange* aActualRange = nullptr);
812 
813   /**
814    * SelectedRange() returns current selected range.
815    *
816    * @return                      If an editor has focus, this returns selection
817    *                              range in the editor.  Otherwise, this returns
818    *                              selection range  in the focused document.
819    */
820   NSRange SelectedRange();
821 
822   /**
823    * DrawsVerticallyForCharacterAtIndex() returns whether the character at
824    * the given index is being rendered vertically.
825    *
826    * @param aCharIndex            The character offset to query.
827    *
828    * @return                      True if writing-mode is vertical at the given
829    *                              character offset; otherwise false.
830    */
831   bool DrawsVerticallyForCharacterAtIndex(uint32_t aCharIndex);
832 
833   /**
834    * FirstRectForCharacterRange() returns first *character* rect in the range.
835    * Cocoa needs the first line rect in the range, but we cannot compute it
836    * on current implementation.
837    *
838    * @param aRange                A range of text to examine.  Its position is
839    *                              an offset from the beginning of the focused
840    *                              editor or document.
841    * @param aActualRange          If this is not null, this returns the actual
842    *                              range used for computing the result.
843    * @return                      An NSRect containing the first character in
844    *                              aRange, in screen coordinates.
845    *                              If the length of aRange is 0, the width will
846    *                              be 0.
847    */
848   NSRect FirstRectForCharacterRange(NSRange& aRange,
849                                     NSRange* aActualRange = nullptr);
850 
851   /**
852    * CharacterIndexForPoint() returns an offset of a character at aPoint.
853    * XXX This isn't implemented, always returns 0.
854    *
855    * @param                       The point in screen coordinates.
856    * @return                      The offset of the character at aPoint from
857    *                              the beginning of the focused editor or
858    *                              document.
859    */
860   NSUInteger CharacterIndexForPoint(NSPoint& aPoint);
861 
862   /**
863    * GetValidAttributesForMarkedText() returns attributes which we support.
864    *
865    * @return                      Always empty array for now.
866    */
867   NSArray* GetValidAttributesForMarkedText();
868 
869   bool HasMarkedText();
870   NSRange MarkedRange();
871 
IsIMEComposing()872   bool IsIMEComposing() { return mIsIMEComposing; }
873   bool IsIMEOpened();
IsIMEEnabled()874   bool IsIMEEnabled() { return mIsIMEEnabled; }
IsASCIICapableOnly()875   bool IsASCIICapableOnly() { return mIsASCIICapableOnly; }
IgnoreIMECommit()876   bool IgnoreIMECommit() { return mIgnoreIMECommit; }
877 
IgnoreIMEComposition()878   bool IgnoreIMEComposition()
879   {
880     // Ignore the IME composition events when we're pending to discard the
881     // composition and we are not to handle the IME composition now.
882     return (mPendingMethods & kDiscardIMEComposition) &&
883            (mIsInFocusProcessing || !IsFocused());
884   }
885 
886   void CommitIMEComposition();
887   void CancelIMEComposition();
888 
889   void EnableIME(bool aEnableIME);
890   void SetIMEOpenState(bool aOpen);
891   void SetASCIICapableOnly(bool aASCIICapableOnly);
892 
893   /**
894    * True if OSX believes that our view has keyboard focus.
895    */
896   bool IsFocused();
897 
898   static CFArrayRef CreateAllIMEModeList();
899   static void DebugPrintAllIMEModes();
900 
901   // Don't use ::TSMGetActiveDocument() API directly, the document may not
902   // be what you want.
903   static TSMDocumentID GetCurrentTSMDocumentID();
904 
905 protected:
906   // We cannot do some jobs in the given stack by some reasons.
907   // Following flags and the timer provide the execution pending mechanism,
908   // See the comment in nsCocoaTextInputHandler.mm.
909   nsCOMPtr<nsITimer> mTimer;
910   enum {
911     kNotifyIMEOfFocusChangeInGecko = 1,
912     kDiscardIMEComposition         = 2,
913     kSyncASCIICapableOnly          = 4
914   };
915   uint32_t mPendingMethods;
916 
917   IMEInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView);
918   virtual ~IMEInputHandler();
919 
920   void ResetTimer();
921 
922   virtual void ExecutePendingMethods();
923 
924   /**
925    * InsertTextAsCommittingComposition() commits current composition.  If there
926    * is no composition, this starts a composition and commits it immediately.
927    *
928    * @param aAttrString           A string which is committed.
929    * @param aReplacementRange     The range which will be replaced with the
930    *                              aAttrString instead of current selection.
931    */
932   void InsertTextAsCommittingComposition(NSAttributedString* aAttrString,
933                                          NSRange* aReplacementRange);
934 
935 private:
936   // If mIsIMEComposing is true, the composition string is stored here.
937   NSString* mIMECompositionString;
938   // If mIsIMEComposing is true, the start offset of the composition string.
939   uint32_t mIMECompositionStart;
940 
941   NSRange mMarkedRange;
942   NSRange mSelectedRange;
943 
944   NSRange mRangeForWritingMode; // range within which mWritingMode applies
945   mozilla::WritingMode mWritingMode;
946 
947   bool mIsIMEComposing;
948   bool mIsIMEEnabled;
949   bool mIsASCIICapableOnly;
950   bool mIgnoreIMECommit;
951   // This flag is enabled by OnFocusChangeInGecko, and will be cleared by
952   // ExecutePendingMethods.  When this is true, IsFocus() returns TRUE.  At
953   // that time, the focus processing in Gecko might not be finished yet.  So,
954   // you cannot use WidgetQueryContentEvent or something.
955   bool mIsInFocusProcessing;
956   bool mIMEHasFocus;
957 
958   void KillIMEComposition();
959   void SendCommittedText(NSString *aString);
960   void OpenSystemPreferredLanguageIME();
961 
962   // Pending methods
963   void NotifyIMEOfFocusChangeInGecko();
964   void DiscardIMEComposition();
965   void SyncASCIICapableOnly();
966 
967   static bool sStaticMembersInitialized;
968   static CFStringRef sLatestIMEOpenedModeInputSourceID;
969   static void InitStaticMembers();
970   static void OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter,
971                                              void* aObserver,
972                                              CFStringRef aName,
973                                              const void* aObject,
974                                              CFDictionaryRef aUserInfo);
975 
976   static void FlushPendingMethods(nsITimer* aTimer, void* aClosure);
977 
978   /**
979    * ConvertToTextRangeStyle converts the given native underline style to
980    * our defined text range type.
981    *
982    * @param aUnderlineStyle       NSUnderlineStyleSingle or
983    *                              NSUnderlineStyleThick.
984    * @param aSelectedRange        Current selected range (or caret position).
985    * @return                      NS_TEXTRANGE_*.
986    */
987   TextRangeType ConvertToTextRangeType(uint32_t aUnderlineStyle,
988                                        NSRange& aSelectedRange);
989 
990   /**
991    * GetRangeCount() computes the range count of aAttrString.
992    *
993    * @param aAttrString           An NSAttributedString instance whose number of
994    *                              NSUnderlineStyleAttributeName ranges you with
995    *                              to know.
996    * @return                      The count of NSUnderlineStyleAttributeName
997    *                              ranges in aAttrString.
998    */
999   uint32_t GetRangeCount(NSAttributedString *aString);
1000 
1001   /**
1002    * CreateTextRangeArray() returns text ranges for clauses and/or caret.
1003    *
1004    * @param aAttrString           An NSAttributedString instance which indicates
1005    *                              current composition string.
1006    * @param aSelectedRange        Current selected range (or caret position).
1007    * @return                      The result is set to the
1008    *                              NSUnderlineStyleAttributeName ranges in
1009    *                              aAttrString.
1010    */
1011   already_AddRefed<mozilla::TextRangeArray>
1012     CreateTextRangeArray(NSAttributedString *aAttrString,
1013                          NSRange& aSelectedRange);
1014 
1015   /**
1016    * DispatchCompositionStartEvent() dispatches a compositionstart event and
1017    * initializes the members indicating composition state.
1018    *
1019    * @return                      true if it can continues handling composition.
1020    *                              Otherwise, e.g., canceled by the web page,
1021    *                              this returns false.
1022    */
1023   bool DispatchCompositionStartEvent();
1024 
1025   /**
1026    * DispatchCompositionChangeEvent() dispatches a compositionchange event on
1027    * mWidget and modifies the members indicating composition state.
1028    *
1029    * @param aText                 User text input.
1030    * @param aAttrString           An NSAttributedString instance which indicates
1031    *                              current composition string.
1032    * @param aSelectedRange        Current selected range (or caret position).
1033    *
1034    * @return                      true if it can continues handling composition.
1035    *                              Otherwise, e.g., canceled by the web page,
1036    *                              this returns false.
1037    */
1038   bool DispatchCompositionChangeEvent(const nsString& aText,
1039                                       NSAttributedString* aAttrString,
1040                                       NSRange& aSelectedRange);
1041 
1042   /**
1043    * DispatchCompositionCommitEvent() dispatches a compositioncommit event or
1044    * compositioncommitasis event.  If aCommitString is null, dispatches
1045    * compositioncommitasis event.  I.e., if aCommitString is null, this
1046    * commits the composition with the last data.  Otherwise, commits the
1047    * composition with aCommitString value.
1048    *
1049    * @return                      true if the widget isn't destroyed.
1050    *                              Otherwise, false.
1051    */
1052   bool DispatchCompositionCommitEvent(const nsAString* aCommitString = nullptr);
1053 
1054   // The focused IME handler.  Please note that the handler might lost the
1055   // actual focus by deactivating the application.  If we are active, this
1056   // must have the actual focused handle.
1057   // We cannot access to the NSInputManager during we aren't active, so, the
1058   // focused handler can have an IME transaction even if we are deactive.
1059   static IMEInputHandler* sFocusedIMEHandler;
1060 
1061   static bool sCachedIsForRTLLangage;
1062 };
1063 
1064 /**
1065  * TextInputHandler implements the NSTextInput protocol.
1066  */
1067 class TextInputHandler : public IMEInputHandler
1068 {
1069 public:
1070   static NSUInteger sLastModifierState;
1071 
1072   static CFArrayRef CreateAllKeyboardLayoutList();
1073   static void DebugPrintAllKeyboardLayouts();
1074 
1075   TextInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView);
1076   virtual ~TextInputHandler();
1077 
1078   /**
1079    * KeyDown event handler.
1080    *
1081    * @param aNativeEvent          A native NSKeyDown event.
1082    * @return                      TRUE if the event is consumed by web contents
1083    *                              or chrome contents.  Otherwise, FALSE.
1084    */
1085   bool HandleKeyDownEvent(NSEvent* aNativeEvent);
1086 
1087   /**
1088    * KeyUp event handler.
1089    *
1090    * @param aNativeEvent          A native NSKeyUp event.
1091    */
1092   void HandleKeyUpEvent(NSEvent* aNativeEvent);
1093 
1094   /**
1095    * FlagsChanged event handler.
1096    *
1097    * @param aNativeEvent          A native NSFlagsChanged event.
1098    */
1099   void HandleFlagsChanged(NSEvent* aNativeEvent);
1100 
1101   /**
1102    * Insert the string to content.  I.e., this is a text input event handler.
1103    * If this is called during keydown event handling, this may dispatch a
1104    * eKeyPress event.  If this is called during composition, this commits
1105    * the composition by the aAttrString.
1106    *
1107    * @param aAttrString           An inserted string.
1108    * @param aReplacementRange     The range which will be replaced with the
1109    *                              aAttrString instead of current selection.
1110    */
1111   void InsertText(NSAttributedString *aAttrString,
1112                   NSRange* aReplacementRange = nullptr);
1113 
1114   /**
1115    * doCommandBySelector event handler.
1116    *
1117    * @param aSelector             A selector of the command.
1118    * @return                      TRUE if the command is consumed.  Otherwise,
1119    *                              FALSE.
1120    */
1121   bool DoCommandBySelector(const char* aSelector);
1122 
1123   /**
1124    * KeyPressWasHandled() checks whether keypress event was handled or not.
1125    *
1126    * @return                      TRUE if keypress event for latest native key
1127    *                              event was handled.  Otherwise, FALSE.
1128    *                              If this handler isn't handling any key events,
1129    *                              always returns FALSE.
1130    */
KeyPressWasHandled()1131   bool KeyPressWasHandled()
1132   {
1133     KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
1134     return currentKeyEvent && currentKeyEvent->mKeyPressHandled;
1135   }
1136 
1137 protected:
1138   // Stores the association of device dependent modifier flags with a modifier
1139   // keyCode.  Being device dependent, this association may differ from one kind
1140   // of hardware to the next.
1141   struct ModifierKey
1142   {
1143     NSUInteger flags;
1144     unsigned short keyCode;
1145 
ModifierKeyModifierKey1146     ModifierKey(NSUInteger aFlags, unsigned short aKeyCode) :
1147       flags(aFlags), keyCode(aKeyCode)
1148     {
1149     }
1150 
GetDeviceDependentFlagsModifierKey1151     NSUInteger GetDeviceDependentFlags() const
1152     {
1153       return (flags & ~NSDeviceIndependentModifierFlagsMask);
1154     }
1155 
GetDeviceIndependentFlagsModifierKey1156     NSUInteger GetDeviceIndependentFlags() const
1157     {
1158       return (flags & NSDeviceIndependentModifierFlagsMask);
1159     }
1160   };
1161   typedef nsTArray<ModifierKey> ModifierKeyArray;
1162   ModifierKeyArray mModifierKeys;
1163 
1164   /**
1165    * GetModifierKeyForNativeKeyCode() returns the stored ModifierKey for
1166    * the key.
1167    */
1168   const ModifierKey*
1169     GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const;
1170 
1171   /**
1172    * GetModifierKeyForDeviceDependentFlags() returns the stored ModifierKey for
1173    * the device dependent flags.
1174    */
1175   const ModifierKey*
1176     GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const;
1177 
1178   /**
1179    * DispatchKeyEventForFlagsChanged() dispatches keydown event or keyup event
1180    * for the aNativeEvent.
1181    *
1182    * @param aNativeEvent          A native flagschanged event which you want to
1183    *                              dispatch our key event for.
1184    * @param aDispatchKeyDown      TRUE if you want to dispatch a keydown event.
1185    *                              Otherwise, i.e., to dispatch keyup event,
1186    *                              FALSE.
1187    */
1188   void DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent,
1189                                        bool aDispatchKeyDown);
1190 };
1191 
1192 } // namespace widget
1193 } // namespace mozilla
1194 
1195 #endif // TextInputHandler_h_
1196