Bug 1791155 [wpt PR 35927] - Move webkit-mask-box-enumeration.html into compat/,...
[gecko.git] / widget / IMEData.h
blob6e34f1d4c4cdfab957dc84fc68b2d62a82d8779c
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"
14 #include "nsPoint.h"
15 #include "nsRect.h"
16 #include "nsString.h"
17 #include "nsXULAppAPI.h"
18 #include "Units.h"
20 class nsIWidget;
22 namespace mozilla {
24 class ContentSelection;
25 class WritingMode;
27 template <class T>
28 class Maybe;
30 // Helper class to logging string which may contain various Unicode characters
31 // and/or may be too long string for logging.
32 class MOZ_STACK_CLASS PrintStringDetail : public nsAutoCString {
33 public:
34 static constexpr uint32_t kMaxLengthForCompositionString = 8;
35 static constexpr uint32_t kMaxLengthForSelectedString = 12;
36 static constexpr uint32_t kMaxLengthForEditor = 20;
38 PrintStringDetail() = delete;
39 explicit PrintStringDetail(const nsAString& aString,
40 uint32_t aMaxLength = UINT32_MAX);
41 template <typename StringType>
42 explicit PrintStringDetail(const Maybe<StringType>& aMaybeString,
43 uint32_t aMaxLength = UINT32_MAX);
45 private:
46 static nsCString PrintCharData(char32_t aChar);
49 // StartAndEndOffsets represents a range in flat-text.
50 template <typename IntType>
51 class StartAndEndOffsets {
52 protected:
53 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
55 public:
56 StartAndEndOffsets() = delete;
57 explicit StartAndEndOffsets(IntType aStartOffset, IntType aEndOffset)
58 : mStartOffset(aStartOffset),
59 mEndOffset(aStartOffset <= aEndOffset ? aEndOffset : aStartOffset) {
60 MOZ_ASSERT(aStartOffset <= mEndOffset);
63 IntType StartOffset() const { return mStartOffset; }
64 IntType Length() const { return mEndOffset - mStartOffset; }
65 IntType EndOffset() const { return mEndOffset; }
67 bool IsOffsetInRange(IntType aOffset) const {
68 return aOffset >= mStartOffset && aOffset < mEndOffset;
70 bool IsOffsetInRangeOrEndOffset(IntType aOffset) const {
71 return aOffset >= mStartOffset && aOffset <= mEndOffset;
74 void MoveTo(IntType aNewStartOffset) {
75 auto delta = static_cast<int64_t>(mStartOffset) - aNewStartOffset;
76 mStartOffset += delta;
77 mEndOffset += delta;
79 void SetOffsetAndLength(IntType aNewOffset, IntType aNewLength) {
80 mStartOffset = aNewOffset;
81 CheckedInt<IntType> endOffset(aNewOffset + aNewLength);
82 mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
84 void SetEndOffset(IntType aEndOffset) {
85 MOZ_ASSERT(mStartOffset <= aEndOffset);
86 mEndOffset = std::max(aEndOffset, mStartOffset);
88 void SetStartAndEndOffsets(IntType aStartOffset, IntType aEndOffset) {
89 MOZ_ASSERT(aStartOffset <= aEndOffset);
90 mStartOffset = aStartOffset;
91 mEndOffset = aStartOffset <= aEndOffset ? aEndOffset : aStartOffset;
93 void SetLength(IntType aNewLength) {
94 CheckedInt<IntType> endOffset(mStartOffset + aNewLength);
95 mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
98 friend std::ostream& operator<<(
99 std::ostream& aStream,
100 const StartAndEndOffsets<IntType>& aStartAndEndOffsets) {
101 aStream << "{ mStartOffset=" << aStartAndEndOffsets.mStartOffset
102 << ", mEndOffset=" << aStartAndEndOffsets.mEndOffset
103 << ", Length()=" << aStartAndEndOffsets.Length() << " }";
104 return aStream;
107 private:
108 IntType mStartOffset;
109 IntType mEndOffset;
112 // OffsetAndData class is designed for storing composition string and its
113 // start offset. Length() and EndOffset() return only valid length or
114 // offset. I.e., if the string is too long for inserting at the offset,
115 // the length is shrunken. However, the string itself is not shrunken.
116 // Therefore, moving it to where all of the string can be contained,
117 // they will return longer/bigger value.
118 enum class OffsetAndDataFor {
119 CompositionString,
120 SelectedString,
121 EditorString,
123 template <typename IntType>
124 class OffsetAndData {
125 protected:
126 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
128 public:
129 OffsetAndData() = delete;
130 explicit OffsetAndData(
131 IntType aStartOffset, const nsAString& aData,
132 OffsetAndDataFor aFor = OffsetAndDataFor::CompositionString)
133 : mData(aData), mOffset(aStartOffset), mFor(aFor) {}
135 bool IsValid() const {
136 CheckedInt<IntType> offset(mOffset);
137 offset += mData.Length();
138 return offset.isValid();
140 IntType StartOffset() const { return mOffset; }
141 IntType Length() const {
142 CheckedInt<IntType> endOffset(CheckedInt<IntType>(mOffset) +
143 mData.Length());
144 return endOffset.isValid() ? mData.Length() : MaxOffset() - mOffset;
146 IntType EndOffset() const { return mOffset + Length(); }
147 StartAndEndOffsets<IntType> CreateStartAndEndOffsets() const {
148 return StartAndEndOffsets<IntType>(StartOffset(), EndOffset());
150 const nsString& DataRef() const {
151 // In strictly speaking, we should return substring which may be shrunken
152 // for rounding to the max offset. However, it's unrealistic edge case,
153 // and creating new string is not so cheap job in a hot path. Therefore,
154 // this just returns the data as-is.
155 return mData;
157 bool IsDataEmpty() const { return mData.IsEmpty(); }
159 bool IsOffsetInRange(IntType aOffset) const {
160 return aOffset >= mOffset && aOffset < EndOffset();
162 bool IsOffsetInRangeOrEndOffset(IntType aOffset) const {
163 return aOffset >= mOffset && aOffset <= EndOffset();
166 void Collapse(IntType aOffset) {
167 mOffset = aOffset;
168 mData.Truncate();
170 void MoveTo(IntType aNewOffset) { mOffset = aNewOffset; }
171 void SetOffsetAndData(IntType aStartOffset, const nsAString& aData) {
172 mOffset = aStartOffset;
173 mData = aData;
175 void SetData(const nsAString& aData) { mData = aData; }
176 void TruncateData(uint32_t aLength = 0) { mData.Truncate(aLength); }
177 void ReplaceData(nsAString::size_type aCutStart,
178 nsAString::size_type aCutLength,
179 const nsAString& aNewString) {
180 mData.Replace(aCutStart, aCutLength, aNewString);
183 friend std::ostream& operator<<(
184 std::ostream& aStream, const OffsetAndData<IntType>& aOffsetAndData) {
185 const auto maxDataLength =
186 aOffsetAndData.mFor == OffsetAndDataFor::CompositionString
187 ? PrintStringDetail::kMaxLengthForCompositionString
188 : (aOffsetAndData.mFor == OffsetAndDataFor::SelectedString
189 ? PrintStringDetail::kMaxLengthForSelectedString
190 : PrintStringDetail::kMaxLengthForEditor);
191 aStream << "{ mOffset=" << aOffsetAndData.mOffset << ", mData="
192 << PrintStringDetail(aOffsetAndData.mData, maxDataLength).get()
193 << ", Length()=" << aOffsetAndData.Length()
194 << ", EndOffset()=" << aOffsetAndData.EndOffset() << " }";
195 return aStream;
198 private:
199 nsString mData;
200 IntType mOffset;
201 OffsetAndDataFor mFor;
204 namespace widget {
207 * Preference for receiving IME updates
209 * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
210 * change and/or selection change and call nsIWidget::NotifyIME() with
211 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
212 * Please note that the text change observing cost is very expensive especially
213 * on an HTML editor has focus.
214 * If the IME implementation on a particular platform doesn't care about
215 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
216 * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
217 * If the IME implementation needs notifications even while our process is
218 * deactive, it should also set NOTIFY_DURING_DEACTIVE.
220 struct IMENotificationRequests final {
221 typedef uint8_t Notifications;
223 enum : Notifications {
224 NOTIFY_NOTHING = 0,
225 NOTIFY_TEXT_CHANGE = 1 << 1,
226 NOTIFY_POSITION_CHANGE = 1 << 2,
227 // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
228 // or released on a character in the focused editor. The notification is
229 // notified to IME as a mouse event. If it's consumed by IME, NotifyIME()
230 // returns NS_SUCCESS_EVENT_CONSUMED. Otherwise, it returns NS_OK if it's
231 // handled without any error.
232 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR = 1 << 3,
233 // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
234 // or more compositions are possible. E.g., Mac and Linux (GTK).
235 NOTIFY_DURING_DEACTIVE = 1 << 7,
237 NOTIFY_ALL = NOTIFY_TEXT_CHANGE | NOTIFY_POSITION_CHANGE |
238 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR,
241 IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING) {}
243 explicit IMENotificationRequests(Notifications aWantUpdates)
244 : mWantUpdates(aWantUpdates) {}
246 IMENotificationRequests operator|(
247 const IMENotificationRequests& aOther) const {
248 return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates);
250 IMENotificationRequests& operator|=(const IMENotificationRequests& aOther) {
251 mWantUpdates |= aOther.mWantUpdates;
252 return *this;
254 bool operator==(const IMENotificationRequests& aOther) const {
255 return mWantUpdates == aOther.mWantUpdates;
258 bool WantTextChange() const { return !!(mWantUpdates & NOTIFY_TEXT_CHANGE); }
260 bool WantPositionChanged() const {
261 return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
264 bool WantChanges() const { return WantTextChange(); }
266 bool WantMouseButtonEventOnChar() const {
267 return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
270 bool WantDuringDeactive() const {
271 return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
274 Notifications mWantUpdates;
278 * IME enabled states.
280 * WARNING: If you change these values, you also need to edit:
281 * nsIDOMWindowUtils.idl
283 enum class IMEEnabled {
285 * 'Disabled' means the user cannot use IME. So, the IME open state should
286 * be 'closed' during 'disabled'.
288 Disabled,
290 * 'Enabled' means the user can use IME.
292 Enabled,
294 * 'Password' state is a special case for the password editors.
295 * E.g., on mac, the password editors should disable the non-Roman
296 * keyboard layouts at getting focus. Thus, the password editor may have
297 * special rules on some platforms.
299 Password,
301 * 'Unknown' is useful when you cache this enum. So, this shouldn't be
302 * used with nsIWidget::SetInputContext().
304 Unknown,
308 * Contains IMEStatus plus information about the current
309 * input context that the IME can use as hints if desired.
312 struct IMEState final {
313 IMEEnabled mEnabled;
316 * IME open states the mOpen value of SetInputContext() should be one value of
317 * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE. GetInputContext() should return
318 * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
320 enum Open {
322 * 'Unsupported' means the platform cannot return actual IME open state.
323 * This value is used only by GetInputContext().
325 OPEN_STATE_NOT_SUPPORTED,
327 * 'Don't change' means the widget shouldn't change IME open state when
328 * SetInputContext() is called.
330 DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED,
332 * 'Open' means that IME should compose in its primary language (or latest
333 * input mode except direct ASCII character input mode). Even if IME is
334 * opened by this value, users should be able to close IME by theirselves.
335 * Web contents can specify this value by |ime-mode: active;|.
337 OPEN,
339 * 'Closed' means that IME shouldn't handle key events (or should handle
340 * as ASCII character inputs on mobile device). Even if IME is closed by
341 * this value, users should be able to open IME by theirselves.
342 * Web contents can specify this value by |ime-mode: inactive;|.
344 CLOSED
346 Open mOpen;
348 IMEState() : mEnabled(IMEEnabled::Enabled), mOpen(DONT_CHANGE_OPEN_STATE) {}
350 explicit IMEState(IMEEnabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE)
351 : mEnabled(aEnabled), mOpen(aOpen) {}
353 // Returns true if the user can input characters.
354 // This means that a plain text editor, an HTML editor, a password editor or
355 // a plain text editor whose ime-mode is "disabled".
356 bool IsEditable() const {
357 return mEnabled == IMEEnabled::Enabled || mEnabled == IMEEnabled::Password;
361 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
362 // If there can be only one IME composition in a process, this can be used.
363 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
364 (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
366 struct NativeIMEContext final {
367 // Pointer to native IME context. Typically this is the result of
368 // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
369 // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
370 uintptr_t mRawNativeIMEContext;
371 // Process ID of the origin of mNativeIMEContext.
372 uint64_t mOriginProcessID;
374 NativeIMEContext() : mRawNativeIMEContext(0), mOriginProcessID(0) {
375 Init(nullptr);
378 explicit NativeIMEContext(nsIWidget* aWidget)
379 : mRawNativeIMEContext(0), mOriginProcessID(0) {
380 Init(aWidget);
383 bool IsValid() const {
384 return mRawNativeIMEContext &&
385 mOriginProcessID != static_cast<uintptr_t>(-1);
388 void Init(nsIWidget* aWidget);
389 void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext) {
390 InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext));
392 void InitWithRawNativeIMEContext(void* aRawNativeIMEContext);
394 bool operator==(const NativeIMEContext& aOther) const {
395 return mRawNativeIMEContext == aOther.mRawNativeIMEContext &&
396 mOriginProcessID == aOther.mOriginProcessID;
398 bool operator!=(const NativeIMEContext& aOther) const {
399 return !(*this == aOther);
403 struct InputContext final {
404 InputContext()
405 : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT),
406 mMayBeIMEUnaware(false),
407 mHasHandledUserInput(false),
408 mInPrivateBrowsing(false) {}
410 // If InputContext instance is a static variable, any heap allocated stuff
411 // of its members need to be deleted at XPCOM shutdown. Otherwise, it's
412 // detected as memory leak.
413 void ShutDown() {
414 mHTMLInputType.Truncate();
415 mHTMLInputInputmode.Truncate();
416 mActionHint.Truncate();
417 mAutocapitalize.Truncate();
420 bool IsPasswordEditor() const {
421 return mHTMLInputType.LowerCaseEqualsLiteral("password");
424 NativeKeyBindingsType GetNativeKeyBindingsType() const {
425 MOZ_DIAGNOSTIC_ASSERT(mIMEState.IsEditable());
426 // See GetInputType in IMEStateManager.cpp
427 if (mHTMLInputType.IsEmpty()) {
428 return NativeKeyBindingsType::RichTextEditor;
430 return mHTMLInputType.EqualsLiteral("textarea")
431 ? NativeKeyBindingsType::MultiLineEditor
432 : NativeKeyBindingsType::SingleLineEditor;
435 // https://html.spec.whatwg.org/dev/interaction.html#autocapitalization
436 bool IsAutocapitalizeSupported() const {
437 return !mHTMLInputType.EqualsLiteral("password") &&
438 !mHTMLInputType.EqualsLiteral("url") &&
439 !mHTMLInputType.EqualsLiteral("email");
442 bool IsInputAttributeChanged(const InputContext& aOldContext) const {
443 return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled ||
444 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
445 // input type and inputmode are supported by Windows IME API, GTK
446 // IME API and Android IME API
447 mHTMLInputType != aOldContext.mHTMLInputType ||
448 mHTMLInputInputmode != aOldContext.mHTMLInputInputmode ||
449 #endif
450 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK)
451 // autocapitalize is supported by Android IME API and GTK IME API
452 mAutocapitalize != aOldContext.mAutocapitalize ||
453 #endif
454 #if defined(ANDROID)
455 // enterkeyhint is only supported by Android IME API.
456 mActionHint != aOldContext.mActionHint ||
457 #endif
458 false;
461 IMEState mIMEState;
463 /* The type of the input if the input is a html input field */
464 nsString mHTMLInputType;
466 /* The type of the inputmode */
467 nsString mHTMLInputInputmode;
469 /* A hint for the action that is performed when the input is submitted */
470 nsString mActionHint;
472 /* A hint for autocapitalize */
473 nsString mAutocapitalize;
476 * mOrigin indicates whether this focus event refers to main or remote
477 * content.
479 enum Origin {
480 // Adjusting focus of content on the main process
481 ORIGIN_MAIN,
482 // Adjusting focus of content in a remote process
483 ORIGIN_CONTENT
485 Origin mOrigin;
487 /* True if the webapp may be unaware of IME events such as input event or
488 * composiion events. This enables a key-events-only mode on Android for
489 * compatibility with webapps relying on key listeners. */
490 bool mMayBeIMEUnaware;
493 * True if the document has ever received user input
495 bool mHasHandledUserInput;
497 /* Whether the owning document of the input element has been loaded
498 * in private browsing mode. */
499 bool mInPrivateBrowsing;
501 bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; }
503 bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; }
505 bool IsOriginCurrentProcess() const {
506 if (XRE_IsParentProcess()) {
507 return IsOriginMainProcess();
509 return IsOriginContentProcess();
513 // FYI: Implemented in nsBaseWidget.cpp
514 const char* ToChar(InputContext::Origin aOrigin);
516 struct InputContextAction final {
518 * mCause indicates what action causes calling nsIWidget::SetInputContext().
519 * It must be one of following values.
521 enum Cause {
522 // The cause is unknown but originated from content. Focus might have been
523 // changed by content script.
524 CAUSE_UNKNOWN,
525 // The cause is unknown but originated from chrome. Focus might have been
526 // changed by chrome script.
527 CAUSE_UNKNOWN_CHROME,
528 // The cause is user's keyboard operation.
529 CAUSE_KEY,
530 // The cause is user's mouse operation.
531 CAUSE_MOUSE,
532 // The cause is user's touch operation (implies mouse)
533 CAUSE_TOUCH,
534 // The cause is users' long press operation.
535 CAUSE_LONGPRESS,
536 // The cause is unknown but it occurs during user input except keyboard
537 // input. E.g., an event handler of a user input event moves focus.
538 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
539 // The cause is unknown but it occurs during keyboard input.
540 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
542 Cause mCause;
545 * mFocusChange indicates what happened for focus.
547 enum FocusChange {
548 FOCUS_NOT_CHANGED,
549 // A content got focus.
550 GOT_FOCUS,
551 // Focused content lost focus.
552 LOST_FOCUS,
553 // Menu got pseudo focus that means focused content isn't changed but
554 // keyboard events will be handled by menu.
555 MENU_GOT_PSEUDO_FOCUS,
556 // Menu lost pseudo focus that means focused content will handle keyboard
557 // events.
558 MENU_LOST_PSEUDO_FOCUS,
559 // The widget is created. When a widget is crated, it may need to notify
560 // IME module to initialize its native IME context. In such case, this is
561 // used. I.e., this isn't used by IMEStateManager.
562 WIDGET_CREATED
564 FocusChange mFocusChange;
566 bool ContentGotFocusByTrustedCause() const {
567 return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN);
570 bool UserMightRequestOpenVKB() const {
571 // If focus is changed, user must not request to open VKB.
572 if (mFocusChange != FOCUS_NOT_CHANGED) {
573 return false;
575 switch (mCause) {
576 // If user clicks or touches focused editor, user must request to open
577 // VKB.
578 case CAUSE_MOUSE:
579 case CAUSE_TOUCH:
580 // If script does something during a user input and that causes changing
581 // input context, user might request to open VKB. E.g., user clicks
582 // dummy editor and JS moves focus to an actual editable node. However,
583 // this should return false if the user input is a keyboard event since
584 // physical keyboard operation shouldn't cause opening VKB.
585 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
586 return true;
587 default:
588 return false;
593 * IsHandlingUserInput() returns true if it's caused by a user action directly
594 * or it's caused by script or something but it occurred while we're handling
595 * a user action. E.g., when it's caused by Element.focus() in an event
596 * handler of a user input, this returns true.
598 static bool IsHandlingUserInput(Cause aCause) {
599 switch (aCause) {
600 case CAUSE_KEY:
601 case CAUSE_MOUSE:
602 case CAUSE_TOUCH:
603 case CAUSE_LONGPRESS:
604 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
605 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
606 return true;
607 default:
608 return false;
612 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); }
614 InputContextAction()
615 : mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED) {}
617 explicit InputContextAction(Cause aCause,
618 FocusChange aFocusChange = FOCUS_NOT_CHANGED)
619 : mCause(aCause), mFocusChange(aFocusChange) {}
622 // IMEMessage is shared by IMEStateManager and TextComposition.
623 // Update values in GeckoEditable.java if you make changes here.
624 // XXX Negative values are used in Android...
625 typedef int8_t IMEMessageType;
626 enum IMEMessage : IMEMessageType {
627 // This is used by IMENotification internally. This means that the instance
628 // hasn't been initialized yet.
629 NOTIFY_IME_OF_NOTHING,
630 // An editable content is getting focus
631 NOTIFY_IME_OF_FOCUS,
632 // An editable content is losing focus
633 NOTIFY_IME_OF_BLUR,
634 // Selection in the focused editable content is changed
635 NOTIFY_IME_OF_SELECTION_CHANGE,
636 // Text in the focused editable content is changed
637 NOTIFY_IME_OF_TEXT_CHANGE,
638 // Notified when a dispatched composition event is handled by the
639 // contents. This must be notified after the other notifications.
640 // Note that if a remote process has focus, this is notified only once when
641 // all dispatched events are handled completely. So, the receiver shouldn't
642 // count number of received this notification for comparing with the number
643 // of dispatched events.
644 // NOTE: If a composition event causes moving focus from the focused editor,
645 // this notification may not be notified as usual. Even in such case,
646 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
647 // should tread the blur notification as including this if there is
648 // pending composition events.
649 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
650 // Position or size of focused element may be changed.
651 NOTIFY_IME_OF_POSITION_CHANGE,
652 // Mouse button event is fired on a character in focused editor
653 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
654 // Request to commit current composition to IME
655 // (some platforms may not support)
656 REQUEST_TO_COMMIT_COMPOSITION,
657 // Request to cancel current composition to IME
658 // (some platforms may not support)
659 REQUEST_TO_CANCEL_COMPOSITION
662 // FYI: Implemented in nsBaseWidget.cpp
663 const char* ToChar(IMEMessage aIMEMessage);
665 struct IMENotification final {
666 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING), mSelectionChangeData() {}
668 IMENotification(const IMENotification& aOther)
669 : mMessage(NOTIFY_IME_OF_NOTHING) {
670 Assign(aOther);
673 ~IMENotification() { Clear(); }
675 MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
676 : mMessage(aMessage), mSelectionChangeData() {
677 switch (aMessage) {
678 case NOTIFY_IME_OF_SELECTION_CHANGE:
679 mSelectionChangeData.mString = new nsString();
680 mSelectionChangeData.Clear();
681 break;
682 case NOTIFY_IME_OF_TEXT_CHANGE:
683 mTextChangeData.Clear();
684 break;
685 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
686 mMouseButtonEventData.mEventMessage = eVoidEvent;
687 mMouseButtonEventData.mOffset = UINT32_MAX;
688 mMouseButtonEventData.mCursorPos.MoveTo(0, 0);
689 mMouseButtonEventData.mCharRect.SetRect(0, 0, 0, 0);
690 mMouseButtonEventData.mButton = -1;
691 mMouseButtonEventData.mButtons = 0;
692 mMouseButtonEventData.mModifiers = 0;
693 break;
694 default:
695 break;
699 void Assign(const IMENotification& aOther) {
700 bool changingMessage = mMessage != aOther.mMessage;
701 if (changingMessage) {
702 Clear();
703 mMessage = aOther.mMessage;
705 switch (mMessage) {
706 case NOTIFY_IME_OF_SELECTION_CHANGE:
707 if (changingMessage) {
708 mSelectionChangeData.mString = new nsString();
710 mSelectionChangeData.Assign(aOther.mSelectionChangeData);
711 break;
712 case NOTIFY_IME_OF_TEXT_CHANGE:
713 mTextChangeData = aOther.mTextChangeData;
714 break;
715 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
716 mMouseButtonEventData = aOther.mMouseButtonEventData;
717 break;
718 default:
719 break;
723 IMENotification& operator=(const IMENotification& aOther) {
724 Assign(aOther);
725 return *this;
728 void Clear() {
729 if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
730 MOZ_ASSERT(mSelectionChangeData.mString);
731 delete mSelectionChangeData.mString;
732 mSelectionChangeData.mString = nullptr;
734 mMessage = NOTIFY_IME_OF_NOTHING;
737 bool HasNotification() const { return mMessage != NOTIFY_IME_OF_NOTHING; }
739 void MergeWith(const IMENotification& aNotification) {
740 switch (mMessage) {
741 case NOTIFY_IME_OF_NOTHING:
742 MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
743 Assign(aNotification);
744 break;
745 case NOTIFY_IME_OF_SELECTION_CHANGE:
746 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
747 mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
748 break;
749 case NOTIFY_IME_OF_TEXT_CHANGE:
750 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
751 mTextChangeData += aNotification.mTextChangeData;
752 break;
753 case NOTIFY_IME_OF_POSITION_CHANGE:
754 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
755 MOZ_ASSERT(aNotification.mMessage == mMessage);
756 break;
757 default:
758 MOZ_CRASH("Merging notification isn't supported");
759 break;
763 IMEMessage mMessage;
765 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
766 struct SelectionChangeDataBase {
767 // Selection range.
768 uint32_t mOffset;
770 // Selected string
771 nsString* mString;
773 // Writing mode at the selection.
774 uint8_t mWritingModeBits;
776 bool mIsInitialized;
777 bool mHasRange;
778 bool mReversed;
779 bool mCausedByComposition;
780 bool mCausedBySelectionEvent;
781 bool mOccurredDuringComposition;
783 // FYI: Cannot we make these methods inline because of an include hell of
784 // RawServoAnimationValueMap
785 void SetWritingMode(const WritingMode& aWritingMode);
786 WritingMode GetWritingMode() const;
788 uint32_t StartOffset() const {
789 MOZ_ASSERT(mHasRange);
790 return mOffset;
792 uint32_t EndOffset() const {
793 MOZ_ASSERT(mHasRange);
794 return mOffset + Length();
796 uint32_t AnchorOffset() const {
797 MOZ_ASSERT(mHasRange);
798 return mOffset + (mReversed ? Length() : 0);
800 uint32_t FocusOffset() const {
801 MOZ_ASSERT(mHasRange);
802 return mOffset + (mReversed ? 0 : Length());
804 const nsString& String() const {
805 MOZ_ASSERT(mHasRange);
806 return *mString;
808 uint32_t Length() const {
809 MOZ_ASSERT(mHasRange);
810 return mString->Length();
812 bool IsInInt32Range() const {
813 return mHasRange && mOffset <= INT32_MAX && Length() <= INT32_MAX &&
814 mOffset + Length() <= INT32_MAX;
816 bool HasRange() const { return mIsInitialized && mHasRange; }
817 bool IsCollapsed() const { return !mHasRange || mString->IsEmpty(); }
818 void ClearSelectionData() {
819 mIsInitialized = false;
820 mHasRange = false;
821 mOffset = UINT32_MAX;
822 mString->Truncate();
823 mWritingModeBits = 0;
824 mReversed = false;
826 void Clear() {
827 ClearSelectionData();
828 mCausedByComposition = false;
829 mCausedBySelectionEvent = false;
830 mOccurredDuringComposition = false;
832 bool IsInitialized() const { return mIsInitialized; }
833 void Assign(const SelectionChangeDataBase& aOther) {
834 mIsInitialized = aOther.mIsInitialized;
835 mHasRange = aOther.mHasRange;
836 if (mIsInitialized && mHasRange) {
837 mOffset = aOther.mOffset;
838 *mString = aOther.String();
839 mReversed = aOther.mReversed;
840 mWritingModeBits = aOther.mWritingModeBits;
841 } else {
842 mOffset = UINT32_MAX;
843 mString->Truncate();
844 mReversed = false;
845 // Let's keep the writing mode for avoiding temporarily changing the
846 // writing mode at no selection range.
848 AssignReason(aOther.mCausedByComposition, aOther.mCausedBySelectionEvent,
849 aOther.mOccurredDuringComposition);
851 void Assign(const WidgetQueryContentEvent& aQuerySelectedTextEvent);
852 void AssignReason(bool aCausedByComposition, bool aCausedBySelectionEvent,
853 bool aOccurredDuringComposition) {
854 mCausedByComposition = aCausedByComposition;
855 mCausedBySelectionEvent = aCausedBySelectionEvent;
856 mOccurredDuringComposition = aOccurredDuringComposition;
859 bool EqualsRange(const SelectionChangeDataBase& aOther) const {
860 if (HasRange() != aOther.HasRange()) {
861 return false;
863 if (!HasRange()) {
864 return true;
866 return mOffset == aOther.mOffset && mString->Equals(*aOther.mString);
868 bool EqualsRangeAndDirection(const SelectionChangeDataBase& aOther) const {
869 return EqualsRange(aOther) &&
870 (!HasRange() || mReversed == aOther.mReversed);
872 bool EqualsRangeAndDirectionAndWritingMode(
873 const SelectionChangeDataBase& aOther) const {
874 return EqualsRangeAndDirection(aOther) &&
875 mWritingModeBits == aOther.mWritingModeBits;
878 bool EqualsRange(const ContentSelection& aContentSelection) const;
879 bool EqualsRangeAndWritingMode(
880 const ContentSelection& aContentSelection) const;
882 OffsetAndData<uint32_t> ToUint32OffsetAndData() const {
883 return OffsetAndData<uint32_t>(mOffset, *mString,
884 OffsetAndDataFor::SelectedString);
888 // SelectionChangeDataBase cannot have constructors because it's used in
889 // the union. Therefore, SelectionChangeData should only implement
890 // constructors. In other words, add other members to
891 // SelectionChangeDataBase.
892 struct SelectionChangeData final : public SelectionChangeDataBase {
893 SelectionChangeData() {
894 mString = &mStringInstance;
895 Clear();
897 explicit SelectionChangeData(const SelectionChangeDataBase& aOther) {
898 mString = &mStringInstance;
899 Assign(aOther);
901 SelectionChangeData(const SelectionChangeData& aOther) {
902 mString = &mStringInstance;
903 Assign(aOther);
905 SelectionChangeData& operator=(const SelectionChangeDataBase& aOther) {
906 mString = &mStringInstance;
907 Assign(aOther);
908 return *this;
910 SelectionChangeData& operator=(const SelectionChangeData& aOther) {
911 mString = &mStringInstance;
912 Assign(aOther);
913 return *this;
916 private:
917 // When SelectionChangeData is used outside of union, it shouldn't create
918 // nsString instance in the heap as far as possible.
919 nsString mStringInstance;
922 struct TextChangeDataBase {
923 // mStartOffset is the start offset of modified or removed text in
924 // original content and inserted text in new content.
925 uint32_t mStartOffset;
926 // mRemovalEndOffset is the end offset of modified or removed text in
927 // original content. If the value is same as mStartOffset, no text hasn't
928 // been removed yet.
929 uint32_t mRemovedEndOffset;
930 // mAddedEndOffset is the end offset of inserted text or same as
931 // mStartOffset if just removed. The vlaue is offset in the new content.
932 uint32_t mAddedEndOffset;
934 // Note that TextChangeDataBase may be the result of merging two or more
935 // changes especially in e10s mode.
937 // mCausedOnlyByComposition is true only when *all* merged changes are
938 // caused by composition.
939 bool mCausedOnlyByComposition;
940 // mIncludingChangesDuringComposition is true if at least one change which
941 // is not caused by composition occurred during the last composition.
942 // Note that if after the last composition is finished and there are some
943 // changes not caused by composition, this is set to false.
944 bool mIncludingChangesDuringComposition;
945 // mIncludingChangesWithoutComposition is true if there is at least one
946 // change which did occur when there wasn't a composition ongoing.
947 bool mIncludingChangesWithoutComposition;
949 uint32_t OldLength() const {
950 MOZ_ASSERT(IsValid());
951 return mRemovedEndOffset - mStartOffset;
953 uint32_t NewLength() const {
954 MOZ_ASSERT(IsValid());
955 return mAddedEndOffset - mStartOffset;
958 // Positive if text is added. Negative if text is removed.
959 int64_t Difference() const { return mAddedEndOffset - mRemovedEndOffset; }
961 bool IsInInt32Range() const {
962 MOZ_ASSERT(IsValid());
963 return mStartOffset <= INT32_MAX && mRemovedEndOffset <= INT32_MAX &&
964 mAddedEndOffset <= INT32_MAX;
967 bool IsValid() const {
968 return !(mStartOffset == UINT32_MAX && !mRemovedEndOffset &&
969 !mAddedEndOffset);
972 void Clear() {
973 mStartOffset = UINT32_MAX;
974 mRemovedEndOffset = mAddedEndOffset = 0;
977 void MergeWith(const TextChangeDataBase& aOther);
978 TextChangeDataBase& operator+=(const TextChangeDataBase& aOther) {
979 MergeWith(aOther);
980 return *this;
983 #ifdef DEBUG
984 void Test();
985 #endif // #ifdef DEBUG
988 // TextChangeDataBase cannot have constructors because they are used in union.
989 // Therefore, TextChangeData should only implement constructor. In other
990 // words, add other members to TextChangeDataBase.
991 struct TextChangeData : public TextChangeDataBase {
992 TextChangeData() { Clear(); }
994 TextChangeData(uint32_t aStartOffset, uint32_t aRemovedEndOffset,
995 uint32_t aAddedEndOffset, bool aCausedByComposition,
996 bool aOccurredDuringComposition) {
997 MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
998 "removed end offset must not be smaller than start offset");
999 MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
1000 "added end offset must not be smaller than start offset");
1001 mStartOffset = aStartOffset;
1002 mRemovedEndOffset = aRemovedEndOffset;
1003 mAddedEndOffset = aAddedEndOffset;
1004 mCausedOnlyByComposition = aCausedByComposition;
1005 mIncludingChangesDuringComposition =
1006 !aCausedByComposition && aOccurredDuringComposition;
1007 mIncludingChangesWithoutComposition =
1008 !aCausedByComposition && !aOccurredDuringComposition;
1012 struct MouseButtonEventData {
1013 // The value of WidgetEvent::mMessage
1014 EventMessage mEventMessage;
1015 // Character offset from the start of the focused editor under the cursor
1016 uint32_t mOffset;
1017 // Cursor position in pixels relative to the widget
1018 LayoutDeviceIntPoint mCursorPos;
1019 // Character rect in pixels under the cursor relative to the widget
1020 LayoutDeviceIntRect mCharRect;
1021 // The value of WidgetMouseEventBase::button and buttons
1022 int16_t mButton;
1023 int16_t mButtons;
1024 // The value of WidgetInputEvent::modifiers
1025 Modifiers mModifiers;
1028 union {
1029 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
1030 SelectionChangeDataBase mSelectionChangeData;
1032 // NOTIFY_IME_OF_TEXT_CHANGE specific data
1033 TextChangeDataBase mTextChangeData;
1035 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
1036 MouseButtonEventData mMouseButtonEventData;
1039 void SetData(const SelectionChangeDataBase& aSelectionChangeData) {
1040 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
1041 mSelectionChangeData.Assign(aSelectionChangeData);
1044 void SetData(const TextChangeDataBase& aTextChangeData) {
1045 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
1046 mTextChangeData = aTextChangeData;
1050 struct CandidateWindowPosition {
1051 // Upper left corner of the candidate window if mExcludeRect is false.
1052 // Otherwise, the position currently interested. E.g., caret position.
1053 LayoutDeviceIntPoint mPoint;
1054 // Rect which shouldn't be overlapped with the candidate window.
1055 // This is valid only when mExcludeRect is true.
1056 LayoutDeviceIntRect mRect;
1057 // See explanation of mPoint and mRect.
1058 bool mExcludeRect;
1061 std::ostream& operator<<(std::ostream& aStream, const IMEEnabled& aEnabled);
1062 std::ostream& operator<<(std::ostream& aStream, const IMEState::Open& aOpen);
1063 std::ostream& operator<<(std::ostream& aStream, const IMEState& aState);
1064 std::ostream& operator<<(std::ostream& aStream,
1065 const InputContext::Origin& aOrigin);
1066 std::ostream& operator<<(std::ostream& aStream, const InputContext& aContext);
1067 std::ostream& operator<<(std::ostream& aStream,
1068 const InputContextAction::Cause& aCause);
1069 std::ostream& operator<<(std::ostream& aStream,
1070 const InputContextAction::FocusChange& aFocusChange);
1071 std::ostream& operator<<(std::ostream& aStream,
1072 const IMENotification::SelectionChangeDataBase& aData);
1073 std::ostream& operator<<(std::ostream& aStream,
1074 const IMENotification::TextChangeDataBase& aData);
1076 } // namespace widget
1077 } // namespace mozilla
1079 #endif // #ifndef mozilla_widget_IMEData_h_