Bug 1798574 [wpt PR 36769] - Allow CSS toggles to apply to elements with default...
[gecko.git] / widget / IMEData.h
blob188f973acaa380cb4777e199337d2be0b33dd47f
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 "nsCOMPtr.h"
15 #include "nsIURI.h"
16 #include "nsPoint.h"
17 #include "nsRect.h"
18 #include "nsString.h"
19 #include "nsXULAppAPI.h"
20 #include "Units.h"
22 class nsIWidget;
24 namespace mozilla {
26 class ContentSelection;
27 class WritingMode;
29 template <class T>
30 class Maybe;
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 {
35 public:
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);
47 private:
48 static nsCString PrintCharData(char32_t aChar);
51 // StartAndEndOffsets represents a range in flat-text.
52 template <typename IntType>
53 class StartAndEndOffsets {
54 protected:
55 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
57 public:
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;
79 mEndOffset += 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() << " }";
106 return aStream;
109 private:
110 IntType mStartOffset;
111 IntType mEndOffset;
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 {
121 CompositionString,
122 SelectedString,
123 EditorString,
125 template <typename IntType>
126 class OffsetAndData {
127 protected:
128 static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
130 public:
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) +
145 mData.Length());
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.
157 return mData;
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) {
169 mOffset = aOffset;
170 mData.Truncate();
172 void MoveTo(IntType aNewOffset) { mOffset = aNewOffset; }
173 void SetOffsetAndData(IntType aStartOffset, const nsAString& aData) {
174 mOffset = aStartOffset;
175 mData = aData;
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() << " }";
197 return aStream;
200 private:
201 nsString mData;
202 IntType mOffset;
203 OffsetAndDataFor mFor;
206 namespace widget {
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 {
226 NOTIFY_NOTHING = 0,
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;
254 return *this;
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'.
290 Disabled,
292 * 'Enabled' means the user can use IME.
294 Enabled,
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.
301 Password,
303 * 'Unknown' is useful when you cache this enum. So, this shouldn't be
304 * used with nsIWidget::SetInputContext().
306 Unknown,
310 * Contains IMEStatus plus information about the current
311 * input context that the IME can use as hints if desired.
314 struct IMEState final {
315 IMEEnabled mEnabled;
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.
322 enum Open {
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;|.
339 OPEN,
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;|.
346 CLOSED
348 Open mOpen;
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 uint64_t mOriginProcessID;
376 NativeIMEContext() : mRawNativeIMEContext(0), mOriginProcessID(0) {
377 Init(nullptr);
380 explicit NativeIMEContext(nsIWidget* aWidget)
381 : mRawNativeIMEContext(0), mOriginProcessID(0) {
382 Init(aWidget);
385 bool IsValid() const {
386 return mRawNativeIMEContext &&
387 mOriginProcessID != static_cast<uintptr_t>(-1);
390 void Init(nsIWidget* aWidget);
391 void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext) {
392 InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext));
394 void InitWithRawNativeIMEContext(void* aRawNativeIMEContext);
396 bool operator==(const NativeIMEContext& aOther) const {
397 return mRawNativeIMEContext == aOther.mRawNativeIMEContext &&
398 mOriginProcessID == aOther.mOriginProcessID;
400 bool operator!=(const NativeIMEContext& aOther) const {
401 return !(*this == aOther);
405 struct InputContext final {
406 InputContext()
407 : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT),
408 mMayBeIMEUnaware(false),
409 mHasHandledUserInput(false),
410 mInPrivateBrowsing(false) {}
412 // If InputContext instance is a static variable, any heap allocated stuff
413 // of its members need to be deleted at XPCOM shutdown. Otherwise, it's
414 // detected as memory leak.
415 void ShutDown() {
416 mURI = nullptr;
417 mHTMLInputType.Truncate();
418 mHTMLInputMode.Truncate();
419 mActionHint.Truncate();
420 mAutocapitalize.Truncate();
423 bool IsPasswordEditor() const {
424 return mHTMLInputType.LowerCaseEqualsLiteral("password");
427 NativeKeyBindingsType GetNativeKeyBindingsType() const {
428 MOZ_DIAGNOSTIC_ASSERT(mIMEState.IsEditable());
429 // See GetInputType in IMEStateManager.cpp
430 if (mHTMLInputType.IsEmpty()) {
431 return NativeKeyBindingsType::RichTextEditor;
433 return mHTMLInputType.EqualsLiteral("textarea")
434 ? NativeKeyBindingsType::MultiLineEditor
435 : NativeKeyBindingsType::SingleLineEditor;
438 // https://html.spec.whatwg.org/dev/interaction.html#autocapitalization
439 bool IsAutocapitalizeSupported() const {
440 return !mHTMLInputType.EqualsLiteral("password") &&
441 !mHTMLInputType.EqualsLiteral("url") &&
442 !mHTMLInputType.EqualsLiteral("email");
445 bool IsInputAttributeChanged(const InputContext& aOldContext) const {
446 return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled ||
447 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
448 // input type and inputmode are supported by Windows IME API, GTK
449 // IME API and Android IME API
450 mHTMLInputType != aOldContext.mHTMLInputType ||
451 mHTMLInputMode != aOldContext.mHTMLInputMode ||
452 #endif
453 #if defined(ANDROID) || defined(MOZ_WIDGET_GTK)
454 // autocapitalize is supported by Android IME API and GTK IME API
455 mAutocapitalize != aOldContext.mAutocapitalize ||
456 #endif
457 #if defined(ANDROID)
458 // enterkeyhint is only supported by Android IME API.
459 mActionHint != aOldContext.mActionHint ||
460 #endif
461 false;
464 IMEState mIMEState;
466 // The URI of the document which has the editable element.
467 nsCOMPtr<nsIURI> mURI;
469 /* The type of the input if the input is a html input field */
470 nsString mHTMLInputType;
472 // The value of the inputmode
473 nsString mHTMLInputMode;
475 /* A hint for the action that is performed when the input is submitted */
476 nsString mActionHint;
478 /* A hint for autocapitalize */
479 nsString mAutocapitalize;
482 * mOrigin indicates whether this focus event refers to main or remote
483 * content.
485 enum Origin {
486 // Adjusting focus of content on the main process
487 ORIGIN_MAIN,
488 // Adjusting focus of content in a remote process
489 ORIGIN_CONTENT
491 Origin mOrigin;
493 /* True if the webapp may be unaware of IME events such as input event or
494 * composiion events. This enables a key-events-only mode on Android for
495 * compatibility with webapps relying on key listeners. */
496 bool mMayBeIMEUnaware;
499 * True if the document has ever received user input
501 bool mHasHandledUserInput;
503 /* Whether the owning document of the input element has been loaded
504 * in private browsing mode. */
505 bool mInPrivateBrowsing;
507 bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; }
509 bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; }
511 bool IsOriginCurrentProcess() const {
512 if (XRE_IsParentProcess()) {
513 return IsOriginMainProcess();
515 return IsOriginContentProcess();
519 // FYI: Implemented in nsBaseWidget.cpp
520 const char* ToChar(InputContext::Origin aOrigin);
522 struct InputContextAction final {
524 * mCause indicates what action causes calling nsIWidget::SetInputContext().
525 * It must be one of following values.
527 enum Cause {
528 // The cause is unknown but originated from content. Focus might have been
529 // changed by content script.
530 CAUSE_UNKNOWN,
531 // The cause is unknown but originated from chrome. Focus might have been
532 // changed by chrome script.
533 CAUSE_UNKNOWN_CHROME,
534 // The cause is user's keyboard operation.
535 CAUSE_KEY,
536 // The cause is user's mouse operation.
537 CAUSE_MOUSE,
538 // The cause is user's touch operation (implies mouse)
539 CAUSE_TOUCH,
540 // The cause is users' long press operation.
541 CAUSE_LONGPRESS,
542 // The cause is unknown but it occurs during user input except keyboard
543 // input. E.g., an event handler of a user input event moves focus.
544 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
545 // The cause is unknown but it occurs during keyboard input.
546 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
548 Cause mCause;
551 * mFocusChange indicates what happened for focus.
553 enum FocusChange {
554 FOCUS_NOT_CHANGED,
555 // A content got focus.
556 GOT_FOCUS,
557 // Focused content lost focus.
558 LOST_FOCUS,
559 // Menu got pseudo focus that means focused content isn't changed but
560 // keyboard events will be handled by menu.
561 MENU_GOT_PSEUDO_FOCUS,
562 // Menu lost pseudo focus that means focused content will handle keyboard
563 // events.
564 MENU_LOST_PSEUDO_FOCUS,
565 // The widget is created. When a widget is crated, it may need to notify
566 // IME module to initialize its native IME context. In such case, this is
567 // used. I.e., this isn't used by IMEStateManager.
568 WIDGET_CREATED
570 FocusChange mFocusChange;
572 bool ContentGotFocusByTrustedCause() const {
573 return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN);
576 bool UserMightRequestOpenVKB() const {
577 // If focus is changed, user must not request to open VKB.
578 if (mFocusChange != FOCUS_NOT_CHANGED) {
579 return false;
581 switch (mCause) {
582 // If user clicks or touches focused editor, user must request to open
583 // VKB.
584 case CAUSE_MOUSE:
585 case CAUSE_TOUCH:
586 // If script does something during a user input and that causes changing
587 // input context, user might request to open VKB. E.g., user clicks
588 // dummy editor and JS moves focus to an actual editable node. However,
589 // this should return false if the user input is a keyboard event since
590 // physical keyboard operation shouldn't cause opening VKB.
591 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
592 return true;
593 default:
594 return false;
599 * IsHandlingUserInput() returns true if it's caused by a user action directly
600 * or it's caused by script or something but it occurred while we're handling
601 * a user action. E.g., when it's caused by Element.focus() in an event
602 * handler of a user input, this returns true.
604 static bool IsHandlingUserInput(Cause aCause) {
605 switch (aCause) {
606 case CAUSE_KEY:
607 case CAUSE_MOUSE:
608 case CAUSE_TOUCH:
609 case CAUSE_LONGPRESS:
610 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
611 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
612 return true;
613 default:
614 return false;
618 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); }
620 InputContextAction()
621 : mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED) {}
623 explicit InputContextAction(Cause aCause,
624 FocusChange aFocusChange = FOCUS_NOT_CHANGED)
625 : mCause(aCause), mFocusChange(aFocusChange) {}
628 // IMEMessage is shared by IMEStateManager and TextComposition.
629 // Update values in GeckoEditable.java if you make changes here.
630 // XXX Negative values are used in Android...
631 typedef int8_t IMEMessageType;
632 enum IMEMessage : IMEMessageType {
633 // This is used by IMENotification internally. This means that the instance
634 // hasn't been initialized yet.
635 NOTIFY_IME_OF_NOTHING,
636 // An editable content is getting focus
637 NOTIFY_IME_OF_FOCUS,
638 // An editable content is losing focus
639 NOTIFY_IME_OF_BLUR,
640 // Selection in the focused editable content is changed
641 NOTIFY_IME_OF_SELECTION_CHANGE,
642 // Text in the focused editable content is changed
643 NOTIFY_IME_OF_TEXT_CHANGE,
644 // Notified when a dispatched composition event is handled by the
645 // contents. This must be notified after the other notifications.
646 // Note that if a remote process has focus, this is notified only once when
647 // all dispatched events are handled completely. So, the receiver shouldn't
648 // count number of received this notification for comparing with the number
649 // of dispatched events.
650 // NOTE: If a composition event causes moving focus from the focused editor,
651 // this notification may not be notified as usual. Even in such case,
652 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
653 // should tread the blur notification as including this if there is
654 // pending composition events.
655 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
656 // Position or size of focused element may be changed.
657 NOTIFY_IME_OF_POSITION_CHANGE,
658 // Mouse button event is fired on a character in focused editor
659 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
660 // Request to commit current composition to IME
661 // (some platforms may not support)
662 REQUEST_TO_COMMIT_COMPOSITION,
663 // Request to cancel current composition to IME
664 // (some platforms may not support)
665 REQUEST_TO_CANCEL_COMPOSITION
668 // FYI: Implemented in nsBaseWidget.cpp
669 const char* ToChar(IMEMessage aIMEMessage);
671 struct IMENotification final {
672 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING), mSelectionChangeData() {}
674 IMENotification(const IMENotification& aOther)
675 : mMessage(NOTIFY_IME_OF_NOTHING) {
676 Assign(aOther);
679 ~IMENotification() { Clear(); }
681 MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
682 : mMessage(aMessage), mSelectionChangeData() {
683 switch (aMessage) {
684 case NOTIFY_IME_OF_SELECTION_CHANGE:
685 mSelectionChangeData.mString = new nsString();
686 mSelectionChangeData.Clear();
687 break;
688 case NOTIFY_IME_OF_TEXT_CHANGE:
689 mTextChangeData.Clear();
690 break;
691 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
692 mMouseButtonEventData.mEventMessage = eVoidEvent;
693 mMouseButtonEventData.mOffset = UINT32_MAX;
694 mMouseButtonEventData.mCursorPos.MoveTo(0, 0);
695 mMouseButtonEventData.mCharRect.SetRect(0, 0, 0, 0);
696 mMouseButtonEventData.mButton = -1;
697 mMouseButtonEventData.mButtons = 0;
698 mMouseButtonEventData.mModifiers = 0;
699 break;
700 default:
701 break;
705 void Assign(const IMENotification& aOther) {
706 bool changingMessage = mMessage != aOther.mMessage;
707 if (changingMessage) {
708 Clear();
709 mMessage = aOther.mMessage;
711 switch (mMessage) {
712 case NOTIFY_IME_OF_SELECTION_CHANGE:
713 if (changingMessage) {
714 mSelectionChangeData.mString = new nsString();
716 mSelectionChangeData.Assign(aOther.mSelectionChangeData);
717 break;
718 case NOTIFY_IME_OF_TEXT_CHANGE:
719 mTextChangeData = aOther.mTextChangeData;
720 break;
721 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
722 mMouseButtonEventData = aOther.mMouseButtonEventData;
723 break;
724 default:
725 break;
729 IMENotification& operator=(const IMENotification& aOther) {
730 Assign(aOther);
731 return *this;
734 void Clear() {
735 if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
736 MOZ_ASSERT(mSelectionChangeData.mString);
737 delete mSelectionChangeData.mString;
738 mSelectionChangeData.mString = nullptr;
740 mMessage = NOTIFY_IME_OF_NOTHING;
743 bool HasNotification() const { return mMessage != NOTIFY_IME_OF_NOTHING; }
745 void MergeWith(const IMENotification& aNotification) {
746 switch (mMessage) {
747 case NOTIFY_IME_OF_NOTHING:
748 MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
749 Assign(aNotification);
750 break;
751 case NOTIFY_IME_OF_SELECTION_CHANGE:
752 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
753 mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
754 break;
755 case NOTIFY_IME_OF_TEXT_CHANGE:
756 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
757 mTextChangeData += aNotification.mTextChangeData;
758 break;
759 case NOTIFY_IME_OF_POSITION_CHANGE:
760 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
761 MOZ_ASSERT(aNotification.mMessage == mMessage);
762 break;
763 default:
764 MOZ_CRASH("Merging notification isn't supported");
765 break;
769 IMEMessage mMessage;
771 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
772 struct SelectionChangeDataBase {
773 // Selection range.
774 uint32_t mOffset;
776 // Selected string
777 nsString* mString;
779 // Writing mode at the selection.
780 uint8_t mWritingModeBits;
782 bool mIsInitialized;
783 bool mHasRange;
784 bool mReversed;
785 bool mCausedByComposition;
786 bool mCausedBySelectionEvent;
787 bool mOccurredDuringComposition;
789 // FYI: Cannot we make these methods inline because of an include hell of
790 // RawServoAnimationValueMap
791 void SetWritingMode(const WritingMode& aWritingMode);
792 WritingMode GetWritingMode() const;
794 uint32_t StartOffset() const {
795 MOZ_ASSERT(mHasRange);
796 return mOffset;
798 uint32_t EndOffset() const {
799 MOZ_ASSERT(mHasRange);
800 return mOffset + Length();
802 uint32_t AnchorOffset() const {
803 MOZ_ASSERT(mHasRange);
804 return mOffset + (mReversed ? Length() : 0);
806 uint32_t FocusOffset() const {
807 MOZ_ASSERT(mHasRange);
808 return mOffset + (mReversed ? 0 : Length());
810 const nsString& String() const {
811 MOZ_ASSERT(mHasRange);
812 return *mString;
814 uint32_t Length() const {
815 MOZ_ASSERT(mHasRange);
816 return mString->Length();
818 bool IsInInt32Range() const {
819 return mHasRange && mOffset <= INT32_MAX && Length() <= INT32_MAX &&
820 mOffset + Length() <= INT32_MAX;
822 bool HasRange() const { return mIsInitialized && mHasRange; }
823 bool IsCollapsed() const { return !mHasRange || mString->IsEmpty(); }
824 void ClearSelectionData() {
825 mIsInitialized = false;
826 mHasRange = false;
827 mOffset = UINT32_MAX;
828 mString->Truncate();
829 mWritingModeBits = 0;
830 mReversed = false;
832 void Clear() {
833 ClearSelectionData();
834 mCausedByComposition = false;
835 mCausedBySelectionEvent = false;
836 mOccurredDuringComposition = false;
838 bool IsInitialized() const { return mIsInitialized; }
839 void Assign(const SelectionChangeDataBase& aOther) {
840 mIsInitialized = aOther.mIsInitialized;
841 mHasRange = aOther.mHasRange;
842 if (mIsInitialized && mHasRange) {
843 mOffset = aOther.mOffset;
844 *mString = aOther.String();
845 mReversed = aOther.mReversed;
846 mWritingModeBits = aOther.mWritingModeBits;
847 } else {
848 mOffset = UINT32_MAX;
849 mString->Truncate();
850 mReversed = false;
851 // Let's keep the writing mode for avoiding temporarily changing the
852 // writing mode at no selection range.
854 AssignReason(aOther.mCausedByComposition, aOther.mCausedBySelectionEvent,
855 aOther.mOccurredDuringComposition);
857 void Assign(const WidgetQueryContentEvent& aQuerySelectedTextEvent);
858 void AssignReason(bool aCausedByComposition, bool aCausedBySelectionEvent,
859 bool aOccurredDuringComposition) {
860 mCausedByComposition = aCausedByComposition;
861 mCausedBySelectionEvent = aCausedBySelectionEvent;
862 mOccurredDuringComposition = aOccurredDuringComposition;
865 bool EqualsRange(const SelectionChangeDataBase& aOther) const {
866 if (HasRange() != aOther.HasRange()) {
867 return false;
869 if (!HasRange()) {
870 return true;
872 return mOffset == aOther.mOffset && mString->Equals(*aOther.mString);
874 bool EqualsRangeAndDirection(const SelectionChangeDataBase& aOther) const {
875 return EqualsRange(aOther) &&
876 (!HasRange() || mReversed == aOther.mReversed);
878 bool EqualsRangeAndDirectionAndWritingMode(
879 const SelectionChangeDataBase& aOther) const {
880 return EqualsRangeAndDirection(aOther) &&
881 mWritingModeBits == aOther.mWritingModeBits;
884 bool EqualsRange(const ContentSelection& aContentSelection) const;
885 bool EqualsRangeAndWritingMode(
886 const ContentSelection& aContentSelection) const;
888 OffsetAndData<uint32_t> ToUint32OffsetAndData() const {
889 return OffsetAndData<uint32_t>(mOffset, *mString,
890 OffsetAndDataFor::SelectedString);
894 // SelectionChangeDataBase cannot have constructors because it's used in
895 // the union. Therefore, SelectionChangeData should only implement
896 // constructors. In other words, add other members to
897 // SelectionChangeDataBase.
898 struct SelectionChangeData final : public SelectionChangeDataBase {
899 SelectionChangeData() {
900 mString = &mStringInstance;
901 Clear();
903 explicit SelectionChangeData(const SelectionChangeDataBase& aOther) {
904 mString = &mStringInstance;
905 Assign(aOther);
907 SelectionChangeData(const SelectionChangeData& aOther) {
908 mString = &mStringInstance;
909 Assign(aOther);
911 SelectionChangeData& operator=(const SelectionChangeDataBase& aOther) {
912 mString = &mStringInstance;
913 Assign(aOther);
914 return *this;
916 SelectionChangeData& operator=(const SelectionChangeData& aOther) {
917 mString = &mStringInstance;
918 Assign(aOther);
919 return *this;
922 private:
923 // When SelectionChangeData is used outside of union, it shouldn't create
924 // nsString instance in the heap as far as possible.
925 nsString mStringInstance;
928 struct TextChangeDataBase {
929 // mStartOffset is the start offset of modified or removed text in
930 // original content and inserted text in new content.
931 uint32_t mStartOffset;
932 // mRemovalEndOffset is the end offset of modified or removed text in
933 // original content. If the value is same as mStartOffset, no text hasn't
934 // been removed yet.
935 uint32_t mRemovedEndOffset;
936 // mAddedEndOffset is the end offset of inserted text or same as
937 // mStartOffset if just removed. The vlaue is offset in the new content.
938 uint32_t mAddedEndOffset;
940 // Note that TextChangeDataBase may be the result of merging two or more
941 // changes especially in e10s mode.
943 // mCausedOnlyByComposition is true only when *all* merged changes are
944 // caused by composition.
945 bool mCausedOnlyByComposition;
946 // mIncludingChangesDuringComposition is true if at least one change which
947 // is not caused by composition occurred during the last composition.
948 // Note that if after the last composition is finished and there are some
949 // changes not caused by composition, this is set to false.
950 bool mIncludingChangesDuringComposition;
951 // mIncludingChangesWithoutComposition is true if there is at least one
952 // change which did occur when there wasn't a composition ongoing.
953 bool mIncludingChangesWithoutComposition;
955 uint32_t OldLength() const {
956 MOZ_ASSERT(IsValid());
957 return mRemovedEndOffset - mStartOffset;
959 uint32_t NewLength() const {
960 MOZ_ASSERT(IsValid());
961 return mAddedEndOffset - mStartOffset;
964 // Positive if text is added. Negative if text is removed.
965 int64_t Difference() const { return mAddedEndOffset - mRemovedEndOffset; }
967 bool IsInInt32Range() const {
968 MOZ_ASSERT(IsValid());
969 return mStartOffset <= INT32_MAX && mRemovedEndOffset <= INT32_MAX &&
970 mAddedEndOffset <= INT32_MAX;
973 bool IsValid() const {
974 return !(mStartOffset == UINT32_MAX && !mRemovedEndOffset &&
975 !mAddedEndOffset);
978 void Clear() {
979 mStartOffset = UINT32_MAX;
980 mRemovedEndOffset = mAddedEndOffset = 0;
983 void MergeWith(const TextChangeDataBase& aOther);
984 TextChangeDataBase& operator+=(const TextChangeDataBase& aOther) {
985 MergeWith(aOther);
986 return *this;
989 #ifdef DEBUG
990 void Test();
991 #endif // #ifdef DEBUG
994 // TextChangeDataBase cannot have constructors because they are used in union.
995 // Therefore, TextChangeData should only implement constructor. In other
996 // words, add other members to TextChangeDataBase.
997 struct TextChangeData : public TextChangeDataBase {
998 TextChangeData() { Clear(); }
1000 TextChangeData(uint32_t aStartOffset, uint32_t aRemovedEndOffset,
1001 uint32_t aAddedEndOffset, bool aCausedByComposition,
1002 bool aOccurredDuringComposition) {
1003 MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
1004 "removed end offset must not be smaller than start offset");
1005 MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
1006 "added end offset must not be smaller than start offset");
1007 mStartOffset = aStartOffset;
1008 mRemovedEndOffset = aRemovedEndOffset;
1009 mAddedEndOffset = aAddedEndOffset;
1010 mCausedOnlyByComposition = aCausedByComposition;
1011 mIncludingChangesDuringComposition =
1012 !aCausedByComposition && aOccurredDuringComposition;
1013 mIncludingChangesWithoutComposition =
1014 !aCausedByComposition && !aOccurredDuringComposition;
1018 struct MouseButtonEventData {
1019 // The value of WidgetEvent::mMessage
1020 EventMessage mEventMessage;
1021 // Character offset from the start of the focused editor under the cursor
1022 uint32_t mOffset;
1023 // Cursor position in pixels relative to the widget
1024 LayoutDeviceIntPoint mCursorPos;
1025 // Character rect in pixels under the cursor relative to the widget
1026 LayoutDeviceIntRect mCharRect;
1027 // The value of WidgetMouseEventBase::button and buttons
1028 int16_t mButton;
1029 int16_t mButtons;
1030 // The value of WidgetInputEvent::modifiers
1031 Modifiers mModifiers;
1034 union {
1035 // NOTIFY_IME_OF_SELECTION_CHANGE specific data
1036 SelectionChangeDataBase mSelectionChangeData;
1038 // NOTIFY_IME_OF_TEXT_CHANGE specific data
1039 TextChangeDataBase mTextChangeData;
1041 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
1042 MouseButtonEventData mMouseButtonEventData;
1045 void SetData(const SelectionChangeDataBase& aSelectionChangeData) {
1046 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
1047 mSelectionChangeData.Assign(aSelectionChangeData);
1050 void SetData(const TextChangeDataBase& aTextChangeData) {
1051 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
1052 mTextChangeData = aTextChangeData;
1056 struct CandidateWindowPosition {
1057 // Upper left corner of the candidate window if mExcludeRect is false.
1058 // Otherwise, the position currently interested. E.g., caret position.
1059 LayoutDeviceIntPoint mPoint;
1060 // Rect which shouldn't be overlapped with the candidate window.
1061 // This is valid only when mExcludeRect is true.
1062 LayoutDeviceIntRect mRect;
1063 // See explanation of mPoint and mRect.
1064 bool mExcludeRect;
1067 std::ostream& operator<<(std::ostream& aStream, const IMEEnabled& aEnabled);
1068 std::ostream& operator<<(std::ostream& aStream, const IMEState::Open& aOpen);
1069 std::ostream& operator<<(std::ostream& aStream, const IMEState& aState);
1070 std::ostream& operator<<(std::ostream& aStream,
1071 const InputContext::Origin& aOrigin);
1072 std::ostream& operator<<(std::ostream& aStream, const InputContext& aContext);
1073 std::ostream& operator<<(std::ostream& aStream,
1074 const InputContextAction::Cause& aCause);
1075 std::ostream& operator<<(std::ostream& aStream,
1076 const InputContextAction::FocusChange& aFocusChange);
1077 std::ostream& operator<<(std::ostream& aStream,
1078 const IMENotification::SelectionChangeDataBase& aData);
1079 std::ostream& operator<<(std::ostream& aStream,
1080 const IMENotification::TextChangeDataBase& aData);
1082 } // namespace widget
1083 } // namespace mozilla
1085 #endif // #ifndef mozilla_widget_IMEData_h_