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/NativeKeyBindingsType.h"
12 #include "mozilla/ToString.h"
19 #include "nsXULAppAPI.h"
26 class ContentSelection
;
32 // Helper class to logging string which may contain various Unicode characters
33 // and/or may be too long string for logging.
34 class MOZ_STACK_CLASS PrintStringDetail
: public nsAutoCString
{
36 static constexpr uint32_t kMaxLengthForCompositionString
= 8;
37 static constexpr uint32_t kMaxLengthForSelectedString
= 12;
38 static constexpr uint32_t kMaxLengthForEditor
= 20;
40 PrintStringDetail() = delete;
41 explicit PrintStringDetail(const nsAString
& aString
,
42 uint32_t aMaxLength
= UINT32_MAX
);
43 template <typename StringType
>
44 explicit PrintStringDetail(const Maybe
<StringType
>& aMaybeString
,
45 uint32_t aMaxLength
= UINT32_MAX
);
48 static nsCString
PrintCharData(char32_t aChar
);
51 // StartAndEndOffsets represents a range in flat-text.
52 template <typename IntType
>
53 class StartAndEndOffsets
{
55 static IntType
MaxOffset() { return std::numeric_limits
<IntType
>::max(); }
58 StartAndEndOffsets() = delete;
59 explicit StartAndEndOffsets(IntType aStartOffset
, IntType aEndOffset
)
60 : mStartOffset(aStartOffset
),
61 mEndOffset(aStartOffset
<= aEndOffset
? aEndOffset
: aStartOffset
) {
62 MOZ_ASSERT(aStartOffset
<= mEndOffset
);
65 IntType
StartOffset() const { return mStartOffset
; }
66 IntType
Length() const { return mEndOffset
- mStartOffset
; }
67 IntType
EndOffset() const { return mEndOffset
; }
69 bool IsOffsetInRange(IntType aOffset
) const {
70 return aOffset
>= mStartOffset
&& aOffset
< mEndOffset
;
72 bool IsOffsetInRangeOrEndOffset(IntType aOffset
) const {
73 return aOffset
>= mStartOffset
&& aOffset
<= mEndOffset
;
76 void MoveTo(IntType aNewStartOffset
) {
77 auto delta
= static_cast<int64_t>(mStartOffset
) - aNewStartOffset
;
78 mStartOffset
+= delta
;
81 void SetOffsetAndLength(IntType aNewOffset
, IntType aNewLength
) {
82 mStartOffset
= aNewOffset
;
83 CheckedInt
<IntType
> endOffset(aNewOffset
+ aNewLength
);
84 mEndOffset
= endOffset
.isValid() ? endOffset
.value() : MaxOffset();
86 void SetEndOffset(IntType aEndOffset
) {
87 MOZ_ASSERT(mStartOffset
<= aEndOffset
);
88 mEndOffset
= std::max(aEndOffset
, mStartOffset
);
90 void SetStartAndEndOffsets(IntType aStartOffset
, IntType aEndOffset
) {
91 MOZ_ASSERT(aStartOffset
<= aEndOffset
);
92 mStartOffset
= aStartOffset
;
93 mEndOffset
= aStartOffset
<= aEndOffset
? aEndOffset
: aStartOffset
;
95 void SetLength(IntType aNewLength
) {
96 CheckedInt
<IntType
> endOffset(mStartOffset
+ aNewLength
);
97 mEndOffset
= endOffset
.isValid() ? endOffset
.value() : MaxOffset();
100 friend std::ostream
& operator<<(
101 std::ostream
& aStream
,
102 const StartAndEndOffsets
<IntType
>& aStartAndEndOffsets
) {
103 aStream
<< "{ mStartOffset=" << aStartAndEndOffsets
.mStartOffset
104 << ", mEndOffset=" << aStartAndEndOffsets
.mEndOffset
105 << ", Length()=" << aStartAndEndOffsets
.Length() << " }";
110 IntType mStartOffset
;
114 // OffsetAndData class is designed for storing composition string and its
115 // start offset. Length() and EndOffset() return only valid length or
116 // offset. I.e., if the string is too long for inserting at the offset,
117 // the length is shrunken. However, the string itself is not shrunken.
118 // Therefore, moving it to where all of the string can be contained,
119 // they will return longer/bigger value.
120 enum class OffsetAndDataFor
{
125 template <typename IntType
>
126 class OffsetAndData
{
128 static IntType
MaxOffset() { return std::numeric_limits
<IntType
>::max(); }
131 OffsetAndData() = delete;
132 explicit OffsetAndData(
133 IntType aStartOffset
, const nsAString
& aData
,
134 OffsetAndDataFor aFor
= OffsetAndDataFor::CompositionString
)
135 : mData(aData
), mOffset(aStartOffset
), mFor(aFor
) {}
137 bool IsValid() const {
138 CheckedInt
<IntType
> offset(mOffset
);
139 offset
+= mData
.Length();
140 return offset
.isValid();
142 IntType
StartOffset() const { return mOffset
; }
143 IntType
Length() const {
144 CheckedInt
<IntType
> endOffset(CheckedInt
<IntType
>(mOffset
) +
146 return endOffset
.isValid() ? mData
.Length() : MaxOffset() - mOffset
;
148 IntType
EndOffset() const { return mOffset
+ Length(); }
149 StartAndEndOffsets
<IntType
> CreateStartAndEndOffsets() const {
150 return StartAndEndOffsets
<IntType
>(StartOffset(), EndOffset());
152 const nsString
& DataRef() const {
153 // In strictly speaking, we should return substring which may be shrunken
154 // for rounding to the max offset. However, it's unrealistic edge case,
155 // and creating new string is not so cheap job in a hot path. Therefore,
156 // this just returns the data as-is.
159 bool IsDataEmpty() const { return mData
.IsEmpty(); }
161 bool IsOffsetInRange(IntType aOffset
) const {
162 return aOffset
>= mOffset
&& aOffset
< EndOffset();
164 bool IsOffsetInRangeOrEndOffset(IntType aOffset
) const {
165 return aOffset
>= mOffset
&& aOffset
<= EndOffset();
168 void Collapse(IntType aOffset
) {
172 void MoveTo(IntType aNewOffset
) { mOffset
= aNewOffset
; }
173 void SetOffsetAndData(IntType aStartOffset
, const nsAString
& aData
) {
174 mOffset
= aStartOffset
;
177 void SetData(const nsAString
& aData
) { mData
= aData
; }
178 void TruncateData(uint32_t aLength
= 0) { mData
.Truncate(aLength
); }
179 void ReplaceData(nsAString::size_type aCutStart
,
180 nsAString::size_type aCutLength
,
181 const nsAString
& aNewString
) {
182 mData
.Replace(aCutStart
, aCutLength
, aNewString
);
185 friend std::ostream
& operator<<(
186 std::ostream
& aStream
, const OffsetAndData
<IntType
>& aOffsetAndData
) {
187 const auto maxDataLength
=
188 aOffsetAndData
.mFor
== OffsetAndDataFor::CompositionString
189 ? PrintStringDetail::kMaxLengthForCompositionString
190 : (aOffsetAndData
.mFor
== OffsetAndDataFor::SelectedString
191 ? PrintStringDetail::kMaxLengthForSelectedString
192 : PrintStringDetail::kMaxLengthForEditor
);
193 aStream
<< "{ mOffset=" << aOffsetAndData
.mOffset
<< ", mData="
194 << PrintStringDetail(aOffsetAndData
.mData
, maxDataLength
).get()
195 << ", Length()=" << aOffsetAndData
.Length()
196 << ", EndOffset()=" << aOffsetAndData
.EndOffset() << " }";
203 OffsetAndDataFor mFor
;
209 * Preference for receiving IME updates
211 * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
212 * change and/or selection change and call nsIWidget::NotifyIME() with
213 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
214 * Please note that the text change observing cost is very expensive especially
215 * on an HTML editor has focus.
216 * If the IME implementation on a particular platform doesn't care about
217 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
218 * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
219 * If the IME implementation needs notifications even while our process is
220 * deactive, it should also set NOTIFY_DURING_DEACTIVE.
222 struct IMENotificationRequests final
{
223 typedef uint8_t Notifications
;
225 enum : Notifications
{
227 NOTIFY_TEXT_CHANGE
= 1 << 1,
228 NOTIFY_POSITION_CHANGE
= 1 << 2,
229 // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
230 // or released on a character in the focused editor. The notification is
231 // notified to IME as a mouse event. If it's consumed by IME, NotifyIME()
232 // returns NS_SUCCESS_EVENT_CONSUMED. Otherwise, it returns NS_OK if it's
233 // handled without any error.
234 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
= 1 << 3,
235 // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
236 // or more compositions are possible. E.g., Mac and Linux (GTK).
237 NOTIFY_DURING_DEACTIVE
= 1 << 7,
239 NOTIFY_ALL
= NOTIFY_TEXT_CHANGE
| NOTIFY_POSITION_CHANGE
|
240 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
,
243 IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING
) {}
245 explicit IMENotificationRequests(Notifications aWantUpdates
)
246 : mWantUpdates(aWantUpdates
) {}
248 IMENotificationRequests
operator|(
249 const IMENotificationRequests
& aOther
) const {
250 return IMENotificationRequests(aOther
.mWantUpdates
| mWantUpdates
);
252 IMENotificationRequests
& operator|=(const IMENotificationRequests
& aOther
) {
253 mWantUpdates
|= aOther
.mWantUpdates
;
256 bool operator==(const IMENotificationRequests
& aOther
) const {
257 return mWantUpdates
== aOther
.mWantUpdates
;
260 bool WantTextChange() const { return !!(mWantUpdates
& NOTIFY_TEXT_CHANGE
); }
262 bool WantPositionChanged() const {
263 return !!(mWantUpdates
& NOTIFY_POSITION_CHANGE
);
266 bool WantChanges() const { return WantTextChange(); }
268 bool WantMouseButtonEventOnChar() const {
269 return !!(mWantUpdates
& NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
);
272 bool WantDuringDeactive() const {
273 return !!(mWantUpdates
& NOTIFY_DURING_DEACTIVE
);
276 Notifications mWantUpdates
;
280 * IME enabled states.
282 * WARNING: If you change these values, you also need to edit:
283 * nsIDOMWindowUtils.idl
285 enum class IMEEnabled
{
287 * 'Disabled' means the user cannot use IME. So, the IME open state should
288 * be 'closed' during 'disabled'.
292 * 'Enabled' means the user can use IME.
296 * 'Password' state is a special case for the password editors.
297 * E.g., on mac, the password editors should disable the non-Roman
298 * keyboard layouts at getting focus. Thus, the password editor may have
299 * special rules on some platforms.
303 * 'Unknown' is useful when you cache this enum. So, this shouldn't be
304 * used with nsIWidget::SetInputContext().
310 * Contains IMEStatus plus information about the current
311 * input context that the IME can use as hints if desired.
314 struct IMEState final
{
318 * IME open states the mOpen value of SetInputContext() should be one value of
319 * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE. GetInputContext() should return
320 * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
324 * 'Unsupported' means the platform cannot return actual IME open state.
325 * This value is used only by GetInputContext().
327 OPEN_STATE_NOT_SUPPORTED
,
329 * 'Don't change' means the widget shouldn't change IME open state when
330 * SetInputContext() is called.
332 DONT_CHANGE_OPEN_STATE
= OPEN_STATE_NOT_SUPPORTED
,
334 * 'Open' means that IME should compose in its primary language (or latest
335 * input mode except direct ASCII character input mode). Even if IME is
336 * opened by this value, users should be able to close IME by theirselves.
337 * Web contents can specify this value by |ime-mode: active;|.
341 * 'Closed' means that IME shouldn't handle key events (or should handle
342 * as ASCII character inputs on mobile device). Even if IME is closed by
343 * this value, users should be able to open IME by theirselves.
344 * Web contents can specify this value by |ime-mode: inactive;|.
350 IMEState() : mEnabled(IMEEnabled::Enabled
), mOpen(DONT_CHANGE_OPEN_STATE
) {}
352 explicit IMEState(IMEEnabled aEnabled
, Open aOpen
= DONT_CHANGE_OPEN_STATE
)
353 : mEnabled(aEnabled
), mOpen(aOpen
) {}
355 // Returns true if the user can input characters.
356 // This means that a plain text editor, an HTML editor, a password editor or
357 // a plain text editor whose ime-mode is "disabled".
358 bool IsEditable() const {
359 return mEnabled
== IMEEnabled::Enabled
|| mEnabled
== IMEEnabled::Password
;
363 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
364 // If there can be only one IME composition in a process, this can be used.
365 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
366 (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
368 struct NativeIMEContext final
{
369 // Pointer to native IME context. Typically this is the result of
370 // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
371 // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
372 uintptr_t mRawNativeIMEContext
;
373 // Process ID of the origin of mNativeIMEContext.
374 // static_cast<uint64_t>(-1) if the instance is not initialized properly.
375 // 0 if the instance is originated in the parent process.
376 // 1 or greater if the instance is originated in a content process.
377 uint64_t mOriginProcessID
;
379 NativeIMEContext() : mRawNativeIMEContext(0), mOriginProcessID(0) {
383 explicit NativeIMEContext(nsIWidget
* aWidget
)
384 : mRawNativeIMEContext(0), mOriginProcessID(0) {
388 bool IsValid() const {
389 return mRawNativeIMEContext
&&
390 mOriginProcessID
!= static_cast<uint64_t>(-1);
393 bool IsOriginatedInParentProcess() const {
394 return mOriginProcessID
!= 0 &&
395 mOriginProcessID
!= static_cast<uint64_t>(-1);
398 void Init(nsIWidget
* aWidget
);
399 void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext
) {
400 InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext
));
402 void InitWithRawNativeIMEContext(void* aRawNativeIMEContext
);
404 bool operator==(const NativeIMEContext
& aOther
) const {
405 return mRawNativeIMEContext
== aOther
.mRawNativeIMEContext
&&
406 mOriginProcessID
== aOther
.mOriginProcessID
;
408 bool operator!=(const NativeIMEContext
& aOther
) const {
409 return !(*this == aOther
);
413 struct InputContext final
{
415 : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN
: ORIGIN_CONTENT
),
416 mHasHandledUserInput(false),
417 mInPrivateBrowsing(false) {}
419 // If InputContext instance is a static variable, any heap allocated stuff
420 // of its members need to be deleted at XPCOM shutdown. Otherwise, it's
421 // detected as memory leak.
424 mHTMLInputType
.Truncate();
425 mHTMLInputMode
.Truncate();
426 mActionHint
.Truncate();
427 mAutocapitalize
.Truncate();
430 bool IsPasswordEditor() const {
431 return mHTMLInputType
.LowerCaseEqualsLiteral("password");
434 NativeKeyBindingsType
GetNativeKeyBindingsType() const {
435 MOZ_DIAGNOSTIC_ASSERT(mIMEState
.IsEditable());
436 // See GetInputType in IMEStateManager.cpp
437 if (mHTMLInputType
.IsEmpty()) {
438 return NativeKeyBindingsType::RichTextEditor
;
440 return mHTMLInputType
.EqualsLiteral("textarea")
441 ? NativeKeyBindingsType::MultiLineEditor
442 : NativeKeyBindingsType::SingleLineEditor
;
445 // https://html.spec.whatwg.org/dev/interaction.html#autocapitalization
446 bool IsAutocapitalizeSupported() const {
447 return !mHTMLInputType
.EqualsLiteral("password") &&
448 !mHTMLInputType
.EqualsLiteral("url") &&
449 !mHTMLInputType
.EqualsLiteral("email");
452 bool IsInputAttributeChanged(const InputContext
& aOldContext
) const {
453 return mIMEState
.mEnabled
!= aOldContext
.mIMEState
.mEnabled
||
454 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN) || \
456 // input type and inputmode are supported by Windows IME API, GTK
457 // IME API, Android IME API and iOS API.
458 mHTMLInputType
!= aOldContext
.mHTMLInputType
||
459 mHTMLInputMode
!= aOldContext
.mHTMLInputMode
||
461 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_IOS)
462 // autocapitalize is supported by Android IME API, GTK IME API, and
464 mAutocapitalize
!= aOldContext
.mAutocapitalize
||
466 #if defined(ANDROID) || defined(XP_IOS)
467 // enterkeyhint is only supported by Android IME API and iOS API.
468 mActionHint
!= aOldContext
.mActionHint
||
475 // The URI of the document which has the editable element.
476 nsCOMPtr
<nsIURI
> mURI
;
478 /* The type of the input if the input is a html input field */
479 nsString mHTMLInputType
;
481 // The value of the inputmode
482 nsString mHTMLInputMode
;
484 /* A hint for the action that is performed when the input is submitted */
485 nsString mActionHint
;
487 /* A hint for autocapitalize */
488 nsString mAutocapitalize
;
491 * mOrigin indicates whether this focus event refers to main or remote
495 // Adjusting focus of content on the main process
497 // Adjusting focus of content in a remote process
503 * True if the document has ever received user input
505 bool mHasHandledUserInput
;
507 /* Whether the owning document of the input element has been loaded
508 * in private browsing mode. */
509 bool mInPrivateBrowsing
;
511 bool IsOriginMainProcess() const { return mOrigin
== ORIGIN_MAIN
; }
513 bool IsOriginContentProcess() const { return mOrigin
== ORIGIN_CONTENT
; }
515 bool IsOriginCurrentProcess() const {
516 if (XRE_IsParentProcess()) {
517 return IsOriginMainProcess();
519 return IsOriginContentProcess();
523 // FYI: Implemented in nsBaseWidget.cpp
524 const char* ToChar(InputContext::Origin aOrigin
);
526 struct InputContextAction final
{
528 * mCause indicates what action causes calling nsIWidget::SetInputContext().
529 * It must be one of following values.
532 // The cause is unknown but originated from content. Focus might have been
533 // changed by content script.
535 // The cause is unknown but originated from chrome. Focus might have been
536 // changed by chrome script.
537 CAUSE_UNKNOWN_CHROME
,
538 // The cause is user's keyboard operation.
540 // The cause is user's mouse operation.
542 // The cause is user's touch operation (implies mouse)
544 // The cause is users' long press operation.
546 // The cause is unknown but it occurs during user input except keyboard
547 // input. E.g., an event handler of a user input event moves focus.
548 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
,
549 // The cause is unknown but it occurs during keyboard input.
550 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
,
555 * mFocusChange indicates what happened for focus.
559 // A content got focus.
561 // Focused content lost focus.
563 // Menu got pseudo focus that means focused content isn't changed but
564 // keyboard events will be handled by menu.
565 MENU_GOT_PSEUDO_FOCUS
,
566 // Menu lost pseudo focus that means focused content will handle keyboard
568 MENU_LOST_PSEUDO_FOCUS
,
569 // The widget is created. When a widget is crated, it may need to notify
570 // IME module to initialize its native IME context. In such case, this is
571 // used. I.e., this isn't used by IMEStateManager.
574 FocusChange mFocusChange
;
576 bool ContentGotFocusByTrustedCause() const {
577 return (mFocusChange
== GOT_FOCUS
&& mCause
!= CAUSE_UNKNOWN
);
580 bool UserMightRequestOpenVKB() const {
581 // If focus is changed, user must not request to open VKB.
582 if (mFocusChange
!= FOCUS_NOT_CHANGED
) {
586 // If user clicks or touches focused editor, user must request to open
590 // If script does something during a user input and that causes changing
591 // input context, user might request to open VKB. E.g., user clicks
592 // dummy editor and JS moves focus to an actual editable node. However,
593 // this should return false if the user input is a keyboard event since
594 // physical keyboard operation shouldn't cause opening VKB.
595 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
:
603 * IsHandlingUserInput() returns true if it's caused by a user action directly
604 * or it's caused by script or something but it occurred while we're handling
605 * a user action. E.g., when it's caused by Element.focus() in an event
606 * handler of a user input, this returns true.
608 static bool IsHandlingUserInput(Cause aCause
) {
613 case CAUSE_LONGPRESS
:
614 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
:
615 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
:
622 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause
); }
625 : mCause(CAUSE_UNKNOWN
), mFocusChange(FOCUS_NOT_CHANGED
) {}
627 explicit InputContextAction(Cause aCause
,
628 FocusChange aFocusChange
= FOCUS_NOT_CHANGED
)
629 : mCause(aCause
), mFocusChange(aFocusChange
) {}
632 // IMEMessage is shared by IMEStateManager and TextComposition.
633 // Update values in GeckoEditable.java if you make changes here.
634 // XXX Negative values are used in Android...
635 typedef int8_t IMEMessageType
;
636 enum IMEMessage
: IMEMessageType
{
637 // This is used by IMENotification internally. This means that the instance
638 // hasn't been initialized yet.
639 NOTIFY_IME_OF_NOTHING
,
640 // An editable content is getting focus
642 // An editable content is losing focus
644 // Selection in the focused editable content is changed
645 NOTIFY_IME_OF_SELECTION_CHANGE
,
646 // Text in the focused editable content is changed
647 NOTIFY_IME_OF_TEXT_CHANGE
,
648 // Notified when a dispatched composition event is handled by the
649 // contents. This must be notified after the other notifications.
650 // Note that if a remote process has focus, this is notified only once when
651 // all dispatched events are handled completely. So, the receiver shouldn't
652 // count number of received this notification for comparing with the number
653 // of dispatched events.
654 // NOTE: If a composition event causes moving focus from the focused editor,
655 // this notification may not be notified as usual. Even in such case,
656 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
657 // should tread the blur notification as including this if there is
658 // pending composition events.
659 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
,
660 // Position or size of focused element may be changed.
661 NOTIFY_IME_OF_POSITION_CHANGE
,
662 // Mouse button event is fired on a character in focused editor
663 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
,
664 // Request to commit current composition to IME
665 // (some platforms may not support)
666 REQUEST_TO_COMMIT_COMPOSITION
,
667 // Request to cancel current composition to IME
668 // (some platforms may not support)
669 REQUEST_TO_CANCEL_COMPOSITION
672 // FYI: Implemented in nsBaseWidget.cpp
673 const char* ToChar(IMEMessage aIMEMessage
);
675 struct IMENotification final
{
676 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING
), mSelectionChangeData() {}
678 IMENotification(const IMENotification
& aOther
)
679 : mMessage(NOTIFY_IME_OF_NOTHING
) {
683 ~IMENotification() { Clear(); }
685 MOZ_IMPLICIT
IMENotification(IMEMessage aMessage
)
686 : mMessage(aMessage
), mSelectionChangeData() {
688 case NOTIFY_IME_OF_SELECTION_CHANGE
:
689 mSelectionChangeData
.mString
= new nsString();
690 mSelectionChangeData
.Clear();
692 case NOTIFY_IME_OF_TEXT_CHANGE
:
693 mTextChangeData
.Clear();
695 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
696 mMouseButtonEventData
.mEventMessage
= eVoidEvent
;
697 mMouseButtonEventData
.mOffset
= UINT32_MAX
;
698 mMouseButtonEventData
.mCursorPos
.MoveTo(0, 0);
699 mMouseButtonEventData
.mCharRect
.SetRect(0, 0, 0, 0);
700 mMouseButtonEventData
.mButton
= -1;
701 mMouseButtonEventData
.mButtons
= 0;
702 mMouseButtonEventData
.mModifiers
= 0;
709 void Assign(const IMENotification
& aOther
) {
710 bool changingMessage
= mMessage
!= aOther
.mMessage
;
711 if (changingMessage
) {
713 mMessage
= aOther
.mMessage
;
716 case NOTIFY_IME_OF_SELECTION_CHANGE
:
717 if (changingMessage
) {
718 mSelectionChangeData
.mString
= new nsString();
720 mSelectionChangeData
.Assign(aOther
.mSelectionChangeData
);
722 case NOTIFY_IME_OF_TEXT_CHANGE
:
723 mTextChangeData
= aOther
.mTextChangeData
;
725 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
726 mMouseButtonEventData
= aOther
.mMouseButtonEventData
;
733 IMENotification
& operator=(const IMENotification
& aOther
) {
739 if (mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
) {
740 MOZ_ASSERT(mSelectionChangeData
.mString
);
741 delete mSelectionChangeData
.mString
;
742 mSelectionChangeData
.mString
= nullptr;
744 mMessage
= NOTIFY_IME_OF_NOTHING
;
747 bool HasNotification() const { return mMessage
!= NOTIFY_IME_OF_NOTHING
; }
749 void MergeWith(const IMENotification
& aNotification
) {
751 case NOTIFY_IME_OF_NOTHING
:
752 MOZ_ASSERT(aNotification
.mMessage
!= NOTIFY_IME_OF_NOTHING
);
753 Assign(aNotification
);
755 case NOTIFY_IME_OF_SELECTION_CHANGE
:
756 MOZ_ASSERT(aNotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
);
757 mSelectionChangeData
.Assign(aNotification
.mSelectionChangeData
);
759 case NOTIFY_IME_OF_TEXT_CHANGE
:
760 MOZ_ASSERT(aNotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
);
761 mTextChangeData
+= aNotification
.mTextChangeData
;
763 case NOTIFY_IME_OF_POSITION_CHANGE
:
764 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
765 MOZ_ASSERT(aNotification
.mMessage
== mMessage
);
768 MOZ_CRASH("Merging notification isn't supported");
775 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
776 struct SelectionChangeDataBase
{
783 // Writing mode at the selection.
784 uint8_t mWritingModeBits
;
789 bool mCausedByComposition
;
790 bool mCausedBySelectionEvent
;
791 bool mOccurredDuringComposition
;
793 void SetWritingMode(const WritingMode
& aWritingMode
);
794 WritingMode
GetWritingMode() const;
796 uint32_t StartOffset() const {
797 MOZ_ASSERT(mHasRange
);
800 uint32_t EndOffset() const {
801 MOZ_ASSERT(mHasRange
);
802 return mOffset
+ Length();
804 uint32_t AnchorOffset() const {
805 MOZ_ASSERT(mHasRange
);
806 return mOffset
+ (mReversed
? Length() : 0);
808 uint32_t FocusOffset() const {
809 MOZ_ASSERT(mHasRange
);
810 return mOffset
+ (mReversed
? 0 : Length());
812 const nsString
& String() const {
813 MOZ_ASSERT(mHasRange
);
816 uint32_t Length() const {
817 MOZ_ASSERT(mHasRange
);
818 return mString
->Length();
820 bool IsInInt32Range() const {
821 return mHasRange
&& mOffset
<= INT32_MAX
&& Length() <= INT32_MAX
&&
822 mOffset
+ Length() <= INT32_MAX
;
824 bool HasRange() const { return mIsInitialized
&& mHasRange
; }
825 bool IsCollapsed() const { return !mHasRange
|| mString
->IsEmpty(); }
826 void ClearSelectionData() {
827 mIsInitialized
= false;
829 mOffset
= UINT32_MAX
;
831 mWritingModeBits
= 0;
835 ClearSelectionData();
836 mCausedByComposition
= false;
837 mCausedBySelectionEvent
= false;
838 mOccurredDuringComposition
= false;
840 bool IsInitialized() const { return mIsInitialized
; }
841 void Assign(const SelectionChangeDataBase
& aOther
) {
842 mIsInitialized
= aOther
.mIsInitialized
;
843 mHasRange
= aOther
.mHasRange
;
844 if (mIsInitialized
&& mHasRange
) {
845 mOffset
= aOther
.mOffset
;
846 *mString
= aOther
.String();
847 mReversed
= aOther
.mReversed
;
848 mWritingModeBits
= aOther
.mWritingModeBits
;
850 mOffset
= UINT32_MAX
;
853 // Let's keep the writing mode for avoiding temporarily changing the
854 // writing mode at no selection range.
856 AssignReason(aOther
.mCausedByComposition
, aOther
.mCausedBySelectionEvent
,
857 aOther
.mOccurredDuringComposition
);
859 void Assign(const WidgetQueryContentEvent
& aQuerySelectedTextEvent
);
860 void AssignReason(bool aCausedByComposition
, bool aCausedBySelectionEvent
,
861 bool aOccurredDuringComposition
) {
862 mCausedByComposition
= aCausedByComposition
;
863 mCausedBySelectionEvent
= aCausedBySelectionEvent
;
864 mOccurredDuringComposition
= aOccurredDuringComposition
;
867 bool EqualsRange(const SelectionChangeDataBase
& aOther
) const {
868 if (HasRange() != aOther
.HasRange()) {
874 return mOffset
== aOther
.mOffset
&& mString
->Equals(*aOther
.mString
);
876 bool EqualsRangeAndDirection(const SelectionChangeDataBase
& aOther
) const {
877 return EqualsRange(aOther
) &&
878 (!HasRange() || mReversed
== aOther
.mReversed
);
880 bool EqualsRangeAndDirectionAndWritingMode(
881 const SelectionChangeDataBase
& aOther
) const {
882 return EqualsRangeAndDirection(aOther
) &&
883 mWritingModeBits
== aOther
.mWritingModeBits
;
886 bool EqualsRange(const ContentSelection
& aContentSelection
) const;
887 bool EqualsRangeAndWritingMode(
888 const ContentSelection
& aContentSelection
) const;
890 OffsetAndData
<uint32_t> ToUint32OffsetAndData() const {
891 return OffsetAndData
<uint32_t>(mOffset
, *mString
,
892 OffsetAndDataFor::SelectedString
);
896 // SelectionChangeDataBase cannot have constructors because it's used in
897 // the union. Therefore, SelectionChangeData should only implement
898 // constructors. In other words, add other members to
899 // SelectionChangeDataBase.
900 struct SelectionChangeData final
: public SelectionChangeDataBase
{
901 SelectionChangeData() {
902 mString
= &mStringInstance
;
905 explicit SelectionChangeData(const SelectionChangeDataBase
& aOther
) {
906 mString
= &mStringInstance
;
909 SelectionChangeData(const SelectionChangeData
& aOther
) {
910 mString
= &mStringInstance
;
913 SelectionChangeData
& operator=(const SelectionChangeDataBase
& aOther
) {
914 mString
= &mStringInstance
;
918 SelectionChangeData
& operator=(const SelectionChangeData
& aOther
) {
919 mString
= &mStringInstance
;
925 // When SelectionChangeData is used outside of union, it shouldn't create
926 // nsString instance in the heap as far as possible.
927 nsString mStringInstance
;
930 struct TextChangeDataBase
{
931 // mStartOffset is the start offset of modified or removed text in
932 // original content and inserted text in new content.
933 uint32_t mStartOffset
;
934 // mRemovalEndOffset is the end offset of modified or removed text in
935 // original content. If the value is same as mStartOffset, no text hasn't
937 uint32_t mRemovedEndOffset
;
938 // mAddedEndOffset is the end offset of inserted text or same as
939 // mStartOffset if just removed. The vlaue is offset in the new content.
940 uint32_t mAddedEndOffset
;
942 // Note that TextChangeDataBase may be the result of merging two or more
943 // changes especially in e10s mode.
945 // mCausedOnlyByComposition is true only when *all* merged changes are
946 // caused by composition.
947 bool mCausedOnlyByComposition
;
948 // mIncludingChangesDuringComposition is true if at least one change which
949 // is not caused by composition occurred during the last composition.
950 // Note that if after the last composition is finished and there are some
951 // changes not caused by composition, this is set to false.
952 bool mIncludingChangesDuringComposition
;
953 // mIncludingChangesWithoutComposition is true if there is at least one
954 // change which did occur when there wasn't a composition ongoing.
955 bool mIncludingChangesWithoutComposition
;
957 uint32_t OldLength() const {
958 MOZ_ASSERT(IsValid());
959 return mRemovedEndOffset
- mStartOffset
;
961 uint32_t NewLength() const {
962 MOZ_ASSERT(IsValid());
963 return mAddedEndOffset
- mStartOffset
;
966 // Positive if text is added. Negative if text is removed.
967 int64_t Difference() const { return mAddedEndOffset
- mRemovedEndOffset
; }
969 bool IsInInt32Range() const {
970 MOZ_ASSERT(IsValid());
971 return mStartOffset
<= INT32_MAX
&& mRemovedEndOffset
<= INT32_MAX
&&
972 mAddedEndOffset
<= INT32_MAX
;
975 bool IsValid() const {
976 return !(mStartOffset
== UINT32_MAX
&& !mRemovedEndOffset
&&
981 mStartOffset
= UINT32_MAX
;
982 mRemovedEndOffset
= mAddedEndOffset
= 0;
985 void MergeWith(const TextChangeDataBase
& aOther
);
986 TextChangeDataBase
& operator+=(const TextChangeDataBase
& aOther
) {
993 #endif // #ifdef DEBUG
996 // TextChangeDataBase cannot have constructors because they are used in union.
997 // Therefore, TextChangeData should only implement constructor. In other
998 // words, add other members to TextChangeDataBase.
999 struct TextChangeData
: public TextChangeDataBase
{
1000 TextChangeData() { Clear(); }
1002 TextChangeData(uint32_t aStartOffset
, uint32_t aRemovedEndOffset
,
1003 uint32_t aAddedEndOffset
, bool aCausedByComposition
,
1004 bool aOccurredDuringComposition
) {
1005 MOZ_ASSERT(aRemovedEndOffset
>= aStartOffset
,
1006 "removed end offset must not be smaller than start offset");
1007 MOZ_ASSERT(aAddedEndOffset
>= aStartOffset
,
1008 "added end offset must not be smaller than start offset");
1009 mStartOffset
= aStartOffset
;
1010 mRemovedEndOffset
= aRemovedEndOffset
;
1011 mAddedEndOffset
= aAddedEndOffset
;
1012 mCausedOnlyByComposition
= aCausedByComposition
;
1013 mIncludingChangesDuringComposition
=
1014 !aCausedByComposition
&& aOccurredDuringComposition
;
1015 mIncludingChangesWithoutComposition
=
1016 !aCausedByComposition
&& !aOccurredDuringComposition
;
1020 struct MouseButtonEventData
{
1021 // The value of WidgetEvent::mMessage
1022 EventMessage mEventMessage
;
1023 // Character offset from the start of the focused editor under the cursor
1025 // Cursor position in pixels relative to the widget
1026 LayoutDeviceIntPoint mCursorPos
;
1027 // Character rect in pixels under the cursor relative to the widget
1028 LayoutDeviceIntRect mCharRect
;
1029 // The value of WidgetMouseEventBase::button and buttons
1032 // The value of WidgetInputEvent::modifiers
1033 Modifiers mModifiers
;
1037 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
1038 SelectionChangeDataBase mSelectionChangeData
;
1040 // NOTIFY_IME_OF_TEXT_CHANGE specific data
1041 TextChangeDataBase mTextChangeData
;
1043 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
1044 MouseButtonEventData mMouseButtonEventData
;
1047 void SetData(const SelectionChangeDataBase
& aSelectionChangeData
) {
1048 MOZ_RELEASE_ASSERT(mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
);
1049 mSelectionChangeData
.Assign(aSelectionChangeData
);
1052 void SetData(const TextChangeDataBase
& aTextChangeData
) {
1053 MOZ_RELEASE_ASSERT(mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
);
1054 mTextChangeData
= aTextChangeData
;
1058 struct CandidateWindowPosition
{
1059 // Upper left corner of the candidate window if mExcludeRect is false.
1060 // Otherwise, the position currently interested. E.g., caret position.
1061 LayoutDeviceIntPoint mPoint
;
1062 // Rect which shouldn't be overlapped with the candidate window.
1063 // This is valid only when mExcludeRect is true.
1064 LayoutDeviceIntRect mRect
;
1065 // See explanation of mPoint and mRect.
1069 std::ostream
& operator<<(std::ostream
& aStream
, const IMEEnabled
& aEnabled
);
1070 std::ostream
& operator<<(std::ostream
& aStream
, const IMEState::Open
& aOpen
);
1071 std::ostream
& operator<<(std::ostream
& aStream
, const IMEState
& aState
);
1072 std::ostream
& operator<<(std::ostream
& aStream
,
1073 const InputContext::Origin
& aOrigin
);
1074 std::ostream
& operator<<(std::ostream
& aStream
, const InputContext
& aContext
);
1075 std::ostream
& operator<<(std::ostream
& aStream
,
1076 const InputContextAction::Cause
& aCause
);
1077 std::ostream
& operator<<(std::ostream
& aStream
,
1078 const InputContextAction::FocusChange
& aFocusChange
);
1079 std::ostream
& operator<<(std::ostream
& aStream
,
1080 const IMENotification::SelectionChangeDataBase
& aData
);
1081 std::ostream
& operator<<(std::ostream
& aStream
,
1082 const IMENotification::TextChangeDataBase
& aData
);
1084 } // namespace widget
1085 } // namespace mozilla
1087 #endif // #ifndef mozilla_widget_IMEData_h_