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