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 "nsString.h"
12 #include "nsXULAppAPI.h"
13 #include "Units.h"
14 
15 class nsIWidget;
16 
17 namespace mozilla {
18 
19 class WritingMode;
20 
21 namespace widget {
22 
23 /**
24  * Preference for receiving IME updates
25  *
26  * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
27  * change and/or selection change and call nsIWidget::NotifyIME() with
28  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
29  * Please note that the text change observing cost is very expensive especially
30  * on an HTML editor has focus.
31  * If the IME implementation on a particular platform doesn't care about
32  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
33  * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
34  * If the IME implementation needs notifications even while our process is
35  * deactive, it should also set NOTIFY_DURING_DEACTIVE.
36  */
37 struct IMENotificationRequests final {
38   typedef uint8_t Notifications;
39 
40   enum : Notifications {
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     NOTIFY_ALL = NOTIFY_TEXT_CHANGE | NOTIFY_POSITION_CHANGE |
55                  NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR,
56   };
57 
IMENotificationRequestsfinal58   IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING) {}
59 
IMENotificationRequestsfinal60   explicit IMENotificationRequests(Notifications aWantUpdates)
61       : mWantUpdates(aWantUpdates) {}
62 
63   IMENotificationRequests operator|(
64       const IMENotificationRequests& aOther) const {
65     return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates);
66   }
67   IMENotificationRequests& operator|=(const IMENotificationRequests& aOther) {
68     mWantUpdates |= aOther.mWantUpdates;
69     return *this;
70   }
71   bool operator==(const IMENotificationRequests& aOther) const {
72     return mWantUpdates == aOther.mWantUpdates;
73   }
74 
WantTextChangefinal75   bool WantTextChange() const { return !!(mWantUpdates & NOTIFY_TEXT_CHANGE); }
76 
WantPositionChangedfinal77   bool WantPositionChanged() const {
78     return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
79   }
80 
WantChangesfinal81   bool WantChanges() const { return WantTextChange(); }
82 
WantMouseButtonEventOnCharfinal83   bool WantMouseButtonEventOnChar() const {
84     return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
85   }
86 
WantDuringDeactivefinal87   bool WantDuringDeactive() const {
88     return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
89   }
90 
91   Notifications mWantUpdates;
92 };
93 
94 /**
95  * Contains IMEStatus plus information about the current
96  * input context that the IME can use as hints if desired.
97  */
98 
99 struct IMEState final {
100   /**
101    * IME enabled states, the mEnabled value of
102    * SetInputContext()/GetInputContext() should be one value of following
103    * values.
104    *
105    * WARNING: If you change these values, you also need to edit:
106    *   nsIDOMWindowUtils.idl
107    *   nsContentUtils::GetWidgetStatusFromIMEStatus
108    */
109   enum Enabled {
110     /**
111      * 'Disabled' means the user cannot use IME. So, the IME open state should
112      * be 'closed' during 'disabled'.
113      */
114     DISABLED,
115     /**
116      * 'Enabled' means the user can use IME.
117      */
118     ENABLED,
119     /**
120      * 'Password' state is a special case for the password editors.
121      * E.g., on mac, the password editors should disable the non-Roman
122      * keyboard layouts at getting focus. Thus, the password editor may have
123      * special rules on some platforms.
124      */
125     PASSWORD,
126     /**
127      * This state is used when a plugin is focused.
128      * When a plug-in is focused content, we should send native events
129      * directly. Because we don't process some native events, but they may
130      * be needed by the plug-in.
131      */
132     PLUGIN,
133     /**
134      * 'Unknown' is useful when you cache this enum.  So, this shouldn't be
135      * used with nsIWidget::SetInputContext().
136      */
137     UNKNOWN
138   };
139   Enabled mEnabled;
140 
141   /**
142    * IME open states the mOpen value of SetInputContext() should be one value of
143    * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE.  GetInputContext() should return
144    * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
145    */
146   enum Open {
147     /**
148      * 'Unsupported' means the platform cannot return actual IME open state.
149      * This value is used only by GetInputContext().
150      */
151     OPEN_STATE_NOT_SUPPORTED,
152     /**
153      * 'Don't change' means the widget shouldn't change IME open state when
154      * SetInputContext() is called.
155      */
156     DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED,
157     /**
158      * 'Open' means that IME should compose in its primary language (or latest
159      * input mode except direct ASCII character input mode).  Even if IME is
160      * opened by this value, users should be able to close IME by theirselves.
161      * Web contents can specify this value by |ime-mode: active;|.
162      */
163     OPEN,
164     /**
165      * 'Closed' means that IME shouldn't handle key events (or should handle
166      * as ASCII character inputs on mobile device).  Even if IME is closed by
167      * this value, users should be able to open IME by theirselves.
168      * Web contents can specify this value by |ime-mode: inactive;|.
169      */
170     CLOSED
171   };
172   Open mOpen;
173 
IMEStatefinal174   IMEState() : mEnabled(ENABLED), mOpen(DONT_CHANGE_OPEN_STATE) {}
175 
176   explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE)
mEnabledfinal177       : mEnabled(aEnabled), mOpen(aOpen) {}
178 
179   // Returns true if the user can input characters.
180   // This means that a plain text editor, an HTML editor, a password editor or
181   // a plain text editor whose ime-mode is "disabled".
IsEditablefinal182   bool IsEditable() const {
183     return mEnabled == ENABLED || mEnabled == PASSWORD;
184   }
185   // Returns true if the user might be able to input characters.
186   // This means that a plain text editor, an HTML editor, a password editor,
187   // a plain text editor whose ime-mode is "disabled" or a windowless plugin
188   // has focus.
MaybeEditablefinal189   bool MaybeEditable() const { return IsEditable() || mEnabled == PLUGIN; }
190 };
191 
192 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
193 // If there can be only one IME composition in a process, this can be used.
194 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
195   (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
196 
197 struct NativeIMEContext final {
198   // Pointer to native IME context.  Typically this is the result of
199   // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
200   // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
201   uintptr_t mRawNativeIMEContext;
202   // Process ID of the origin of mNativeIMEContext.
203   uint64_t mOriginProcessID;
204 
NativeIMEContextfinal205   NativeIMEContext() { Init(nullptr); }
206 
NativeIMEContextfinal207   explicit NativeIMEContext(nsIWidget* aWidget) { Init(aWidget); }
208 
IsValidfinal209   bool IsValid() const {
210     return mRawNativeIMEContext &&
211            mOriginProcessID != static_cast<uintptr_t>(-1);
212   }
213 
214   void Init(nsIWidget* aWidget);
InitWithRawNativeIMEContextfinal215   void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext) {
216     InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext));
217   }
218   void InitWithRawNativeIMEContext(void* aRawNativeIMEContext);
219 
220   bool operator==(const NativeIMEContext& aOther) const {
221     return mRawNativeIMEContext == aOther.mRawNativeIMEContext &&
222            mOriginProcessID == aOther.mOriginProcessID;
223   }
224   bool operator!=(const NativeIMEContext& aOther) const {
225     return !(*this == aOther);
226   }
227 };
228 
229 struct InputContext final {
InputContextfinal230   InputContext()
231       : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT),
232         mMayBeIMEUnaware(false),
233         mHasHandledUserInput(false),
234         mInPrivateBrowsing(false) {}
235 
236   // If InputContext instance is a static variable, any heap allocated stuff
237   // of its members need to be deleted at XPCOM shutdown.  Otherwise, it's
238   // detected as memory leak.
ShutDownfinal239   void ShutDown() {
240     // The buffer for nsString will be released with a call of SetCapacity(0).
241     // Truncate() isn't enough because it just sets length to 0.
242     mHTMLInputType.SetCapacity(0);
243     mHTMLInputInputmode.SetCapacity(0);
244     mActionHint.SetCapacity(0);
245   }
246 
IsPasswordEditorfinal247   bool IsPasswordEditor() const {
248     return mHTMLInputType.LowerCaseEqualsLiteral("password");
249   }
250 
251   IMEState mIMEState;
252 
253   /* The type of the input if the input is a html input field */
254   nsString mHTMLInputType;
255 
256   /* The type of the inputmode */
257   nsString mHTMLInputInputmode;
258 
259   /* A hint for the action that is performed when the input is submitted */
260   nsString mActionHint;
261 
262   /**
263    * mOrigin indicates whether this focus event refers to main or remote
264    * content.
265    */
266   enum Origin {
267     // Adjusting focus of content on the main process
268     ORIGIN_MAIN,
269     // Adjusting focus of content in a remote process
270     ORIGIN_CONTENT
271   };
272   Origin mOrigin;
273 
274   /* True if the webapp may be unaware of IME events such as input event or
275    * composiion events. This enables a key-events-only mode on Android for
276    * compatibility with webapps relying on key listeners. */
277   bool mMayBeIMEUnaware;
278 
279   /**
280    * True if the document has ever received user input
281    */
282   bool mHasHandledUserInput;
283 
284   /* Whether the owning document of the input element has been loaded
285    * in private browsing mode. */
286   bool mInPrivateBrowsing;
287 
IsOriginMainProcessfinal288   bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; }
289 
IsOriginContentProcessfinal290   bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; }
291 
IsOriginCurrentProcessfinal292   bool IsOriginCurrentProcess() const {
293     if (XRE_IsParentProcess()) {
294       return IsOriginMainProcess();
295     }
296     return IsOriginContentProcess();
297   }
298 };
299 
300 // FYI: Implemented in nsBaseWidget.cpp
301 const char* ToChar(InputContext::Origin aOrigin);
302 
303 struct InputContextAction final {
304   /**
305    * mCause indicates what action causes calling nsIWidget::SetInputContext().
306    * It must be one of following values.
307    */
308   enum Cause {
309     // The cause is unknown but originated from content. Focus might have been
310     // changed by content script.
311     CAUSE_UNKNOWN,
312     // The cause is unknown but originated from chrome. Focus might have been
313     // changed by chrome script.
314     CAUSE_UNKNOWN_CHROME,
315     // The cause is user's keyboard operation.
316     CAUSE_KEY,
317     // The cause is user's mouse operation.
318     CAUSE_MOUSE,
319     // The cause is user's touch operation (implies mouse)
320     CAUSE_TOUCH,
321     // The cause is unknown but it occurs during user input except keyboard
322     // input.  E.g., an event handler of a user input event moves focus.
323     CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
324     // The cause is unknown but it occurs during keyboard input.
325     CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
326   };
327   Cause mCause;
328 
329   /**
330    * mFocusChange indicates what happened for focus.
331    */
332   enum FocusChange {
333     FOCUS_NOT_CHANGED,
334     // A content got focus.
335     GOT_FOCUS,
336     // Focused content lost focus.
337     LOST_FOCUS,
338     // Menu got pseudo focus that means focused content isn't changed but
339     // keyboard events will be handled by menu.
340     MENU_GOT_PSEUDO_FOCUS,
341     // Menu lost pseudo focus that means focused content will handle keyboard
342     // events.
343     MENU_LOST_PSEUDO_FOCUS,
344     // The widget is created.  When a widget is crated, it may need to notify
345     // IME module to initialize its native IME context.  In such case, this is
346     // used.  I.e., this isn't used by IMEStateManager.
347     WIDGET_CREATED
348   };
349   FocusChange mFocusChange;
350 
ContentGotFocusByTrustedCausefinal351   bool ContentGotFocusByTrustedCause() const {
352     return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN);
353   }
354 
UserMightRequestOpenVKBfinal355   bool UserMightRequestOpenVKB() const {
356     // If focus is changed, user must not request to open VKB.
357     if (mFocusChange != FOCUS_NOT_CHANGED) {
358       return false;
359     }
360     switch (mCause) {
361       // If user clicks or touches focused editor, user must request to open
362       // VKB.
363       case CAUSE_MOUSE:
364       case CAUSE_TOUCH:
365       // If script does something during a user input and that causes changing
366       // input context, user might request to open VKB.  E.g., user clicks
367       // dummy editor and JS moves focus to an actual editable node.  However,
368       // this should return false if the user input is a keyboard event since
369       // physical keyboard operation shouldn't cause opening VKB.
370       case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
371         return true;
372       default:
373         return false;
374     }
375   }
376 
377   /**
378    * IsHandlingUserInput() returns true if it's caused by a user action directly
379    * or it's caused by script or something but it occurred while we're handling
380    * a user action.  E.g., when it's caused by Element.focus() in an event
381    * handler of a user input, this returns true.
382    */
IsHandlingUserInputfinal383   static bool IsHandlingUserInput(Cause aCause) {
384     switch (aCause) {
385       case CAUSE_KEY:
386       case CAUSE_MOUSE:
387       case CAUSE_TOUCH:
388       case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
389       case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
390         return true;
391       default:
392         return false;
393     }
394   }
395 
IsHandlingUserInputfinal396   bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); }
397 
InputContextActionfinal398   InputContextAction()
399       : mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED) {}
400 
401   explicit InputContextAction(Cause aCause,
402                               FocusChange aFocusChange = FOCUS_NOT_CHANGED)
mCausefinal403       : mCause(aCause), mFocusChange(aFocusChange) {}
404 };
405 
406 // IMEMessage is shared by IMEStateManager and TextComposition.
407 // Update values in GeckoEditable.java if you make changes here.
408 // XXX Negative values are used in Android...
409 typedef int8_t IMEMessageType;
410 enum IMEMessage : IMEMessageType {
411   // This is used by IMENotification internally.  This means that the instance
412   // hasn't been initialized yet.
413   NOTIFY_IME_OF_NOTHING,
414   // An editable content is getting focus
415   NOTIFY_IME_OF_FOCUS,
416   // An editable content is losing focus
417   NOTIFY_IME_OF_BLUR,
418   // Selection in the focused editable content is changed
419   NOTIFY_IME_OF_SELECTION_CHANGE,
420   // Text in the focused editable content is changed
421   NOTIFY_IME_OF_TEXT_CHANGE,
422   // Notified when a dispatched composition event is handled by the
423   // contents.  This must be notified after the other notifications.
424   // Note that if a remote process has focus, this is notified only once when
425   // all dispatched events are handled completely.  So, the receiver shouldn't
426   // count number of received this notification for comparing with the number
427   // of dispatched events.
428   // NOTE: If a composition event causes moving focus from the focused editor,
429   //       this notification may not be notified as usual.  Even in such case,
430   //       NOTIFY_IME_OF_BLUR is always sent.  So, notification listeners
431   //       should tread the blur notification as including this if there is
432   //       pending composition events.
433   NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
434   // Position or size of focused element may be changed.
435   NOTIFY_IME_OF_POSITION_CHANGE,
436   // Mouse button event is fired on a character in focused editor
437   NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
438   // Request to commit current composition to IME
439   // (some platforms may not support)
440   REQUEST_TO_COMMIT_COMPOSITION,
441   // Request to cancel current composition to IME
442   // (some platforms may not support)
443   REQUEST_TO_CANCEL_COMPOSITION
444 };
445 
446 // FYI: Implemented in nsBaseWidget.cpp
447 const char* ToChar(IMEMessage aIMEMessage);
448 
449 struct IMENotification final {
IMENotificationfinal450   IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING) {}
451 
IMENotificationfinal452   IMENotification(const IMENotification& aOther)
453       : mMessage(NOTIFY_IME_OF_NOTHING) {
454     Assign(aOther);
455   }
456 
~IMENotificationfinal457   ~IMENotification() { Clear(); }
458 
IMENotificationfinal459   MOZ_IMPLICIT IMENotification(IMEMessage aMessage) : mMessage(aMessage) {
460     switch (aMessage) {
461       case NOTIFY_IME_OF_SELECTION_CHANGE:
462         mSelectionChangeData.mString = new nsString();
463         mSelectionChangeData.Clear();
464         break;
465       case NOTIFY_IME_OF_TEXT_CHANGE:
466         mTextChangeData.Clear();
467         break;
468       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
469         mMouseButtonEventData.mEventMessage = eVoidEvent;
470         mMouseButtonEventData.mOffset = UINT32_MAX;
471         mMouseButtonEventData.mCursorPos.Set(nsIntPoint(0, 0));
472         mMouseButtonEventData.mCharRect.Set(nsIntRect(0, 0, 0, 0));
473         mMouseButtonEventData.mButton = -1;
474         mMouseButtonEventData.mButtons = 0;
475         mMouseButtonEventData.mModifiers = 0;
476         break;
477       default:
478         break;
479     }
480   }
481 
Assignfinal482   void Assign(const IMENotification& aOther) {
483     bool changingMessage = mMessage != aOther.mMessage;
484     if (changingMessage) {
485       Clear();
486       mMessage = aOther.mMessage;
487     }
488     switch (mMessage) {
489       case NOTIFY_IME_OF_SELECTION_CHANGE:
490         if (changingMessage) {
491           mSelectionChangeData.mString = new nsString();
492         }
493         mSelectionChangeData.Assign(aOther.mSelectionChangeData);
494         break;
495       case NOTIFY_IME_OF_TEXT_CHANGE:
496         mTextChangeData = aOther.mTextChangeData;
497         break;
498       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
499         mMouseButtonEventData = aOther.mMouseButtonEventData;
500         break;
501       default:
502         break;
503     }
504   }
505 
506   IMENotification& operator=(const IMENotification& aOther) {
507     Assign(aOther);
508     return *this;
509   }
510 
Clearfinal511   void Clear() {
512     if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
513       MOZ_ASSERT(mSelectionChangeData.mString);
514       delete mSelectionChangeData.mString;
515       mSelectionChangeData.mString = nullptr;
516     }
517     mMessage = NOTIFY_IME_OF_NOTHING;
518   }
519 
HasNotificationfinal520   bool HasNotification() const { return mMessage != NOTIFY_IME_OF_NOTHING; }
521 
MergeWithfinal522   void MergeWith(const IMENotification& aNotification) {
523     switch (mMessage) {
524       case NOTIFY_IME_OF_NOTHING:
525         MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
526         Assign(aNotification);
527         break;
528       case NOTIFY_IME_OF_SELECTION_CHANGE:
529         MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
530         mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
531         break;
532       case NOTIFY_IME_OF_TEXT_CHANGE:
533         MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
534         mTextChangeData += aNotification.mTextChangeData;
535         break;
536       case NOTIFY_IME_OF_POSITION_CHANGE:
537       case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
538         MOZ_ASSERT(aNotification.mMessage == mMessage);
539         break;
540       default:
541         MOZ_CRASH("Merging notification isn't supported");
542         break;
543     }
544   }
545 
546   IMEMessage mMessage;
547 
548   struct Point {
549     int32_t mX;
550     int32_t mY;
551 
Setfinal::Point552     void Set(const nsIntPoint& aPoint) {
553       mX = aPoint.x;
554       mY = aPoint.y;
555     }
AsIntPointfinal::Point556     nsIntPoint AsIntPoint() const { return nsIntPoint(mX, mY); }
557   };
558 
559   struct Rect {
560     int32_t mX;
561     int32_t mY;
562     int32_t mWidth;
563     int32_t mHeight;
564 
Setfinal::Rect565     void Set(const nsIntRect& aRect) {
566       aRect.GetRect(&mX, &mY, &mWidth, &mHeight);
567     }
AsIntRectfinal::Rect568     nsIntRect AsIntRect() const { return nsIntRect(mX, mY, mWidth, mHeight); }
569   };
570 
571   // NOTIFY_IME_OF_SELECTION_CHANGE specific data
572   struct SelectionChangeDataBase {
573     // Selection range.
574     uint32_t mOffset;
575 
576     // Selected string
577     nsString* mString;
578 
579     // Writing mode at the selection.
580     uint8_t mWritingMode;
581 
582     bool mReversed;
583     bool mCausedByComposition;
584     bool mCausedBySelectionEvent;
585     bool mOccurredDuringComposition;
586 
587     void SetWritingMode(const WritingMode& aWritingMode);
588     WritingMode GetWritingMode() const;
589 
StartOffsetfinal::SelectionChangeDataBase590     uint32_t StartOffset() const {
591       return mOffset + (mReversed ? Length() : 0);
592     }
EndOffsetfinal::SelectionChangeDataBase593     uint32_t EndOffset() const { return mOffset + (mReversed ? 0 : Length()); }
Stringfinal::SelectionChangeDataBase594     const nsString& String() const { return *mString; }
Lengthfinal::SelectionChangeDataBase595     uint32_t Length() const { return mString->Length(); }
IsInInt32Rangefinal::SelectionChangeDataBase596     bool IsInInt32Range() const { return mOffset + Length() <= INT32_MAX; }
IsCollapsedfinal::SelectionChangeDataBase597     bool IsCollapsed() const { return mString->IsEmpty(); }
ClearSelectionDatafinal::SelectionChangeDataBase598     void ClearSelectionData() {
599       mOffset = UINT32_MAX;
600       mString->Truncate();
601       mWritingMode = 0;
602       mReversed = false;
603     }
Clearfinal::SelectionChangeDataBase604     void Clear() {
605       ClearSelectionData();
606       mCausedByComposition = false;
607       mCausedBySelectionEvent = false;
608       mOccurredDuringComposition = false;
609     }
IsValidfinal::SelectionChangeDataBase610     bool IsValid() const { return mOffset != UINT32_MAX; }
Assignfinal::SelectionChangeDataBase611     void Assign(const SelectionChangeDataBase& aOther) {
612       mOffset = aOther.mOffset;
613       *mString = aOther.String();
614       mWritingMode = aOther.mWritingMode;
615       mReversed = aOther.mReversed;
616       AssignReason(aOther.mCausedByComposition, aOther.mCausedBySelectionEvent,
617                    aOther.mOccurredDuringComposition);
618     }
AssignReasonfinal::SelectionChangeDataBase619     void AssignReason(bool aCausedByComposition, bool aCausedBySelectionEvent,
620                       bool aOccurredDuringComposition) {
621       mCausedByComposition = aCausedByComposition;
622       mCausedBySelectionEvent = aCausedBySelectionEvent;
623       mOccurredDuringComposition = aOccurredDuringComposition;
624     }
625   };
626 
627   // SelectionChangeDataBase cannot have constructors because it's used in
628   // the union.  Therefore, SelectionChangeData should only implement
629   // constructors.  In other words, add other members to
630   // SelectionChangeDataBase.
631   struct SelectionChangeData final : public SelectionChangeDataBase {
SelectionChangeDatafinal::final632     SelectionChangeData() {
633       mString = &mStringInstance;
634       Clear();
635     }
SelectionChangeDatafinal::final636     explicit SelectionChangeData(const SelectionChangeDataBase& aOther) {
637       mString = &mStringInstance;
638       Assign(aOther);
639     }
SelectionChangeDatafinal::final640     SelectionChangeData(const SelectionChangeData& aOther) {
641       mString = &mStringInstance;
642       Assign(aOther);
643     }
644     SelectionChangeData& operator=(const SelectionChangeDataBase& aOther) {
645       mString = &mStringInstance;
646       Assign(aOther);
647       return *this;
648     }
649     SelectionChangeData& operator=(const SelectionChangeData& aOther) {
650       mString = &mStringInstance;
651       Assign(aOther);
652       return *this;
653     }
654 
655    private:
656     // When SelectionChangeData is used outside of union, it shouldn't create
657     // nsString instance in the heap as far as possible.
658     nsString mStringInstance;
659   };
660 
661   struct TextChangeDataBase {
662     // mStartOffset is the start offset of modified or removed text in
663     // original content and inserted text in new content.
664     uint32_t mStartOffset;
665     // mRemovalEndOffset is the end offset of modified or removed text in
666     // original content.  If the value is same as mStartOffset, no text hasn't
667     // been removed yet.
668     uint32_t mRemovedEndOffset;
669     // mAddedEndOffset is the end offset of inserted text or same as
670     // mStartOffset if just removed.  The vlaue is offset in the new content.
671     uint32_t mAddedEndOffset;
672 
673     // Note that TextChangeDataBase may be the result of merging two or more
674     // changes especially in e10s mode.
675 
676     // mCausedOnlyByComposition is true only when *all* merged changes are
677     // caused by composition.
678     bool mCausedOnlyByComposition;
679     // mIncludingChangesDuringComposition is true if at least one change which
680     // is not caused by composition occurred during the last composition.
681     // Note that if after the last composition is finished and there are some
682     // changes not caused by composition, this is set to false.
683     bool mIncludingChangesDuringComposition;
684     // mIncludingChangesWithoutComposition is true if there is at least one
685     // change which did occur when there wasn't a composition ongoing.
686     bool mIncludingChangesWithoutComposition;
687 
OldLengthfinal::TextChangeDataBase688     uint32_t OldLength() const {
689       MOZ_ASSERT(IsValid());
690       return mRemovedEndOffset - mStartOffset;
691     }
NewLengthfinal::TextChangeDataBase692     uint32_t NewLength() const {
693       MOZ_ASSERT(IsValid());
694       return mAddedEndOffset - mStartOffset;
695     }
696 
697     // Positive if text is added. Negative if text is removed.
Differencefinal::TextChangeDataBase698     int64_t Difference() const { return mAddedEndOffset - mRemovedEndOffset; }
699 
IsInInt32Rangefinal::TextChangeDataBase700     bool IsInInt32Range() const {
701       MOZ_ASSERT(IsValid());
702       return mStartOffset <= INT32_MAX && mRemovedEndOffset <= INT32_MAX &&
703              mAddedEndOffset <= INT32_MAX;
704     }
705 
IsValidfinal::TextChangeDataBase706     bool IsValid() const {
707       return !(mStartOffset == UINT32_MAX && !mRemovedEndOffset &&
708                !mAddedEndOffset);
709     }
710 
Clearfinal::TextChangeDataBase711     void Clear() {
712       mStartOffset = UINT32_MAX;
713       mRemovedEndOffset = mAddedEndOffset = 0;
714     }
715 
716     void MergeWith(const TextChangeDataBase& aOther);
717     TextChangeDataBase& operator+=(const TextChangeDataBase& aOther) {
718       MergeWith(aOther);
719       return *this;
720     }
721 
722 #ifdef DEBUG
723     void Test();
724 #endif  // #ifdef DEBUG
725   };
726 
727   // TextChangeDataBase cannot have constructors because they are used in union.
728   // Therefore, TextChangeData should only implement constructor.  In other
729   // words, add other members to TextChangeDataBase.
730   struct TextChangeData : public TextChangeDataBase {
TextChangeDatafinal::TextChangeData731     TextChangeData() { Clear(); }
732 
TextChangeDatafinal::TextChangeData733     TextChangeData(uint32_t aStartOffset, uint32_t aRemovedEndOffset,
734                    uint32_t aAddedEndOffset, bool aCausedByComposition,
735                    bool aOccurredDuringComposition) {
736       MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
737                  "removed end offset must not be smaller than start offset");
738       MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
739                  "added end offset must not be smaller than start offset");
740       mStartOffset = aStartOffset;
741       mRemovedEndOffset = aRemovedEndOffset;
742       mAddedEndOffset = aAddedEndOffset;
743       mCausedOnlyByComposition = aCausedByComposition;
744       mIncludingChangesDuringComposition =
745           !aCausedByComposition && aOccurredDuringComposition;
746       mIncludingChangesWithoutComposition =
747           !aCausedByComposition && !aOccurredDuringComposition;
748     }
749   };
750 
751   struct MouseButtonEventData {
752     // The value of WidgetEvent::mMessage
753     EventMessage mEventMessage;
754     // Character offset from the start of the focused editor under the cursor
755     uint32_t mOffset;
756     // Cursor position in pixels relative to the widget
757     Point mCursorPos;
758     // Character rect in pixels under the cursor relative to the widget
759     Rect mCharRect;
760     // The value of WidgetMouseEventBase::button and buttons
761     int16_t mButton;
762     int16_t mButtons;
763     // The value of WidgetInputEvent::modifiers
764     Modifiers mModifiers;
765   };
766 
767   union {
768     // NOTIFY_IME_OF_SELECTION_CHANGE specific data
769     SelectionChangeDataBase mSelectionChangeData;
770 
771     // NOTIFY_IME_OF_TEXT_CHANGE specific data
772     TextChangeDataBase mTextChangeData;
773 
774     // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
775     MouseButtonEventData mMouseButtonEventData;
776   };
777 
SetDatafinal778   void SetData(const SelectionChangeDataBase& aSelectionChangeData) {
779     MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
780     mSelectionChangeData.Assign(aSelectionChangeData);
781   }
782 
SetDatafinal783   void SetData(const TextChangeDataBase& aTextChangeData) {
784     MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
785     mTextChangeData = aTextChangeData;
786   }
787 };
788 
789 struct CandidateWindowPosition {
790   // Upper left corner of the candidate window if mExcludeRect is false.
791   // Otherwise, the position currently interested.  E.g., caret position.
792   LayoutDeviceIntPoint mPoint;
793   // Rect which shouldn't be overlapped with the candidate window.
794   // This is valid only when mExcludeRect is true.
795   LayoutDeviceIntRect mRect;
796   // See explanation of mPoint and mRect.
797   bool mExcludeRect;
798 };
799 
800 }  // namespace widget
801 }  // namespace mozilla
802 
803 #endif  // #ifndef mozilla_widget_IMEData_h_
804