1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #ifndef mozilla_widget_IMEData_h_
7 #define mozilla_widget_IMEData_h_
8 
9 #include "nsPoint.h"
10 #include "nsRect.h"
11 #include "nsStringGlue.h"
12 
13 class nsIWidget;
14 
15 namespace mozilla {
16 
17 class WritingMode;
18 
19 } // namespace mozilla
20 
21 /**
22  * Preference for receiving IME updates
23  *
24  * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
25  * change and/or selection change and call nsIWidget::NotifyIME() with
26  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
27  * Please note that the text change observing cost is very expensive especially
28  * on an HTML editor has focus.
29  * If the IME implementation on a particular platform doesn't care about
30  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
31  * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
32  * If the IME implementation needs notifications even while our process is
33  * deactive, it should also set NOTIFY_DURING_DEACTIVE.
34  */
35 struct nsIMEUpdatePreference final
36 {
37   typedef uint8_t Notifications;
38 
39   enum : Notifications
40   {
41     NOTIFY_NOTHING                       = 0,
42     NOTIFY_TEXT_CHANGE                   = 1 << 1,
43     NOTIFY_POSITION_CHANGE               = 1 << 2,
44     // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
45     // or released on a character in the focused editor.  The notification is
46     // notified to IME as a mouse event.  If it's consumed by IME, NotifyIME()
47     // returns NS_SUCCESS_EVENT_CONSUMED.  Otherwise, it returns NS_OK if it's
48     // handled without any error.
49     NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR    = 1 << 3,
50     // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
51     //       or more compositions are possible.  E.g., Mac and Linux (GTK).
52     NOTIFY_DURING_DEACTIVE               = 1 << 7
53   };
54 
nsIMEUpdatePreferencefinal55   nsIMEUpdatePreference()
56     : mWantUpdates(NOTIFY_NOTHING)
57   {
58   }
59 
nsIMEUpdatePreferencefinal60   explicit nsIMEUpdatePreference(Notifications aWantUpdates)
61     : mWantUpdates(aWantUpdates)
62   {
63   }
64 
65   nsIMEUpdatePreference operator|(const nsIMEUpdatePreference& aOther) const
66   {
67     return nsIMEUpdatePreference(aOther.mWantUpdates | mWantUpdates);
68   }
69 
WantTextChangefinal70   bool WantTextChange() const
71   {
72     return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
73   }
74 
WantPositionChangedfinal75   bool WantPositionChanged() const
76   {
77     return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
78   }
79 
WantChangesfinal80   bool WantChanges() const
81   {
82     return WantTextChange();
83   }
84 
WantMouseButtonEventOnCharfinal85   bool WantMouseButtonEventOnChar() const
86   {
87     return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
88   }
89 
WantDuringDeactivefinal90   bool WantDuringDeactive() const
91   {
92     return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
93   }
94 
95   Notifications mWantUpdates;
96 };
97 
98 /**
99  * Contains IMEStatus plus information about the current
100  * input context that the IME can use as hints if desired.
101  */
102 
103 namespace mozilla {
104 namespace widget {
105 
106 struct IMEState final
107 {
108   /**
109    * IME enabled states, the mEnabled value of
110    * SetInputContext()/GetInputContext() should be one value of following
111    * values.
112    *
113    * WARNING: If you change these values, you also need to edit:
114    *   nsIDOMWindowUtils.idl
115    *   nsContentUtils::GetWidgetStatusFromIMEStatus
116    */
117   enum Enabled
118   {
119     /**
120      * 'Disabled' means the user cannot use IME. So, the IME open state should
121      * be 'closed' during 'disabled'.
122      */
123     DISABLED,
124     /**
125      * 'Enabled' means the user can use IME.
126      */
127     ENABLED,
128     /**
129      * 'Password' state is a special case for the password editors.
130      * E.g., on mac, the password editors should disable the non-Roman
131      * keyboard layouts at getting focus. Thus, the password editor may have
132      * special rules on some platforms.
133      */
134     PASSWORD,
135     /**
136      * This state is used when a plugin is focused.
137      * When a plug-in is focused content, we should send native events
138      * directly. Because we don't process some native events, but they may
139      * be needed by the plug-in.
140      */
141     PLUGIN,
142     /**
143      * 'Unknown' is useful when you cache this enum.  So, this shouldn't be
144      * used with nsIWidget::SetInputContext().
145      */
146     UNKNOWN
147   };
148   Enabled mEnabled;
149 
150   /**
151    * IME open states the mOpen value of SetInputContext() should be one value of
152    * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE.  GetInputContext() should return
153    * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
154    */
155   enum Open
156   {
157     /**
158      * 'Unsupported' means the platform cannot return actual IME open state.
159      * This value is used only by GetInputContext().
160      */
161     OPEN_STATE_NOT_SUPPORTED,
162     /**
163      * 'Don't change' means the widget shouldn't change IME open state when
164      * SetInputContext() is called.
165      */
166     DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED,
167     /**
168      * 'Open' means that IME should compose in its primary language (or latest
169      * input mode except direct ASCII character input mode).  Even if IME is
170      * opened by this value, users should be able to close IME by theirselves.
171      * Web contents can specify this value by |ime-mode: active;|.
172      */
173     OPEN,
174     /**
175      * 'Closed' means that IME shouldn't handle key events (or should handle
176      * as ASCII character inputs on mobile device).  Even if IME is closed by
177      * this value, users should be able to open IME by theirselves.
178      * Web contents can specify this value by |ime-mode: inactive;|.
179      */
180     CLOSED
181   };
182   Open mOpen;
183 
IMEStatefinal184   IMEState()
185     : mEnabled(ENABLED)
186     , mOpen(DONT_CHANGE_OPEN_STATE)
187   {
188   }
189 
190   explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE)
mEnabledfinal191     : mEnabled(aEnabled)
192     , mOpen(aOpen)
193   {
194   }
195 
196   // Returns true if the user can input characters.
197   // This means that a plain text editor, an HTML editor, a password editor or
198   // a plain text editor whose ime-mode is "disabled".
IsEditablefinal199   bool IsEditable() const
200   {
201     return mEnabled == ENABLED || mEnabled == PASSWORD;
202   }
203   // Returns true if the user might be able to input characters.
204   // This means that a plain text editor, an HTML editor, a password editor,
205   // a plain text editor whose ime-mode is "disabled" or a windowless plugin
206   // has focus.
MaybeEditablefinal207   bool MaybeEditable() const
208   {
209     return IsEditable() || mEnabled == PLUGIN;
210   }
211 };
212 
213 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
214 // If there can be only one IME composition in a process, this can be used.
215 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
216   (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
217 
218 struct NativeIMEContext final
219 {
220   // Pointer to native IME context.  Typically this is the result of
221   // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
222   // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
223   uintptr_t mRawNativeIMEContext;
224   // Process ID of the origin of mNativeIMEContext.
225   uint64_t mOriginProcessID;
226 
NativeIMEContextfinal227   NativeIMEContext()
228   {
229     Init(nullptr);
230   }
231 
NativeIMEContextfinal232   explicit NativeIMEContext(nsIWidget* aWidget)
233   {
234     Init(aWidget);
235   }
236 
IsValidfinal237   bool IsValid() const
238   {
239     return mRawNativeIMEContext &&
240            mOriginProcessID != static_cast<uintptr_t>(-1);
241   }
242 
243   void Init(nsIWidget* aWidget);
InitWithRawNativeIMEContextfinal244   void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext)
245   {
246     InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext));
247   }
248   void InitWithRawNativeIMEContext(void* aRawNativeIMEContext);
249 
250   bool operator==(const NativeIMEContext& aOther) const
251   {
252     return mRawNativeIMEContext == aOther.mRawNativeIMEContext &&
253            mOriginProcessID == aOther.mOriginProcessID;
254   }
255   bool operator!=(const NativeIMEContext& aOther) const
256   {
257     return !(*this == aOther);
258   }
259 };
260 
261 struct InputContext final
262 {
InputContextfinal263   InputContext()
264     : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT)
265     , mMayBeIMEUnaware(false)
266   {
267   }
268 
IsPasswordEditorfinal269   bool IsPasswordEditor() const
270   {
271     return mHTMLInputType.LowerCaseEqualsLiteral("password");
272   }
273 
274   IMEState mIMEState;
275 
276   /* The type of the input if the input is a html input field */
277   nsString mHTMLInputType;
278 
279   /* The type of the inputmode */
280   nsString mHTMLInputInputmode;
281 
282   /* A hint for the action that is performed when the input is submitted */
283   nsString mActionHint;
284 
285   /**
286    * mOrigin indicates whether this focus event refers to main or remote
287    * content.
288    */
289   enum Origin
290   {
291     // Adjusting focus of content on the main process
292     ORIGIN_MAIN,
293     // Adjusting focus of content in a remote process
294     ORIGIN_CONTENT
295   };
296   Origin mOrigin;
297 
298   /* True if the webapp may be unaware of IME events such as input event or
299    * composiion events. This enables a key-events-only mode on Android for
300    * compatibility with webapps relying on key listeners. */
301   bool mMayBeIMEUnaware;
302 
IsOriginMainProcessfinal303   bool IsOriginMainProcess() const
304   {
305     return mOrigin == ORIGIN_MAIN;
306   }
307 
IsOriginContentProcessfinal308   bool IsOriginContentProcess() const
309   {
310     return mOrigin == ORIGIN_CONTENT;
311   }
312 
IsOriginCurrentProcessfinal313   bool IsOriginCurrentProcess() const
314   {
315     if (XRE_IsParentProcess()) {
316       return IsOriginMainProcess();
317     }
318     return IsOriginContentProcess();
319   }
320 };
321 
322 struct InputContextAction final
323 {
324   /**
325    * mCause indicates what action causes calling nsIWidget::SetInputContext().
326    * It must be one of following values.
327    */
328   enum Cause
329   {
330     // The cause is unknown but originated from content. Focus might have been
331     // changed by content script.
332     CAUSE_UNKNOWN,
333     // The cause is unknown but originated from chrome. Focus might have been
334     // changed by chrome script.
335     CAUSE_UNKNOWN_CHROME,
336     // The cause is user's keyboard operation.
337     CAUSE_KEY,
338     // The cause is user's mouse operation.
339     CAUSE_MOUSE,
340     // The cause is user's touch operation (implies mouse)
341     CAUSE_TOUCH
342   };
343   Cause mCause;
344 
345   /**
346    * mFocusChange indicates what happened for focus.
347    */
348   enum FocusChange
349   {
350     FOCUS_NOT_CHANGED,
351     // A content got focus.
352     GOT_FOCUS,
353     // Focused content lost focus.
354     LOST_FOCUS,
355     // Menu got pseudo focus that means focused content isn't changed but
356     // keyboard events will be handled by menu.
357     MENU_GOT_PSEUDO_FOCUS,
358     // Menu lost pseudo focus that means focused content will handle keyboard
359     // events.
360     MENU_LOST_PSEUDO_FOCUS
361   };
362   FocusChange mFocusChange;
363 
ContentGotFocusByTrustedCausefinal364   bool ContentGotFocusByTrustedCause() const
365   {
366     return (mFocusChange == GOT_FOCUS &&
367             mCause != CAUSE_UNKNOWN);
368   }
369 
UserMightRequestOpenVKBfinal370   bool UserMightRequestOpenVKB() const
371   {
372     return (mFocusChange == FOCUS_NOT_CHANGED &&
373             (mCause == CAUSE_MOUSE || mCause == CAUSE_TOUCH));
374   }
375 
IsUserActionfinal376   static bool IsUserAction(Cause aCause)
377   {
378     switch (aCause) {
379       case CAUSE_KEY:
380       case CAUSE_MOUSE:
381       case CAUSE_TOUCH:
382         return true;
383       default:
384         return false;
385     }
386   }
387 
InputContextActionfinal388   InputContextAction()
389     : mCause(CAUSE_UNKNOWN)
390     , mFocusChange(FOCUS_NOT_CHANGED)
391   {
392   }
393 
394   explicit InputContextAction(Cause aCause,
395                               FocusChange aFocusChange = FOCUS_NOT_CHANGED)
mCausefinal396     : mCause(aCause)
397     , mFocusChange(aFocusChange)
398   {
399   }
400 };
401 
402 // IMEMessage is shared by IMEStateManager and TextComposition.
403 // Update values in GeckoEditable.java if you make changes here.
404 // XXX Negative values are used in Android...
405 typedef int8_t IMEMessageType;
406 enum IMEMessage : IMEMessageType
407 {
408   // This is used by IMENotification internally.  This means that the instance
409   // hasn't been initialized yet.
410   NOTIFY_IME_OF_NOTHING,
411   // An editable content is getting focus
412   NOTIFY_IME_OF_FOCUS,
413   // An editable content is losing focus
414   NOTIFY_IME_OF_BLUR,
415   // Selection in the focused editable content is changed
416   NOTIFY_IME_OF_SELECTION_CHANGE,
417   // Text in the focused editable content is changed
418   NOTIFY_IME_OF_TEXT_CHANGE,
419   // Notified when a dispatched composition event is handled by the
420   // contents.  This must be notified after the other notifications.
421   // Note that if a remote process has focus, this is notified only once when
422   // all dispatched events are handled completely.  So, the receiver shouldn't
423   // count number of received this notification for comparing with the number
424   // of dispatched events.
425   // NOTE: If a composition event causes moving focus from the focused editor,
426   //       this notification may not be notified as usual.  Even in such case,
427   //       NOTIFY_IME_OF_BLUR is always sent.  So, notification listeners
428   //       should tread the blur notification as including this if there is
429   //       pending composition events.
430   NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
431   // Position or size of focused element may be changed.
432   NOTIFY_IME_OF_POSITION_CHANGE,
433   // Mouse button event is fired on a character in focused editor
434   NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
435   // Request to commit current composition to IME
436   // (some platforms may not support)
437   REQUEST_TO_COMMIT_COMPOSITION,
438   // Request to cancel current composition to IME
439   // (some platforms may not support)
440   REQUEST_TO_CANCEL_COMPOSITION
441 };
442 
443 // FYI: Implemented in nsBaseWidget.cpp
444 const char* ToChar(IMEMessage aIMEMessage);
445 
446 struct IMENotification final
447 {
IMENotificationfinal448   IMENotification()
449     : mMessage(NOTIFY_IME_OF_NOTHING)
450   {
451   }
452 
IMENotificationfinal453   IMENotification(const IMENotification& aOther)
454     : mMessage(NOTIFY_IME_OF_NOTHING)
455   {
456     Assign(aOther);
457   }
458 
~IMENotificationfinal459   ~IMENotification()
460   {
461     Clear();
462   }
463 
IMENotificationfinal464   MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
465     : mMessage(aMessage)
466   {
467     switch (aMessage) {
468       case NOTIFY_IME_OF_SELECTION_CHANGE:
469         mSelectionChangeData.mString = new nsString();
470         mSelectionChangeData.Clear();
471         break;
472       case NOTIFY_IME_OF_TEXT_CHANGE:
473         mTextChangeData.Clear();
474         break;
475       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
476         mMouseButtonEventData.mEventMessage = eVoidEvent;
477         mMouseButtonEventData.mOffset = UINT32_MAX;
478         mMouseButtonEventData.mCursorPos.Set(nsIntPoint(0, 0));
479         mMouseButtonEventData.mCharRect.Set(nsIntRect(0, 0, 0, 0));
480         mMouseButtonEventData.mButton = -1;
481         mMouseButtonEventData.mButtons = 0;
482         mMouseButtonEventData.mModifiers = 0;
483         break;
484       default:
485         break;
486     }
487   }
488 
Assignfinal489   void Assign(const IMENotification& aOther)
490   {
491     bool changingMessage = mMessage != aOther.mMessage;
492     if (changingMessage) {
493       Clear();
494       mMessage = aOther.mMessage;
495     }
496     switch (mMessage) {
497       case NOTIFY_IME_OF_SELECTION_CHANGE:
498         if (changingMessage) {
499           mSelectionChangeData.mString = new nsString();
500         }
501         mSelectionChangeData.Assign(aOther.mSelectionChangeData);
502         break;
503       case NOTIFY_IME_OF_TEXT_CHANGE:
504         mTextChangeData = aOther.mTextChangeData;
505         break;
506       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
507         mMouseButtonEventData = aOther.mMouseButtonEventData;
508         break;
509       default:
510         break;
511     }
512   }
513 
514   IMENotification& operator=(const IMENotification& aOther)
515   {
516     Assign(aOther);
517     return *this;
518   }
519 
Clearfinal520   void Clear()
521   {
522     if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
523       MOZ_ASSERT(mSelectionChangeData.mString);
524       delete mSelectionChangeData.mString;
525       mSelectionChangeData.mString = nullptr;
526     }
527     mMessage = NOTIFY_IME_OF_NOTHING;
528   }
529 
HasNotificationfinal530   bool HasNotification() const
531   {
532     return mMessage != NOTIFY_IME_OF_NOTHING;
533   }
534 
MergeWithfinal535   void MergeWith(const IMENotification& aNotification)
536   {
537     switch (mMessage) {
538       case NOTIFY_IME_OF_NOTHING:
539         MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
540         Assign(aNotification);
541         break;
542       case NOTIFY_IME_OF_SELECTION_CHANGE:
543         MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
544         mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
545         break;
546       case NOTIFY_IME_OF_TEXT_CHANGE:
547         MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
548         mTextChangeData += aNotification.mTextChangeData;
549         break;
550       case NOTIFY_IME_OF_POSITION_CHANGE:
551       case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
552         MOZ_ASSERT(aNotification.mMessage == mMessage);
553         break;
554       default:
555         MOZ_CRASH("Merging notification isn't supported");
556         break;
557     }
558   }
559 
560   IMEMessage mMessage;
561 
562   struct Point
563   {
564     int32_t mX;
565     int32_t mY;
566 
Setfinal::Point567     void Set(const nsIntPoint& aPoint)
568     {
569       mX = aPoint.x;
570       mY = aPoint.y;
571     }
AsIntPointfinal::Point572     nsIntPoint AsIntPoint() const
573     {
574       return nsIntPoint(mX, mY);
575     }
576   };
577 
578   struct Rect
579   {
580     int32_t mX;
581     int32_t mY;
582     int32_t mWidth;
583     int32_t mHeight;
584 
Setfinal::Rect585     void Set(const nsIntRect& aRect)
586     {
587       mX = aRect.x;
588       mY = aRect.y;
589       mWidth = aRect.width;
590       mHeight = aRect.height;
591     }
AsIntRectfinal::Rect592     nsIntRect AsIntRect() const
593     {
594       return nsIntRect(mX, mY, mWidth, mHeight);
595     }
596   };
597 
598   // NOTIFY_IME_OF_SELECTION_CHANGE specific data
599   struct SelectionChangeDataBase
600   {
601     // Selection range.
602     uint32_t mOffset;
603 
604     // Selected string
605     nsString* mString;
606 
607     // Writing mode at the selection.
608     uint8_t mWritingMode;
609 
610     bool mReversed;
611     bool mCausedByComposition;
612     bool mCausedBySelectionEvent;
613     bool mOccurredDuringComposition;
614 
615     void SetWritingMode(const WritingMode& aWritingMode);
616     WritingMode GetWritingMode() const;
617 
StartOffsetfinal::SelectionChangeDataBase618     uint32_t StartOffset() const
619     {
620       return mOffset + (mReversed ? Length() : 0);
621     }
EndOffsetfinal::SelectionChangeDataBase622     uint32_t EndOffset() const
623     {
624       return mOffset + (mReversed ? 0 : Length());
625     }
Stringfinal::SelectionChangeDataBase626     const nsString& String() const
627     {
628       return *mString;
629     }
Lengthfinal::SelectionChangeDataBase630     uint32_t Length() const
631     {
632       return mString->Length();
633     }
IsInInt32Rangefinal::SelectionChangeDataBase634     bool IsInInt32Range() const
635     {
636       return mOffset + Length() <= INT32_MAX;
637     }
IsCollapsedfinal::SelectionChangeDataBase638     bool IsCollapsed() const
639     {
640       return mString->IsEmpty();
641     }
ClearSelectionDatafinal::SelectionChangeDataBase642     void ClearSelectionData()
643     {
644       mOffset = UINT32_MAX;
645       mString->Truncate();
646       mWritingMode = 0;
647       mReversed = false;
648     }
Clearfinal::SelectionChangeDataBase649     void Clear()
650     {
651       ClearSelectionData();
652       mCausedByComposition = false;
653       mCausedBySelectionEvent = false;
654       mOccurredDuringComposition = false;
655     }
IsValidfinal::SelectionChangeDataBase656     bool IsValid() const
657     {
658       return mOffset != UINT32_MAX;
659     }
Assignfinal::SelectionChangeDataBase660     void Assign(const SelectionChangeDataBase& aOther)
661     {
662       mOffset = aOther.mOffset;
663       *mString = aOther.String();
664       mWritingMode = aOther.mWritingMode;
665       mReversed = aOther.mReversed;
666       AssignReason(aOther.mCausedByComposition,
667                    aOther.mCausedBySelectionEvent,
668                    aOther.mOccurredDuringComposition);
669     }
AssignReasonfinal::SelectionChangeDataBase670     void AssignReason(bool aCausedByComposition,
671                       bool aCausedBySelectionEvent,
672                       bool aOccurredDuringComposition)
673     {
674       mCausedByComposition = aCausedByComposition;
675       mCausedBySelectionEvent = aCausedBySelectionEvent;
676       mOccurredDuringComposition = aOccurredDuringComposition;
677     }
678   };
679 
680   // SelectionChangeDataBase cannot have constructors because it's used in
681   // the union.  Therefore, SelectionChangeData should only implement
682   // constructors.  In other words, add other members to
683   // SelectionChangeDataBase.
684   struct SelectionChangeData final : public SelectionChangeDataBase
685   {
SelectionChangeDatafinal::final686     SelectionChangeData()
687     {
688       mString = &mStringInstance;
689       Clear();
690     }
SelectionChangeDatafinal::final691     explicit SelectionChangeData(const SelectionChangeDataBase& aOther)
692     {
693       mString = &mStringInstance;
694       Assign(aOther);
695     }
SelectionChangeDatafinal::final696     SelectionChangeData(const SelectionChangeData& aOther)
697     {
698       mString = &mStringInstance;
699       Assign(aOther);
700     }
701     SelectionChangeData& operator=(const SelectionChangeDataBase& aOther)
702     {
703       mString = &mStringInstance;
704       Assign(aOther);
705       return *this;
706     }
707     SelectionChangeData& operator=(const SelectionChangeData& aOther)
708     {
709       mString = &mStringInstance;
710       Assign(aOther);
711       return *this;
712     }
713 
714   private:
715     // When SelectionChangeData is used outside of union, it shouldn't create
716     // nsString instance in the heap as far as possible.
717     nsString mStringInstance;
718   };
719 
720   struct TextChangeDataBase
721   {
722     // mStartOffset is the start offset of modified or removed text in
723     // original content and inserted text in new content.
724     uint32_t mStartOffset;
725     // mRemovalEndOffset is the end offset of modified or removed text in
726     // original content.  If the value is same as mStartOffset, no text hasn't
727     // been removed yet.
728     uint32_t mRemovedEndOffset;
729     // mAddedEndOffset is the end offset of inserted text or same as
730     // mStartOffset if just removed.  The vlaue is offset in the new content.
731     uint32_t mAddedEndOffset;
732 
733     // Note that TextChangeDataBase may be the result of merging two or more
734     // changes especially in e10s mode.
735 
736     // mCausedOnlyByComposition is true only when *all* merged changes are
737     // caused by composition.
738     bool mCausedOnlyByComposition;
739     // mIncludingChangesDuringComposition is true if at least one change which
740     // is not caused by composition occurred during the last composition.
741     // Note that if after the last composition is finished and there are some
742     // changes not caused by composition, this is set to false.
743     bool mIncludingChangesDuringComposition;
744     // mIncludingChangesWithoutComposition is true if there is at least one
745     // change which did occur when there wasn't a composition ongoing.
746     bool mIncludingChangesWithoutComposition;
747 
OldLengthfinal::TextChangeDataBase748     uint32_t OldLength() const
749     {
750       MOZ_ASSERT(IsValid());
751       return mRemovedEndOffset - mStartOffset;
752     }
NewLengthfinal::TextChangeDataBase753     uint32_t NewLength() const
754     {
755       MOZ_ASSERT(IsValid());
756       return mAddedEndOffset - mStartOffset;
757     }
758 
759     // Positive if text is added. Negative if text is removed.
Differencefinal::TextChangeDataBase760     int64_t Difference() const
761     {
762       return mAddedEndOffset - mRemovedEndOffset;
763     }
764 
IsInInt32Rangefinal::TextChangeDataBase765     bool IsInInt32Range() const
766     {
767       MOZ_ASSERT(IsValid());
768       return mStartOffset <= INT32_MAX &&
769              mRemovedEndOffset <= INT32_MAX &&
770              mAddedEndOffset <= INT32_MAX;
771     }
772 
IsValidfinal::TextChangeDataBase773     bool IsValid() const
774     {
775       return !(mStartOffset == UINT32_MAX &&
776                !mRemovedEndOffset && !mAddedEndOffset);
777     }
778 
Clearfinal::TextChangeDataBase779     void Clear()
780     {
781       mStartOffset = UINT32_MAX;
782       mRemovedEndOffset = mAddedEndOffset = 0;
783     }
784 
785     void MergeWith(const TextChangeDataBase& aOther);
786     TextChangeDataBase& operator+=(const TextChangeDataBase& aOther)
787     {
788       MergeWith(aOther);
789       return *this;
790     }
791 
792 #ifdef DEBUG
793     void Test();
794 #endif // #ifdef DEBUG
795   };
796 
797   // TextChangeDataBase cannot have constructors because they are used in union.
798   // Therefore, TextChangeData should only implement constructor.  In other
799   // words, add other members to TextChangeDataBase.
800   struct TextChangeData : public TextChangeDataBase
801   {
TextChangeDatafinal::TextChangeData802     TextChangeData() { Clear(); }
803 
TextChangeDatafinal::TextChangeData804     TextChangeData(uint32_t aStartOffset,
805                    uint32_t aRemovedEndOffset,
806                    uint32_t aAddedEndOffset,
807                    bool aCausedByComposition,
808                    bool aOccurredDuringComposition)
809     {
810       MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
811                  "removed end offset must not be smaller than start offset");
812       MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
813                  "added end offset must not be smaller than start offset");
814       mStartOffset = aStartOffset;
815       mRemovedEndOffset = aRemovedEndOffset;
816       mAddedEndOffset = aAddedEndOffset;
817       mCausedOnlyByComposition = aCausedByComposition;
818       mIncludingChangesDuringComposition =
819         !aCausedByComposition && aOccurredDuringComposition;
820       mIncludingChangesWithoutComposition =
821         !aCausedByComposition && !aOccurredDuringComposition;
822     }
823   };
824 
825   struct MouseButtonEventData
826   {
827     // The value of WidgetEvent::mMessage
828     EventMessage mEventMessage;
829     // Character offset from the start of the focused editor under the cursor
830     uint32_t mOffset;
831     // Cursor position in pixels relative to the widget
832     Point mCursorPos;
833     // Character rect in pixels under the cursor relative to the widget
834     Rect mCharRect;
835     // The value of WidgetMouseEventBase::button and buttons
836     int16_t mButton;
837     int16_t mButtons;
838     // The value of WidgetInputEvent::modifiers
839     Modifiers mModifiers;
840   };
841 
842   union
843   {
844     // NOTIFY_IME_OF_SELECTION_CHANGE specific data
845     SelectionChangeDataBase mSelectionChangeData;
846 
847     // NOTIFY_IME_OF_TEXT_CHANGE specific data
848     TextChangeDataBase mTextChangeData;
849 
850     // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
851     MouseButtonEventData mMouseButtonEventData;
852   };
853 
SetDatafinal854   void SetData(const SelectionChangeDataBase& aSelectionChangeData)
855   {
856     MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
857     mSelectionChangeData.Assign(aSelectionChangeData);
858   }
859 
SetDatafinal860   void SetData(const TextChangeDataBase& aTextChangeData)
861   {
862     MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
863     mTextChangeData = aTextChangeData;
864   }
865 };
866 
867 struct CandidateWindowPosition
868 {
869   // Upper left corner of the candidate window if mExcludeRect is false.
870   // Otherwise, the position currently interested.  E.g., caret position.
871   LayoutDeviceIntPoint mPoint;
872   // Rect which shouldn't be overlapped with the candidate window.
873   // This is valid only when mExcludeRect is true.
874   LayoutDeviceIntRect mRect;
875   // See explanation of mPoint and mRect.
876   bool mExcludeRect;
877 };
878 
879 } // namespace widget
880 } // namespace mozilla
881 
882 #endif // #ifndef mozilla_widget_IMEData_h_
883