Bug 1753131 - wait for focus before dispatching devicechange events r=jib
[gecko.git] / widget / IMEData.h
blob11006bbeedeb7036b2900300e21ca6b5038908ea
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"
13 #include "nsPoint.h"
14 #include "nsRect.h"
15 #include "nsString.h"
16 #include "nsXULAppAPI.h"
17 #include "Units.h"
19 class nsIWidget;
21 namespace mozilla {
23 class WritingMode;
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 {
28 public:
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);
37 private:
38 static nsCString PrintCharData(char32_t aChar);
41 // StartAndEndOffsets represents a range in flat-text.
42 template <typename IntType>
43 class StartAndEndOffsets {
44 protected:
45 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
47 public:
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;
69 mEndOffset += 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() << " }";
96 return aStream;
99 private:
100 IntType mStartOffset;
101 IntType mEndOffset;
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 {
111 CompositionString,
112 SelectedString,
113 EditorString,
115 template <typename IntType>
116 class OffsetAndData {
117 protected:
118 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
120 public:
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.
141 return mData;
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;
155 mData = aData;
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() << " }";
177 return aStream;
180 private:
181 nsString mData;
182 IntType mOffset;
183 OffsetAndDataFor mFor;
186 namespace widget {
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 {
206 NOTIFY_NOTHING = 0,
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;
234 return *this;
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'.
270 Disabled,
272 * 'Enabled' means the user can use IME.
274 Enabled,
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.
281 Password,
283 * 'Unknown' is useful when you cache this enum. So, this shouldn't be
284 * used with nsIWidget::SetInputContext().
286 Unknown,
290 * Contains IMEStatus plus information about the current
291 * input context that the IME can use as hints if desired.
294 struct IMEState final {
295 IMEEnabled mEnabled;
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.
302 enum Open {
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;|.
319 OPEN,
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;|.
326 CLOSED
328 Open mOpen;
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) {
357 Init(nullptr);
360 explicit NativeIMEContext(nsIWidget* aWidget)
361 : mRawNativeIMEContext(0), mOriginProcessID(0) {
362 Init(aWidget);
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 {
386 InputContext()
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.
395 void ShutDown() {
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");
413 bool IsInputAttributeChanged(const InputContext& aOldContext) const {
414 return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled ||
415 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
416 // input type and inputmode are supported by Windows IME API, GTK
417 // IME API and Android IME API
418 mHTMLInputType != aOldContext.mHTMLInputType ||
419 mHTMLInputInputmode != aOldContext.mHTMLInputInputmode ||
420 #endif
421 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK)
422 // autocapitalize is supported by Android IME API and GTK IME API
423 mAutocapitalize != aOldContext.mAutocapitalize ||
424 #endif
425 #if defined(ANDROID)
426 // enterkeyhint is only supported by Android IME API.
427 mActionHint != aOldContext.mActionHint ||
428 #endif
429 false;
432 IMEState mIMEState;
434 /* The type of the input if the input is a html input field */
435 nsString mHTMLInputType;
437 /* The type of the inputmode */
438 nsString mHTMLInputInputmode;
440 /* A hint for the action that is performed when the input is submitted */
441 nsString mActionHint;
443 /* A hint for autocapitalize */
444 nsString mAutocapitalize;
447 * mOrigin indicates whether this focus event refers to main or remote
448 * content.
450 enum Origin {
451 // Adjusting focus of content on the main process
452 ORIGIN_MAIN,
453 // Adjusting focus of content in a remote process
454 ORIGIN_CONTENT
456 Origin mOrigin;
458 /* True if the webapp may be unaware of IME events such as input event or
459 * composiion events. This enables a key-events-only mode on Android for
460 * compatibility with webapps relying on key listeners. */
461 bool mMayBeIMEUnaware;
464 * True if the document has ever received user input
466 bool mHasHandledUserInput;
468 /* Whether the owning document of the input element has been loaded
469 * in private browsing mode. */
470 bool mInPrivateBrowsing;
472 bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; }
474 bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; }
476 bool IsOriginCurrentProcess() const {
477 if (XRE_IsParentProcess()) {
478 return IsOriginMainProcess();
480 return IsOriginContentProcess();
484 // FYI: Implemented in nsBaseWidget.cpp
485 const char* ToChar(InputContext::Origin aOrigin);
487 struct InputContextAction final {
489 * mCause indicates what action causes calling nsIWidget::SetInputContext().
490 * It must be one of following values.
492 enum Cause {
493 // The cause is unknown but originated from content. Focus might have been
494 // changed by content script.
495 CAUSE_UNKNOWN,
496 // The cause is unknown but originated from chrome. Focus might have been
497 // changed by chrome script.
498 CAUSE_UNKNOWN_CHROME,
499 // The cause is user's keyboard operation.
500 CAUSE_KEY,
501 // The cause is user's mouse operation.
502 CAUSE_MOUSE,
503 // The cause is user's touch operation (implies mouse)
504 CAUSE_TOUCH,
505 // The cause is users' long press operation.
506 CAUSE_LONGPRESS,
507 // The cause is unknown but it occurs during user input except keyboard
508 // input. E.g., an event handler of a user input event moves focus.
509 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
510 // The cause is unknown but it occurs during keyboard input.
511 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
513 Cause mCause;
516 * mFocusChange indicates what happened for focus.
518 enum FocusChange {
519 FOCUS_NOT_CHANGED,
520 // A content got focus.
521 GOT_FOCUS,
522 // Focused content lost focus.
523 LOST_FOCUS,
524 // Menu got pseudo focus that means focused content isn't changed but
525 // keyboard events will be handled by menu.
526 MENU_GOT_PSEUDO_FOCUS,
527 // Menu lost pseudo focus that means focused content will handle keyboard
528 // events.
529 MENU_LOST_PSEUDO_FOCUS,
530 // The widget is created. When a widget is crated, it may need to notify
531 // IME module to initialize its native IME context. In such case, this is
532 // used. I.e., this isn't used by IMEStateManager.
533 WIDGET_CREATED
535 FocusChange mFocusChange;
537 bool ContentGotFocusByTrustedCause() const {
538 return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN);
541 bool UserMightRequestOpenVKB() const {
542 // If focus is changed, user must not request to open VKB.
543 if (mFocusChange != FOCUS_NOT_CHANGED) {
544 return false;
546 switch (mCause) {
547 // If user clicks or touches focused editor, user must request to open
548 // VKB.
549 case CAUSE_MOUSE:
550 case CAUSE_TOUCH:
551 // If script does something during a user input and that causes changing
552 // input context, user might request to open VKB. E.g., user clicks
553 // dummy editor and JS moves focus to an actual editable node. However,
554 // this should return false if the user input is a keyboard event since
555 // physical keyboard operation shouldn't cause opening VKB.
556 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
557 return true;
558 default:
559 return false;
564 * IsHandlingUserInput() returns true if it's caused by a user action directly
565 * or it's caused by script or something but it occurred while we're handling
566 * a user action. E.g., when it's caused by Element.focus() in an event
567 * handler of a user input, this returns true.
569 static bool IsHandlingUserInput(Cause aCause) {
570 switch (aCause) {
571 case CAUSE_KEY:
572 case CAUSE_MOUSE:
573 case CAUSE_TOUCH:
574 case CAUSE_LONGPRESS:
575 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
576 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
577 return true;
578 default:
579 return false;
583 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); }
585 InputContextAction()
586 : mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED) {}
588 explicit InputContextAction(Cause aCause,
589 FocusChange aFocusChange = FOCUS_NOT_CHANGED)
590 : mCause(aCause), mFocusChange(aFocusChange) {}
593 // IMEMessage is shared by IMEStateManager and TextComposition.
594 // Update values in GeckoEditable.java if you make changes here.
595 // XXX Negative values are used in Android...
596 typedef int8_t IMEMessageType;
597 enum IMEMessage : IMEMessageType {
598 // This is used by IMENotification internally. This means that the instance
599 // hasn't been initialized yet.
600 NOTIFY_IME_OF_NOTHING,
601 // An editable content is getting focus
602 NOTIFY_IME_OF_FOCUS,
603 // An editable content is losing focus
604 NOTIFY_IME_OF_BLUR,
605 // Selection in the focused editable content is changed
606 NOTIFY_IME_OF_SELECTION_CHANGE,
607 // Text in the focused editable content is changed
608 NOTIFY_IME_OF_TEXT_CHANGE,
609 // Notified when a dispatched composition event is handled by the
610 // contents. This must be notified after the other notifications.
611 // Note that if a remote process has focus, this is notified only once when
612 // all dispatched events are handled completely. So, the receiver shouldn't
613 // count number of received this notification for comparing with the number
614 // of dispatched events.
615 // NOTE: If a composition event causes moving focus from the focused editor,
616 // this notification may not be notified as usual. Even in such case,
617 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
618 // should tread the blur notification as including this if there is
619 // pending composition events.
620 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
621 // Position or size of focused element may be changed.
622 NOTIFY_IME_OF_POSITION_CHANGE,
623 // Mouse button event is fired on a character in focused editor
624 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
625 // Request to commit current composition to IME
626 // (some platforms may not support)
627 REQUEST_TO_COMMIT_COMPOSITION,
628 // Request to cancel current composition to IME
629 // (some platforms may not support)
630 REQUEST_TO_CANCEL_COMPOSITION
633 // FYI: Implemented in nsBaseWidget.cpp
634 const char* ToChar(IMEMessage aIMEMessage);
636 struct IMENotification final {
637 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING), mSelectionChangeData() {}
639 IMENotification(const IMENotification& aOther)
640 : mMessage(NOTIFY_IME_OF_NOTHING) {
641 Assign(aOther);
644 ~IMENotification() { Clear(); }
646 MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
647 : mMessage(aMessage), mSelectionChangeData() {
648 switch (aMessage) {
649 case NOTIFY_IME_OF_SELECTION_CHANGE:
650 mSelectionChangeData.mString = new nsString();
651 mSelectionChangeData.Clear();
652 break;
653 case NOTIFY_IME_OF_TEXT_CHANGE:
654 mTextChangeData.Clear();
655 break;
656 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
657 mMouseButtonEventData.mEventMessage = eVoidEvent;
658 mMouseButtonEventData.mOffset = UINT32_MAX;
659 mMouseButtonEventData.mCursorPos.MoveTo(0, 0);
660 mMouseButtonEventData.mCharRect.SetRect(0, 0, 0, 0);
661 mMouseButtonEventData.mButton = -1;
662 mMouseButtonEventData.mButtons = 0;
663 mMouseButtonEventData.mModifiers = 0;
664 break;
665 default:
666 break;
670 void Assign(const IMENotification& aOther) {
671 bool changingMessage = mMessage != aOther.mMessage;
672 if (changingMessage) {
673 Clear();
674 mMessage = aOther.mMessage;
676 switch (mMessage) {
677 case NOTIFY_IME_OF_SELECTION_CHANGE:
678 if (changingMessage) {
679 mSelectionChangeData.mString = new nsString();
681 mSelectionChangeData.Assign(aOther.mSelectionChangeData);
682 break;
683 case NOTIFY_IME_OF_TEXT_CHANGE:
684 mTextChangeData = aOther.mTextChangeData;
685 break;
686 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
687 mMouseButtonEventData = aOther.mMouseButtonEventData;
688 break;
689 default:
690 break;
694 IMENotification& operator=(const IMENotification& aOther) {
695 Assign(aOther);
696 return *this;
699 void Clear() {
700 if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
701 MOZ_ASSERT(mSelectionChangeData.mString);
702 delete mSelectionChangeData.mString;
703 mSelectionChangeData.mString = nullptr;
705 mMessage = NOTIFY_IME_OF_NOTHING;
708 bool HasNotification() const { return mMessage != NOTIFY_IME_OF_NOTHING; }
710 void MergeWith(const IMENotification& aNotification) {
711 switch (mMessage) {
712 case NOTIFY_IME_OF_NOTHING:
713 MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
714 Assign(aNotification);
715 break;
716 case NOTIFY_IME_OF_SELECTION_CHANGE:
717 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
718 mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
719 break;
720 case NOTIFY_IME_OF_TEXT_CHANGE:
721 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
722 mTextChangeData += aNotification.mTextChangeData;
723 break;
724 case NOTIFY_IME_OF_POSITION_CHANGE:
725 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
726 MOZ_ASSERT(aNotification.mMessage == mMessage);
727 break;
728 default:
729 MOZ_CRASH("Merging notification isn't supported");
730 break;
734 IMEMessage mMessage;
736 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
737 struct SelectionChangeDataBase {
738 // Selection range.
739 uint32_t mOffset;
741 // Selected string
742 nsString* mString;
744 // Writing mode at the selection.
745 uint8_t mWritingMode;
747 bool mReversed;
748 bool mCausedByComposition;
749 bool mCausedBySelectionEvent;
750 bool mOccurredDuringComposition;
752 void SetWritingMode(const WritingMode& aWritingMode);
753 WritingMode GetWritingMode() const;
755 uint32_t StartOffset() const {
756 return mOffset + (mReversed ? Length() : 0);
758 uint32_t EndOffset() const { return mOffset + (mReversed ? 0 : Length()); }
759 const nsString& String() const { return *mString; }
760 uint32_t Length() const { return mString->Length(); }
761 bool IsInInt32Range() const { return mOffset + Length() <= INT32_MAX; }
762 bool IsCollapsed() const { return mString->IsEmpty(); }
763 void ClearSelectionData() {
764 mOffset = UINT32_MAX;
765 mString->Truncate();
766 mWritingMode = 0;
767 mReversed = false;
769 void Clear() {
770 ClearSelectionData();
771 mCausedByComposition = false;
772 mCausedBySelectionEvent = false;
773 mOccurredDuringComposition = false;
775 bool IsValid() const { return mOffset != UINT32_MAX; }
776 void Assign(const SelectionChangeDataBase& aOther) {
777 mOffset = aOther.mOffset;
778 *mString = aOther.String();
779 mWritingMode = aOther.mWritingMode;
780 mReversed = aOther.mReversed;
781 AssignReason(aOther.mCausedByComposition, aOther.mCausedBySelectionEvent,
782 aOther.mOccurredDuringComposition);
784 void AssignReason(bool aCausedByComposition, bool aCausedBySelectionEvent,
785 bool aOccurredDuringComposition) {
786 mCausedByComposition = aCausedByComposition;
787 mCausedBySelectionEvent = aCausedBySelectionEvent;
788 mOccurredDuringComposition = aOccurredDuringComposition;
792 // SelectionChangeDataBase cannot have constructors because it's used in
793 // the union. Therefore, SelectionChangeData should only implement
794 // constructors. In other words, add other members to
795 // SelectionChangeDataBase.
796 struct SelectionChangeData final : public SelectionChangeDataBase {
797 SelectionChangeData() {
798 mString = &mStringInstance;
799 Clear();
801 explicit SelectionChangeData(const SelectionChangeDataBase& aOther) {
802 mString = &mStringInstance;
803 Assign(aOther);
805 SelectionChangeData(const SelectionChangeData& aOther) {
806 mString = &mStringInstance;
807 Assign(aOther);
809 SelectionChangeData& operator=(const SelectionChangeDataBase& aOther) {
810 mString = &mStringInstance;
811 Assign(aOther);
812 return *this;
814 SelectionChangeData& operator=(const SelectionChangeData& aOther) {
815 mString = &mStringInstance;
816 Assign(aOther);
817 return *this;
820 private:
821 // When SelectionChangeData is used outside of union, it shouldn't create
822 // nsString instance in the heap as far as possible.
823 nsString mStringInstance;
826 struct TextChangeDataBase {
827 // mStartOffset is the start offset of modified or removed text in
828 // original content and inserted text in new content.
829 uint32_t mStartOffset;
830 // mRemovalEndOffset is the end offset of modified or removed text in
831 // original content. If the value is same as mStartOffset, no text hasn't
832 // been removed yet.
833 uint32_t mRemovedEndOffset;
834 // mAddedEndOffset is the end offset of inserted text or same as
835 // mStartOffset if just removed. The vlaue is offset in the new content.
836 uint32_t mAddedEndOffset;
838 // Note that TextChangeDataBase may be the result of merging two or more
839 // changes especially in e10s mode.
841 // mCausedOnlyByComposition is true only when *all* merged changes are
842 // caused by composition.
843 bool mCausedOnlyByComposition;
844 // mIncludingChangesDuringComposition is true if at least one change which
845 // is not caused by composition occurred during the last composition.
846 // Note that if after the last composition is finished and there are some
847 // changes not caused by composition, this is set to false.
848 bool mIncludingChangesDuringComposition;
849 // mIncludingChangesWithoutComposition is true if there is at least one
850 // change which did occur when there wasn't a composition ongoing.
851 bool mIncludingChangesWithoutComposition;
853 uint32_t OldLength() const {
854 MOZ_ASSERT(IsValid());
855 return mRemovedEndOffset - mStartOffset;
857 uint32_t NewLength() const {
858 MOZ_ASSERT(IsValid());
859 return mAddedEndOffset - mStartOffset;
862 // Positive if text is added. Negative if text is removed.
863 int64_t Difference() const { return mAddedEndOffset - mRemovedEndOffset; }
865 bool IsInInt32Range() const {
866 MOZ_ASSERT(IsValid());
867 return mStartOffset <= INT32_MAX && mRemovedEndOffset <= INT32_MAX &&
868 mAddedEndOffset <= INT32_MAX;
871 bool IsValid() const {
872 return !(mStartOffset == UINT32_MAX && !mRemovedEndOffset &&
873 !mAddedEndOffset);
876 void Clear() {
877 mStartOffset = UINT32_MAX;
878 mRemovedEndOffset = mAddedEndOffset = 0;
881 void MergeWith(const TextChangeDataBase& aOther);
882 TextChangeDataBase& operator+=(const TextChangeDataBase& aOther) {
883 MergeWith(aOther);
884 return *this;
887 #ifdef DEBUG
888 void Test();
889 #endif // #ifdef DEBUG
892 // TextChangeDataBase cannot have constructors because they are used in union.
893 // Therefore, TextChangeData should only implement constructor. In other
894 // words, add other members to TextChangeDataBase.
895 struct TextChangeData : public TextChangeDataBase {
896 TextChangeData() { Clear(); }
898 TextChangeData(uint32_t aStartOffset, uint32_t aRemovedEndOffset,
899 uint32_t aAddedEndOffset, bool aCausedByComposition,
900 bool aOccurredDuringComposition) {
901 MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
902 "removed end offset must not be smaller than start offset");
903 MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
904 "added end offset must not be smaller than start offset");
905 mStartOffset = aStartOffset;
906 mRemovedEndOffset = aRemovedEndOffset;
907 mAddedEndOffset = aAddedEndOffset;
908 mCausedOnlyByComposition = aCausedByComposition;
909 mIncludingChangesDuringComposition =
910 !aCausedByComposition && aOccurredDuringComposition;
911 mIncludingChangesWithoutComposition =
912 !aCausedByComposition && !aOccurredDuringComposition;
916 struct MouseButtonEventData {
917 // The value of WidgetEvent::mMessage
918 EventMessage mEventMessage;
919 // Character offset from the start of the focused editor under the cursor
920 uint32_t mOffset;
921 // Cursor position in pixels relative to the widget
922 LayoutDeviceIntPoint mCursorPos;
923 // Character rect in pixels under the cursor relative to the widget
924 LayoutDeviceIntRect mCharRect;
925 // The value of WidgetMouseEventBase::button and buttons
926 int16_t mButton;
927 int16_t mButtons;
928 // The value of WidgetInputEvent::modifiers
929 Modifiers mModifiers;
932 union {
933 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
934 SelectionChangeDataBase mSelectionChangeData;
936 // NOTIFY_IME_OF_TEXT_CHANGE specific data
937 TextChangeDataBase mTextChangeData;
939 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
940 MouseButtonEventData mMouseButtonEventData;
943 void SetData(const SelectionChangeDataBase& aSelectionChangeData) {
944 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
945 mSelectionChangeData.Assign(aSelectionChangeData);
948 void SetData(const TextChangeDataBase& aTextChangeData) {
949 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
950 mTextChangeData = aTextChangeData;
954 struct CandidateWindowPosition {
955 // Upper left corner of the candidate window if mExcludeRect is false.
956 // Otherwise, the position currently interested. E.g., caret position.
957 LayoutDeviceIntPoint mPoint;
958 // Rect which shouldn't be overlapped with the candidate window.
959 // This is valid only when mExcludeRect is true.
960 LayoutDeviceIntRect mRect;
961 // See explanation of mPoint and mRect.
962 bool mExcludeRect;
965 std::ostream& operator<<(std::ostream& aStream, const IMEEnabled& aEnabled);
966 std::ostream& operator<<(std::ostream& aStream, const IMEState::Open& aOpen);
967 std::ostream& operator<<(std::ostream& aStream, const IMEState& aState);
968 std::ostream& operator<<(std::ostream& aStream,
969 const InputContext::Origin& aOrigin);
970 std::ostream& operator<<(std::ostream& aStream, const InputContext& aContext);
971 std::ostream& operator<<(std::ostream& aStream,
972 const InputContextAction::Cause& aCause);
973 std::ostream& operator<<(std::ostream& aStream,
974 const InputContextAction::FocusChange& aFocusChange);
975 std::ostream& operator<<(std::ostream& aStream,
976 const IMENotification::SelectionChangeDataBase& aData);
977 std::ostream& operator<<(std::ostream& aStream,
978 const IMENotification::TextChangeDataBase& aData);
980 } // namespace widget
981 } // namespace mozilla
983 #endif // #ifndef mozilla_widget_IMEData_h_