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/. */
6 #ifndef mozilla_widget_IMEData_h_
7 #define mozilla_widget_IMEData_h_
9 #include "mozilla/CheckedInt.h"
10 #include "mozilla/EventForwards.h"
11 #include "mozilla/ToString.h"
16 #include "nsXULAppAPI.h"
25 // Helper class to logging string which may contain various Unicode characters
26 // and/or may be too long string for logging.
27 class MOZ_STACK_CLASS PrintStringDetail
: public nsAutoCString
{
29 static constexpr uint32_t kMaxLengthForCompositionString
= 8;
30 static constexpr uint32_t kMaxLengthForSelectedString
= 12;
31 static constexpr uint32_t kMaxLengthForEditor
= 20;
33 PrintStringDetail() = delete;
34 explicit PrintStringDetail(const nsAString
& aString
,
35 uint32_t aMaxLength
= UINT32_MAX
);
38 static nsCString
PrintCharData(char32_t aChar
);
41 // StartAndEndOffsets represents a range in flat-text.
42 template <typename IntType
>
43 class StartAndEndOffsets
{
45 static IntType
MaxOffset() { return std::numeric_limits
<IntType
>::max(); }
48 StartAndEndOffsets() = delete;
49 explicit StartAndEndOffsets(IntType aStartOffset
, IntType aEndOffset
)
50 : mStartOffset(aStartOffset
),
51 mEndOffset(aStartOffset
<= aEndOffset
? aEndOffset
: aStartOffset
) {
52 MOZ_ASSERT(aStartOffset
<= mEndOffset
);
55 IntType
StartOffset() const { return mStartOffset
; }
56 IntType
Length() const { return mEndOffset
- mStartOffset
; }
57 IntType
EndOffset() const { return mEndOffset
; }
59 bool IsOffsetInRange(IntType aOffset
) const {
60 return aOffset
>= mStartOffset
&& aOffset
< mEndOffset
;
62 bool IsOffsetInRangeOrEndOffset(IntType aOffset
) const {
63 return aOffset
>= mStartOffset
&& aOffset
<= mEndOffset
;
66 void MoveTo(IntType aNewStartOffset
) {
67 auto delta
= static_cast<int64_t>(mStartOffset
) - aNewStartOffset
;
68 mStartOffset
+= delta
;
71 void SetOffsetAndLength(IntType aNewOffset
, IntType aNewLength
) {
72 mStartOffset
= aNewOffset
;
73 CheckedInt
<IntType
> endOffset(aNewOffset
+ aNewLength
);
74 mEndOffset
= endOffset
.isValid() ? endOffset
.value() : MaxOffset();
76 void SetEndOffset(IntType aEndOffset
) {
77 MOZ_ASSERT(mStartOffset
<= aEndOffset
);
78 mEndOffset
= std::max(aEndOffset
, mStartOffset
);
80 void SetStartAndEndOffsets(IntType aStartOffset
, IntType aEndOffset
) {
81 MOZ_ASSERT(aStartOffset
<= aEndOffset
);
82 mStartOffset
= aStartOffset
;
83 mEndOffset
= aStartOffset
<= aEndOffset
? aEndOffset
: aStartOffset
;
85 void SetLength(IntType aNewLength
) {
86 CheckedInt
<IntType
> endOffset(mStartOffset
+ aNewLength
);
87 mEndOffset
= endOffset
.isValid() ? endOffset
.value() : MaxOffset();
90 friend std::ostream
& operator<<(
91 std::ostream
& aStream
,
92 const StartAndEndOffsets
<IntType
>& aStartAndEndOffsets
) {
93 aStream
<< "{ mStartOffset=" << aStartAndEndOffsets
.mStartOffset
94 << ", mEndOffset=" << aStartAndEndOffsets
.mEndOffset
95 << ", Length()=" << aStartAndEndOffsets
.Length() << " }";
100 IntType mStartOffset
;
104 // OffsetAndData class is designed for storing composition string and its
105 // start offset. Length() and EndOffset() return only valid length or
106 // offset. I.e., if the string is too long for inserting at the offset,
107 // the length is shrunken. However, the string itself is not shrunken.
108 // Therefore, moving it to where all of the string can be contained,
109 // they will return longer/bigger value.
110 enum class OffsetAndDataFor
{
115 template <typename IntType
>
116 class OffsetAndData
{
118 static IntType
MaxOffset() { return std::numeric_limits
<IntType
>::max(); }
121 OffsetAndData() = delete;
122 explicit OffsetAndData(
123 IntType aStartOffset
, const nsAString
& aData
,
124 OffsetAndDataFor aFor
= OffsetAndDataFor::CompositionString
)
125 : mData(aData
), mOffset(aStartOffset
), mFor(aFor
) {}
127 IntType
StartOffset() const { return mOffset
; }
128 IntType
Length() const {
129 CheckedInt
<IntType
> endOffset(mOffset
+ mData
.Length());
130 return endOffset
.isValid() ? mData
.Length() : MaxOffset() - mOffset
;
132 IntType
EndOffset() const { return mOffset
+ Length(); }
133 StartAndEndOffsets
<IntType
> CreateStartAndEndOffsets() const {
134 return StartAndEndOffsets
<IntType
>(StartOffset(), EndOffset());
136 const nsString
& DataRef() const {
137 // In strictly speaking, we should return substring which may be shrunken
138 // for rounding to the max offset. However, it's unrealistic edge case,
139 // and creating new string is not so cheap job in a hot path. Therefore,
140 // this just returns the data as-is.
143 bool IsDataEmpty() const { return mData
.IsEmpty(); }
145 bool IsOffsetInRange(IntType aOffset
) const {
146 return aOffset
>= mOffset
&& aOffset
< EndOffset();
148 bool IsOffsetInRangeOrEndOffset(IntType aOffset
) const {
149 return aOffset
>= mOffset
&& aOffset
<= EndOffset();
152 void MoveTo(IntType aNewOffset
) { mOffset
= aNewOffset
; }
153 void SetOffsetAndData(IntType aStartOffset
, const nsAString
& aData
) {
154 mOffset
= aStartOffset
;
157 void SetData(const nsAString
& aData
) { mData
= aData
; }
158 void TruncateData(uint32_t aLength
= 0) { mData
.Truncate(aLength
); }
159 void ReplaceData(nsAString::size_type aCutStart
,
160 nsAString::size_type aCutLength
,
161 const nsAString
& aNewString
) {
162 mData
.Replace(aCutStart
, aCutLength
, aNewString
);
165 friend std::ostream
& operator<<(
166 std::ostream
& aStream
, const OffsetAndData
<IntType
>& aOffsetAndData
) {
167 const auto maxDataLength
=
168 aOffsetAndData
.mFor
== OffsetAndDataFor::CompositionString
169 ? PrintStringDetail::kMaxLengthForCompositionString
170 : (aOffsetAndData
.mFor
== OffsetAndDataFor::SelectedString
171 ? PrintStringDetail::kMaxLengthForSelectedString
172 : PrintStringDetail::kMaxLengthForEditor
);
173 aStream
<< "{ mOffset=" << aOffsetAndData
.mOffset
<< ", mData="
174 << PrintStringDetail(aOffsetAndData
.mData
, maxDataLength
).get()
175 << ", Length()=" << aOffsetAndData
.Length()
176 << ", EndOffset()=" << aOffsetAndData
.EndOffset() << " }";
183 OffsetAndDataFor mFor
;
189 * Preference for receiving IME updates
191 * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
192 * change and/or selection change and call nsIWidget::NotifyIME() with
193 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
194 * Please note that the text change observing cost is very expensive especially
195 * on an HTML editor has focus.
196 * If the IME implementation on a particular platform doesn't care about
197 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
198 * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
199 * If the IME implementation needs notifications even while our process is
200 * deactive, it should also set NOTIFY_DURING_DEACTIVE.
202 struct IMENotificationRequests final
{
203 typedef uint8_t Notifications
;
205 enum : Notifications
{
207 NOTIFY_TEXT_CHANGE
= 1 << 1,
208 NOTIFY_POSITION_CHANGE
= 1 << 2,
209 // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
210 // or released on a character in the focused editor. The notification is
211 // notified to IME as a mouse event. If it's consumed by IME, NotifyIME()
212 // returns NS_SUCCESS_EVENT_CONSUMED. Otherwise, it returns NS_OK if it's
213 // handled without any error.
214 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
= 1 << 3,
215 // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
216 // or more compositions are possible. E.g., Mac and Linux (GTK).
217 NOTIFY_DURING_DEACTIVE
= 1 << 7,
219 NOTIFY_ALL
= NOTIFY_TEXT_CHANGE
| NOTIFY_POSITION_CHANGE
|
220 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
,
223 IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING
) {}
225 explicit IMENotificationRequests(Notifications aWantUpdates
)
226 : mWantUpdates(aWantUpdates
) {}
228 IMENotificationRequests
operator|(
229 const IMENotificationRequests
& aOther
) const {
230 return IMENotificationRequests(aOther
.mWantUpdates
| mWantUpdates
);
232 IMENotificationRequests
& operator|=(const IMENotificationRequests
& aOther
) {
233 mWantUpdates
|= aOther
.mWantUpdates
;
236 bool operator==(const IMENotificationRequests
& aOther
) const {
237 return mWantUpdates
== aOther
.mWantUpdates
;
240 bool WantTextChange() const { return !!(mWantUpdates
& NOTIFY_TEXT_CHANGE
); }
242 bool WantPositionChanged() const {
243 return !!(mWantUpdates
& NOTIFY_POSITION_CHANGE
);
246 bool WantChanges() const { return WantTextChange(); }
248 bool WantMouseButtonEventOnChar() const {
249 return !!(mWantUpdates
& NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
);
252 bool WantDuringDeactive() const {
253 return !!(mWantUpdates
& NOTIFY_DURING_DEACTIVE
);
256 Notifications mWantUpdates
;
260 * IME enabled states.
262 * WARNING: If you change these values, you also need to edit:
263 * nsIDOMWindowUtils.idl
265 enum class IMEEnabled
{
267 * 'Disabled' means the user cannot use IME. So, the IME open state should
268 * be 'closed' during 'disabled'.
272 * 'Enabled' means the user can use IME.
276 * 'Password' state is a special case for the password editors.
277 * E.g., on mac, the password editors should disable the non-Roman
278 * keyboard layouts at getting focus. Thus, the password editor may have
279 * special rules on some platforms.
283 * 'Unknown' is useful when you cache this enum. So, this shouldn't be
284 * used with nsIWidget::SetInputContext().
290 * Contains IMEStatus plus information about the current
291 * input context that the IME can use as hints if desired.
294 struct IMEState final
{
298 * IME open states the mOpen value of SetInputContext() should be one value of
299 * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE. GetInputContext() should return
300 * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
304 * 'Unsupported' means the platform cannot return actual IME open state.
305 * This value is used only by GetInputContext().
307 OPEN_STATE_NOT_SUPPORTED
,
309 * 'Don't change' means the widget shouldn't change IME open state when
310 * SetInputContext() is called.
312 DONT_CHANGE_OPEN_STATE
= OPEN_STATE_NOT_SUPPORTED
,
314 * 'Open' means that IME should compose in its primary language (or latest
315 * input mode except direct ASCII character input mode). Even if IME is
316 * opened by this value, users should be able to close IME by theirselves.
317 * Web contents can specify this value by |ime-mode: active;|.
321 * 'Closed' means that IME shouldn't handle key events (or should handle
322 * as ASCII character inputs on mobile device). Even if IME is closed by
323 * this value, users should be able to open IME by theirselves.
324 * Web contents can specify this value by |ime-mode: inactive;|.
330 IMEState() : mEnabled(IMEEnabled::Enabled
), mOpen(DONT_CHANGE_OPEN_STATE
) {}
332 explicit IMEState(IMEEnabled aEnabled
, Open aOpen
= DONT_CHANGE_OPEN_STATE
)
333 : mEnabled(aEnabled
), mOpen(aOpen
) {}
335 // Returns true if the user can input characters.
336 // This means that a plain text editor, an HTML editor, a password editor or
337 // a plain text editor whose ime-mode is "disabled".
338 bool IsEditable() const {
339 return mEnabled
== IMEEnabled::Enabled
|| mEnabled
== IMEEnabled::Password
;
343 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
344 // If there can be only one IME composition in a process, this can be used.
345 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
346 (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
348 struct NativeIMEContext final
{
349 // Pointer to native IME context. Typically this is the result of
350 // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
351 // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
352 uintptr_t mRawNativeIMEContext
;
353 // Process ID of the origin of mNativeIMEContext.
354 uint64_t mOriginProcessID
;
356 NativeIMEContext() : mRawNativeIMEContext(0), mOriginProcessID(0) {
360 explicit NativeIMEContext(nsIWidget
* aWidget
)
361 : mRawNativeIMEContext(0), mOriginProcessID(0) {
365 bool IsValid() const {
366 return mRawNativeIMEContext
&&
367 mOriginProcessID
!= static_cast<uintptr_t>(-1);
370 void Init(nsIWidget
* aWidget
);
371 void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext
) {
372 InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext
));
374 void InitWithRawNativeIMEContext(void* aRawNativeIMEContext
);
376 bool operator==(const NativeIMEContext
& aOther
) const {
377 return mRawNativeIMEContext
== aOther
.mRawNativeIMEContext
&&
378 mOriginProcessID
== aOther
.mOriginProcessID
;
380 bool operator!=(const NativeIMEContext
& aOther
) const {
381 return !(*this == aOther
);
385 struct InputContext final
{
387 : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN
: ORIGIN_CONTENT
),
388 mMayBeIMEUnaware(false),
389 mHasHandledUserInput(false),
390 mInPrivateBrowsing(false) {}
392 // If InputContext instance is a static variable, any heap allocated stuff
393 // of its members need to be deleted at XPCOM shutdown. Otherwise, it's
394 // detected as memory leak.
396 mHTMLInputType
.Truncate();
397 mHTMLInputInputmode
.Truncate();
398 mActionHint
.Truncate();
399 mAutocapitalize
.Truncate();
402 bool IsPasswordEditor() const {
403 return mHTMLInputType
.LowerCaseEqualsLiteral("password");
406 // https://html.spec.whatwg.org/dev/interaction.html#autocapitalization
407 bool IsAutocapitalizeSupported() const {
408 return !mHTMLInputType
.EqualsLiteral("password") &&
409 !mHTMLInputType
.EqualsLiteral("url") &&
410 !mHTMLInputType
.EqualsLiteral("email");
415 /* The type of the input if the input is a html input field */
416 nsString mHTMLInputType
;
418 /* The type of the inputmode */
419 nsString mHTMLInputInputmode
;
421 /* A hint for the action that is performed when the input is submitted */
422 nsString mActionHint
;
424 /* A hint for autocapitalize */
425 nsString mAutocapitalize
;
428 * mOrigin indicates whether this focus event refers to main or remote
432 // Adjusting focus of content on the main process
434 // Adjusting focus of content in a remote process
439 /* True if the webapp may be unaware of IME events such as input event or
440 * composiion events. This enables a key-events-only mode on Android for
441 * compatibility with webapps relying on key listeners. */
442 bool mMayBeIMEUnaware
;
445 * True if the document has ever received user input
447 bool mHasHandledUserInput
;
449 /* Whether the owning document of the input element has been loaded
450 * in private browsing mode. */
451 bool mInPrivateBrowsing
;
453 bool IsOriginMainProcess() const { return mOrigin
== ORIGIN_MAIN
; }
455 bool IsOriginContentProcess() const { return mOrigin
== ORIGIN_CONTENT
; }
457 bool IsOriginCurrentProcess() const {
458 if (XRE_IsParentProcess()) {
459 return IsOriginMainProcess();
461 return IsOriginContentProcess();
465 // FYI: Implemented in nsBaseWidget.cpp
466 const char* ToChar(InputContext::Origin aOrigin
);
468 struct InputContextAction final
{
470 * mCause indicates what action causes calling nsIWidget::SetInputContext().
471 * It must be one of following values.
474 // The cause is unknown but originated from content. Focus might have been
475 // changed by content script.
477 // The cause is unknown but originated from chrome. Focus might have been
478 // changed by chrome script.
479 CAUSE_UNKNOWN_CHROME
,
480 // The cause is user's keyboard operation.
482 // The cause is user's mouse operation.
484 // The cause is user's touch operation (implies mouse)
486 // The cause is users' long press operation.
488 // The cause is unknown but it occurs during user input except keyboard
489 // input. E.g., an event handler of a user input event moves focus.
490 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
,
491 // The cause is unknown but it occurs during keyboard input.
492 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
,
497 * mFocusChange indicates what happened for focus.
501 // A content got focus.
503 // Focused content lost focus.
505 // Menu got pseudo focus that means focused content isn't changed but
506 // keyboard events will be handled by menu.
507 MENU_GOT_PSEUDO_FOCUS
,
508 // Menu lost pseudo focus that means focused content will handle keyboard
510 MENU_LOST_PSEUDO_FOCUS
,
511 // The widget is created. When a widget is crated, it may need to notify
512 // IME module to initialize its native IME context. In such case, this is
513 // used. I.e., this isn't used by IMEStateManager.
516 FocusChange mFocusChange
;
518 bool ContentGotFocusByTrustedCause() const {
519 return (mFocusChange
== GOT_FOCUS
&& mCause
!= CAUSE_UNKNOWN
);
522 bool UserMightRequestOpenVKB() const {
523 // If focus is changed, user must not request to open VKB.
524 if (mFocusChange
!= FOCUS_NOT_CHANGED
) {
528 // If user clicks or touches focused editor, user must request to open
532 // If script does something during a user input and that causes changing
533 // input context, user might request to open VKB. E.g., user clicks
534 // dummy editor and JS moves focus to an actual editable node. However,
535 // this should return false if the user input is a keyboard event since
536 // physical keyboard operation shouldn't cause opening VKB.
537 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
:
545 * IsHandlingUserInput() returns true if it's caused by a user action directly
546 * or it's caused by script or something but it occurred while we're handling
547 * a user action. E.g., when it's caused by Element.focus() in an event
548 * handler of a user input, this returns true.
550 static bool IsHandlingUserInput(Cause aCause
) {
555 case CAUSE_LONGPRESS
:
556 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
:
557 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
:
564 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause
); }
567 : mCause(CAUSE_UNKNOWN
), mFocusChange(FOCUS_NOT_CHANGED
) {}
569 explicit InputContextAction(Cause aCause
,
570 FocusChange aFocusChange
= FOCUS_NOT_CHANGED
)
571 : mCause(aCause
), mFocusChange(aFocusChange
) {}
574 // IMEMessage is shared by IMEStateManager and TextComposition.
575 // Update values in GeckoEditable.java if you make changes here.
576 // XXX Negative values are used in Android...
577 typedef int8_t IMEMessageType
;
578 enum IMEMessage
: IMEMessageType
{
579 // This is used by IMENotification internally. This means that the instance
580 // hasn't been initialized yet.
581 NOTIFY_IME_OF_NOTHING
,
582 // An editable content is getting focus
584 // An editable content is losing focus
586 // Selection in the focused editable content is changed
587 NOTIFY_IME_OF_SELECTION_CHANGE
,
588 // Text in the focused editable content is changed
589 NOTIFY_IME_OF_TEXT_CHANGE
,
590 // Notified when a dispatched composition event is handled by the
591 // contents. This must be notified after the other notifications.
592 // Note that if a remote process has focus, this is notified only once when
593 // all dispatched events are handled completely. So, the receiver shouldn't
594 // count number of received this notification for comparing with the number
595 // of dispatched events.
596 // NOTE: If a composition event causes moving focus from the focused editor,
597 // this notification may not be notified as usual. Even in such case,
598 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
599 // should tread the blur notification as including this if there is
600 // pending composition events.
601 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
,
602 // Position or size of focused element may be changed.
603 NOTIFY_IME_OF_POSITION_CHANGE
,
604 // Mouse button event is fired on a character in focused editor
605 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
,
606 // Request to commit current composition to IME
607 // (some platforms may not support)
608 REQUEST_TO_COMMIT_COMPOSITION
,
609 // Request to cancel current composition to IME
610 // (some platforms may not support)
611 REQUEST_TO_CANCEL_COMPOSITION
614 // FYI: Implemented in nsBaseWidget.cpp
615 const char* ToChar(IMEMessage aIMEMessage
);
617 struct IMENotification final
{
618 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING
), mSelectionChangeData() {}
620 IMENotification(const IMENotification
& aOther
)
621 : mMessage(NOTIFY_IME_OF_NOTHING
) {
625 ~IMENotification() { Clear(); }
627 MOZ_IMPLICIT
IMENotification(IMEMessage aMessage
)
628 : mMessage(aMessage
), mSelectionChangeData() {
630 case NOTIFY_IME_OF_SELECTION_CHANGE
:
631 mSelectionChangeData
.mString
= new nsString();
632 mSelectionChangeData
.Clear();
634 case NOTIFY_IME_OF_TEXT_CHANGE
:
635 mTextChangeData
.Clear();
637 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
638 mMouseButtonEventData
.mEventMessage
= eVoidEvent
;
639 mMouseButtonEventData
.mOffset
= UINT32_MAX
;
640 mMouseButtonEventData
.mCursorPos
.Set(nsIntPoint(0, 0));
641 mMouseButtonEventData
.mCharRect
.Set(nsIntRect(0, 0, 0, 0));
642 mMouseButtonEventData
.mButton
= -1;
643 mMouseButtonEventData
.mButtons
= 0;
644 mMouseButtonEventData
.mModifiers
= 0;
651 void Assign(const IMENotification
& aOther
) {
652 bool changingMessage
= mMessage
!= aOther
.mMessage
;
653 if (changingMessage
) {
655 mMessage
= aOther
.mMessage
;
658 case NOTIFY_IME_OF_SELECTION_CHANGE
:
659 if (changingMessage
) {
660 mSelectionChangeData
.mString
= new nsString();
662 mSelectionChangeData
.Assign(aOther
.mSelectionChangeData
);
664 case NOTIFY_IME_OF_TEXT_CHANGE
:
665 mTextChangeData
= aOther
.mTextChangeData
;
667 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
668 mMouseButtonEventData
= aOther
.mMouseButtonEventData
;
675 IMENotification
& operator=(const IMENotification
& aOther
) {
681 if (mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
) {
682 MOZ_ASSERT(mSelectionChangeData
.mString
);
683 delete mSelectionChangeData
.mString
;
684 mSelectionChangeData
.mString
= nullptr;
686 mMessage
= NOTIFY_IME_OF_NOTHING
;
689 bool HasNotification() const { return mMessage
!= NOTIFY_IME_OF_NOTHING
; }
691 void MergeWith(const IMENotification
& aNotification
) {
693 case NOTIFY_IME_OF_NOTHING
:
694 MOZ_ASSERT(aNotification
.mMessage
!= NOTIFY_IME_OF_NOTHING
);
695 Assign(aNotification
);
697 case NOTIFY_IME_OF_SELECTION_CHANGE
:
698 MOZ_ASSERT(aNotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
);
699 mSelectionChangeData
.Assign(aNotification
.mSelectionChangeData
);
701 case NOTIFY_IME_OF_TEXT_CHANGE
:
702 MOZ_ASSERT(aNotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
);
703 mTextChangeData
+= aNotification
.mTextChangeData
;
705 case NOTIFY_IME_OF_POSITION_CHANGE
:
706 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
707 MOZ_ASSERT(aNotification
.mMessage
== mMessage
);
710 MOZ_CRASH("Merging notification isn't supported");
721 void Set(const nsIntPoint
& aPoint
) {
725 nsIntPoint
AsIntPoint() const { return nsIntPoint(mX
, mY
); }
734 void Set(const nsIntRect
& aRect
) {
735 aRect
.GetRect(&mX
, &mY
, &mWidth
, &mHeight
);
737 nsIntRect
AsIntRect() const { return nsIntRect(mX
, mY
, mWidth
, mHeight
); }
740 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
741 struct SelectionChangeDataBase
{
748 // Writing mode at the selection.
749 uint8_t mWritingMode
;
752 bool mCausedByComposition
;
753 bool mCausedBySelectionEvent
;
754 bool mOccurredDuringComposition
;
756 void SetWritingMode(const WritingMode
& aWritingMode
);
757 WritingMode
GetWritingMode() const;
759 uint32_t StartOffset() const {
760 return mOffset
+ (mReversed
? Length() : 0);
762 uint32_t EndOffset() const { return mOffset
+ (mReversed
? 0 : Length()); }
763 const nsString
& String() const { return *mString
; }
764 uint32_t Length() const { return mString
->Length(); }
765 bool IsInInt32Range() const { return mOffset
+ Length() <= INT32_MAX
; }
766 bool IsCollapsed() const { return mString
->IsEmpty(); }
767 void ClearSelectionData() {
768 mOffset
= UINT32_MAX
;
774 ClearSelectionData();
775 mCausedByComposition
= false;
776 mCausedBySelectionEvent
= false;
777 mOccurredDuringComposition
= false;
779 bool IsValid() const { return mOffset
!= UINT32_MAX
; }
780 void Assign(const SelectionChangeDataBase
& aOther
) {
781 mOffset
= aOther
.mOffset
;
782 *mString
= aOther
.String();
783 mWritingMode
= aOther
.mWritingMode
;
784 mReversed
= aOther
.mReversed
;
785 AssignReason(aOther
.mCausedByComposition
, aOther
.mCausedBySelectionEvent
,
786 aOther
.mOccurredDuringComposition
);
788 void AssignReason(bool aCausedByComposition
, bool aCausedBySelectionEvent
,
789 bool aOccurredDuringComposition
) {
790 mCausedByComposition
= aCausedByComposition
;
791 mCausedBySelectionEvent
= aCausedBySelectionEvent
;
792 mOccurredDuringComposition
= aOccurredDuringComposition
;
796 // SelectionChangeDataBase cannot have constructors because it's used in
797 // the union. Therefore, SelectionChangeData should only implement
798 // constructors. In other words, add other members to
799 // SelectionChangeDataBase.
800 struct SelectionChangeData final
: public SelectionChangeDataBase
{
801 SelectionChangeData() {
802 mString
= &mStringInstance
;
805 explicit SelectionChangeData(const SelectionChangeDataBase
& aOther
) {
806 mString
= &mStringInstance
;
809 SelectionChangeData(const SelectionChangeData
& aOther
) {
810 mString
= &mStringInstance
;
813 SelectionChangeData
& operator=(const SelectionChangeDataBase
& aOther
) {
814 mString
= &mStringInstance
;
818 SelectionChangeData
& operator=(const SelectionChangeData
& aOther
) {
819 mString
= &mStringInstance
;
825 // When SelectionChangeData is used outside of union, it shouldn't create
826 // nsString instance in the heap as far as possible.
827 nsString mStringInstance
;
830 struct TextChangeDataBase
{
831 // mStartOffset is the start offset of modified or removed text in
832 // original content and inserted text in new content.
833 uint32_t mStartOffset
;
834 // mRemovalEndOffset is the end offset of modified or removed text in
835 // original content. If the value is same as mStartOffset, no text hasn't
837 uint32_t mRemovedEndOffset
;
838 // mAddedEndOffset is the end offset of inserted text or same as
839 // mStartOffset if just removed. The vlaue is offset in the new content.
840 uint32_t mAddedEndOffset
;
842 // Note that TextChangeDataBase may be the result of merging two or more
843 // changes especially in e10s mode.
845 // mCausedOnlyByComposition is true only when *all* merged changes are
846 // caused by composition.
847 bool mCausedOnlyByComposition
;
848 // mIncludingChangesDuringComposition is true if at least one change which
849 // is not caused by composition occurred during the last composition.
850 // Note that if after the last composition is finished and there are some
851 // changes not caused by composition, this is set to false.
852 bool mIncludingChangesDuringComposition
;
853 // mIncludingChangesWithoutComposition is true if there is at least one
854 // change which did occur when there wasn't a composition ongoing.
855 bool mIncludingChangesWithoutComposition
;
857 uint32_t OldLength() const {
858 MOZ_ASSERT(IsValid());
859 return mRemovedEndOffset
- mStartOffset
;
861 uint32_t NewLength() const {
862 MOZ_ASSERT(IsValid());
863 return mAddedEndOffset
- mStartOffset
;
866 // Positive if text is added. Negative if text is removed.
867 int64_t Difference() const { return mAddedEndOffset
- mRemovedEndOffset
; }
869 bool IsInInt32Range() const {
870 MOZ_ASSERT(IsValid());
871 return mStartOffset
<= INT32_MAX
&& mRemovedEndOffset
<= INT32_MAX
&&
872 mAddedEndOffset
<= INT32_MAX
;
875 bool IsValid() const {
876 return !(mStartOffset
== UINT32_MAX
&& !mRemovedEndOffset
&&
881 mStartOffset
= UINT32_MAX
;
882 mRemovedEndOffset
= mAddedEndOffset
= 0;
885 void MergeWith(const TextChangeDataBase
& aOther
);
886 TextChangeDataBase
& operator+=(const TextChangeDataBase
& aOther
) {
893 #endif // #ifdef DEBUG
896 // TextChangeDataBase cannot have constructors because they are used in union.
897 // Therefore, TextChangeData should only implement constructor. In other
898 // words, add other members to TextChangeDataBase.
899 struct TextChangeData
: public TextChangeDataBase
{
900 TextChangeData() { Clear(); }
902 TextChangeData(uint32_t aStartOffset
, uint32_t aRemovedEndOffset
,
903 uint32_t aAddedEndOffset
, bool aCausedByComposition
,
904 bool aOccurredDuringComposition
) {
905 MOZ_ASSERT(aRemovedEndOffset
>= aStartOffset
,
906 "removed end offset must not be smaller than start offset");
907 MOZ_ASSERT(aAddedEndOffset
>= aStartOffset
,
908 "added end offset must not be smaller than start offset");
909 mStartOffset
= aStartOffset
;
910 mRemovedEndOffset
= aRemovedEndOffset
;
911 mAddedEndOffset
= aAddedEndOffset
;
912 mCausedOnlyByComposition
= aCausedByComposition
;
913 mIncludingChangesDuringComposition
=
914 !aCausedByComposition
&& aOccurredDuringComposition
;
915 mIncludingChangesWithoutComposition
=
916 !aCausedByComposition
&& !aOccurredDuringComposition
;
920 struct MouseButtonEventData
{
921 // The value of WidgetEvent::mMessage
922 EventMessage mEventMessage
;
923 // Character offset from the start of the focused editor under the cursor
925 // Cursor position in pixels relative to the widget
927 // Character rect in pixels under the cursor relative to the widget
929 // The value of WidgetMouseEventBase::button and buttons
932 // The value of WidgetInputEvent::modifiers
933 Modifiers mModifiers
;
937 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
938 SelectionChangeDataBase mSelectionChangeData
;
940 // NOTIFY_IME_OF_TEXT_CHANGE specific data
941 TextChangeDataBase mTextChangeData
;
943 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
944 MouseButtonEventData mMouseButtonEventData
;
947 void SetData(const SelectionChangeDataBase
& aSelectionChangeData
) {
948 MOZ_RELEASE_ASSERT(mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
);
949 mSelectionChangeData
.Assign(aSelectionChangeData
);
952 void SetData(const TextChangeDataBase
& aTextChangeData
) {
953 MOZ_RELEASE_ASSERT(mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
);
954 mTextChangeData
= aTextChangeData
;
958 struct CandidateWindowPosition
{
959 // Upper left corner of the candidate window if mExcludeRect is false.
960 // Otherwise, the position currently interested. E.g., caret position.
961 LayoutDeviceIntPoint mPoint
;
962 // Rect which shouldn't be overlapped with the candidate window.
963 // This is valid only when mExcludeRect is true.
964 LayoutDeviceIntRect mRect
;
965 // See explanation of mPoint and mRect.
969 std::ostream
& operator<<(std::ostream
& aStream
, const IMEEnabled
& aEnabled
);
970 std::ostream
& operator<<(std::ostream
& aStream
, const IMEState::Open
& aOpen
);
971 std::ostream
& operator<<(std::ostream
& aStream
, const IMEState
& aState
);
972 std::ostream
& operator<<(std::ostream
& aStream
,
973 const InputContext::Origin
& aOrigin
);
974 std::ostream
& operator<<(std::ostream
& aStream
, const InputContext
& aContext
);
975 std::ostream
& operator<<(std::ostream
& aStream
,
976 const InputContextAction::Cause
& aCause
);
977 std::ostream
& operator<<(std::ostream
& aStream
,
978 const InputContextAction::FocusChange
& aFocusChange
);
979 std::ostream
& operator<<(std::ostream
& aStream
,
980 const IMENotification::SelectionChangeDataBase
& aData
);
981 std::ostream
& operator<<(std::ostream
& aStream
,
982 const IMENotification::TextChangeDataBase
& aData
);
984 } // namespace widget
985 } // namespace mozilla
987 #endif // #ifndef mozilla_widget_IMEData_h_