summaryrefslogtreecommitdiffstats
path: root/widget/IMEData.h
diff options
context:
space:
mode:
Diffstat (limited to 'widget/IMEData.h')
-rw-r--r--widget/IMEData.h882
1 files changed, 882 insertions, 0 deletions
diff --git a/widget/IMEData.h b/widget/IMEData.h
new file mode 100644
index 000000000..b12497be3
--- /dev/null
+++ b/widget/IMEData.h
@@ -0,0 +1,882 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_widget_IMEData_h_
+#define mozilla_widget_IMEData_h_
+
+#include "nsPoint.h"
+#include "nsRect.h"
+#include "nsStringGlue.h"
+
+class nsIWidget;
+
+namespace mozilla {
+
+class WritingMode;
+
+} // namespace mozilla
+
+/**
+ * Preference for receiving IME updates
+ *
+ * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
+ * change and/or selection change and call nsIWidget::NotifyIME() with
+ * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
+ * Please note that the text change observing cost is very expensive especially
+ * on an HTML editor has focus.
+ * If the IME implementation on a particular platform doesn't care about
+ * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
+ * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
+ * If the IME implementation needs notifications even while our process is
+ * deactive, it should also set NOTIFY_DURING_DEACTIVE.
+ */
+struct nsIMEUpdatePreference final
+{
+ typedef uint8_t Notifications;
+
+ enum : Notifications
+ {
+ NOTIFY_NOTHING = 0,
+ NOTIFY_TEXT_CHANGE = 1 << 1,
+ NOTIFY_POSITION_CHANGE = 1 << 2,
+ // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
+ // or released on a character in the focused editor. The notification is
+ // notified to IME as a mouse event. If it's consumed by IME, NotifyIME()
+ // returns NS_SUCCESS_EVENT_CONSUMED. Otherwise, it returns NS_OK if it's
+ // handled without any error.
+ NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR = 1 << 3,
+ // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
+ // or more compositions are possible. E.g., Mac and Linux (GTK).
+ NOTIFY_DURING_DEACTIVE = 1 << 7
+ };
+
+ nsIMEUpdatePreference()
+ : mWantUpdates(NOTIFY_NOTHING)
+ {
+ }
+
+ explicit nsIMEUpdatePreference(Notifications aWantUpdates)
+ : mWantUpdates(aWantUpdates)
+ {
+ }
+
+ nsIMEUpdatePreference operator|(const nsIMEUpdatePreference& aOther) const
+ {
+ return nsIMEUpdatePreference(aOther.mWantUpdates | mWantUpdates);
+ }
+
+ bool WantTextChange() const
+ {
+ return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
+ }
+
+ bool WantPositionChanged() const
+ {
+ return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
+ }
+
+ bool WantChanges() const
+ {
+ return WantTextChange();
+ }
+
+ bool WantMouseButtonEventOnChar() const
+ {
+ return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
+ }
+
+ bool WantDuringDeactive() const
+ {
+ return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
+ }
+
+ Notifications mWantUpdates;
+};
+
+/**
+ * Contains IMEStatus plus information about the current
+ * input context that the IME can use as hints if desired.
+ */
+
+namespace mozilla {
+namespace widget {
+
+struct IMEState final
+{
+ /**
+ * IME enabled states, the mEnabled value of
+ * SetInputContext()/GetInputContext() should be one value of following
+ * values.
+ *
+ * WARNING: If you change these values, you also need to edit:
+ * nsIDOMWindowUtils.idl
+ * nsContentUtils::GetWidgetStatusFromIMEStatus
+ */
+ enum Enabled
+ {
+ /**
+ * 'Disabled' means the user cannot use IME. So, the IME open state should
+ * be 'closed' during 'disabled'.
+ */
+ DISABLED,
+ /**
+ * 'Enabled' means the user can use IME.
+ */
+ ENABLED,
+ /**
+ * 'Password' state is a special case for the password editors.
+ * E.g., on mac, the password editors should disable the non-Roman
+ * keyboard layouts at getting focus. Thus, the password editor may have
+ * special rules on some platforms.
+ */
+ PASSWORD,
+ /**
+ * This state is used when a plugin is focused.
+ * When a plug-in is focused content, we should send native events
+ * directly. Because we don't process some native events, but they may
+ * be needed by the plug-in.
+ */
+ PLUGIN,
+ /**
+ * 'Unknown' is useful when you cache this enum. So, this shouldn't be
+ * used with nsIWidget::SetInputContext().
+ */
+ UNKNOWN
+ };
+ Enabled mEnabled;
+
+ /**
+ * IME open states the mOpen value of SetInputContext() should be one value of
+ * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE. GetInputContext() should return
+ * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
+ */
+ enum Open
+ {
+ /**
+ * 'Unsupported' means the platform cannot return actual IME open state.
+ * This value is used only by GetInputContext().
+ */
+ OPEN_STATE_NOT_SUPPORTED,
+ /**
+ * 'Don't change' means the widget shouldn't change IME open state when
+ * SetInputContext() is called.
+ */
+ DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED,
+ /**
+ * 'Open' means that IME should compose in its primary language (or latest
+ * input mode except direct ASCII character input mode). Even if IME is
+ * opened by this value, users should be able to close IME by theirselves.
+ * Web contents can specify this value by |ime-mode: active;|.
+ */
+ OPEN,
+ /**
+ * 'Closed' means that IME shouldn't handle key events (or should handle
+ * as ASCII character inputs on mobile device). Even if IME is closed by
+ * this value, users should be able to open IME by theirselves.
+ * Web contents can specify this value by |ime-mode: inactive;|.
+ */
+ CLOSED
+ };
+ Open mOpen;
+
+ IMEState()
+ : mEnabled(ENABLED)
+ , mOpen(DONT_CHANGE_OPEN_STATE)
+ {
+ }
+
+ explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE)
+ : mEnabled(aEnabled)
+ , mOpen(aOpen)
+ {
+ }
+
+ // Returns true if the user can input characters.
+ // This means that a plain text editor, an HTML editor, a password editor or
+ // a plain text editor whose ime-mode is "disabled".
+ bool IsEditable() const
+ {
+ return mEnabled == ENABLED || mEnabled == PASSWORD;
+ }
+ // Returns true if the user might be able to input characters.
+ // This means that a plain text editor, an HTML editor, a password editor,
+ // a plain text editor whose ime-mode is "disabled" or a windowless plugin
+ // has focus.
+ bool MaybeEditable() const
+ {
+ return IsEditable() || mEnabled == PLUGIN;
+ }
+};
+
+// NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context.
+// If there can be only one IME composition in a process, this can be used.
+#define NS_ONLY_ONE_NATIVE_IME_CONTEXT \
+ (reinterpret_cast<void*>(static_cast<intptr_t>(-1)))
+
+struct NativeIMEContext final
+{
+ // Pointer to native IME context. Typically this is the result of
+ // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process.
+ // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT.
+ uintptr_t mRawNativeIMEContext;
+ // Process ID of the origin of mNativeIMEContext.
+ uint64_t mOriginProcessID;
+
+ NativeIMEContext()
+ {
+ Init(nullptr);
+ }
+
+ explicit NativeIMEContext(nsIWidget* aWidget)
+ {
+ Init(aWidget);
+ }
+
+ bool IsValid() const
+ {
+ return mRawNativeIMEContext &&
+ mOriginProcessID != static_cast<uintptr_t>(-1);
+ }
+
+ void Init(nsIWidget* aWidget);
+ void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext)
+ {
+ InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext));
+ }
+ void InitWithRawNativeIMEContext(void* aRawNativeIMEContext);
+
+ bool operator==(const NativeIMEContext& aOther) const
+ {
+ return mRawNativeIMEContext == aOther.mRawNativeIMEContext &&
+ mOriginProcessID == aOther.mOriginProcessID;
+ }
+ bool operator!=(const NativeIMEContext& aOther) const
+ {
+ return !(*this == aOther);
+ }
+};
+
+struct InputContext final
+{
+ InputContext()
+ : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT)
+ , mMayBeIMEUnaware(false)
+ {
+ }
+
+ bool IsPasswordEditor() const
+ {
+ return mHTMLInputType.LowerCaseEqualsLiteral("password");
+ }
+
+ IMEState mIMEState;
+
+ /* The type of the input if the input is a html input field */
+ nsString mHTMLInputType;
+
+ /* The type of the inputmode */
+ nsString mHTMLInputInputmode;
+
+ /* A hint for the action that is performed when the input is submitted */
+ nsString mActionHint;
+
+ /**
+ * mOrigin indicates whether this focus event refers to main or remote
+ * content.
+ */
+ enum Origin
+ {
+ // Adjusting focus of content on the main process
+ ORIGIN_MAIN,
+ // Adjusting focus of content in a remote process
+ ORIGIN_CONTENT
+ };
+ Origin mOrigin;
+
+ /* True if the webapp may be unaware of IME events such as input event or
+ * composiion events. This enables a key-events-only mode on Android for
+ * compatibility with webapps relying on key listeners. */
+ bool mMayBeIMEUnaware;
+
+ bool IsOriginMainProcess() const
+ {
+ return mOrigin == ORIGIN_MAIN;
+ }
+
+ bool IsOriginContentProcess() const
+ {
+ return mOrigin == ORIGIN_CONTENT;
+ }
+
+ bool IsOriginCurrentProcess() const
+ {
+ if (XRE_IsParentProcess()) {
+ return IsOriginMainProcess();
+ }
+ return IsOriginContentProcess();
+ }
+};
+
+struct InputContextAction final
+{
+ /**
+ * mCause indicates what action causes calling nsIWidget::SetInputContext().
+ * It must be one of following values.
+ */
+ enum Cause
+ {
+ // The cause is unknown but originated from content. Focus might have been
+ // changed by content script.
+ CAUSE_UNKNOWN,
+ // The cause is unknown but originated from chrome. Focus might have been
+ // changed by chrome script.
+ CAUSE_UNKNOWN_CHROME,
+ // The cause is user's keyboard operation.
+ CAUSE_KEY,
+ // The cause is user's mouse operation.
+ CAUSE_MOUSE,
+ // The cause is user's touch operation (implies mouse)
+ CAUSE_TOUCH
+ };
+ Cause mCause;
+
+ /**
+ * mFocusChange indicates what happened for focus.
+ */
+ enum FocusChange
+ {
+ FOCUS_NOT_CHANGED,
+ // A content got focus.
+ GOT_FOCUS,
+ // Focused content lost focus.
+ LOST_FOCUS,
+ // Menu got pseudo focus that means focused content isn't changed but
+ // keyboard events will be handled by menu.
+ MENU_GOT_PSEUDO_FOCUS,
+ // Menu lost pseudo focus that means focused content will handle keyboard
+ // events.
+ MENU_LOST_PSEUDO_FOCUS
+ };
+ FocusChange mFocusChange;
+
+ bool ContentGotFocusByTrustedCause() const
+ {
+ return (mFocusChange == GOT_FOCUS &&
+ mCause != CAUSE_UNKNOWN);
+ }
+
+ bool UserMightRequestOpenVKB() const
+ {
+ return (mFocusChange == FOCUS_NOT_CHANGED &&
+ (mCause == CAUSE_MOUSE || mCause == CAUSE_TOUCH));
+ }
+
+ static bool IsUserAction(Cause aCause)
+ {
+ switch (aCause) {
+ case CAUSE_KEY:
+ case CAUSE_MOUSE:
+ case CAUSE_TOUCH:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ InputContextAction()
+ : mCause(CAUSE_UNKNOWN)
+ , mFocusChange(FOCUS_NOT_CHANGED)
+ {
+ }
+
+ explicit InputContextAction(Cause aCause,
+ FocusChange aFocusChange = FOCUS_NOT_CHANGED)
+ : mCause(aCause)
+ , mFocusChange(aFocusChange)
+ {
+ }
+};
+
+// IMEMessage is shared by IMEStateManager and TextComposition.
+// Update values in GeckoEditable.java if you make changes here.
+// XXX Negative values are used in Android...
+typedef int8_t IMEMessageType;
+enum IMEMessage : IMEMessageType
+{
+ // This is used by IMENotification internally. This means that the instance
+ // hasn't been initialized yet.
+ NOTIFY_IME_OF_NOTHING,
+ // An editable content is getting focus
+ NOTIFY_IME_OF_FOCUS,
+ // An editable content is losing focus
+ NOTIFY_IME_OF_BLUR,
+ // Selection in the focused editable content is changed
+ NOTIFY_IME_OF_SELECTION_CHANGE,
+ // Text in the focused editable content is changed
+ NOTIFY_IME_OF_TEXT_CHANGE,
+ // Notified when a dispatched composition event is handled by the
+ // contents. This must be notified after the other notifications.
+ // Note that if a remote process has focus, this is notified only once when
+ // all dispatched events are handled completely. So, the receiver shouldn't
+ // count number of received this notification for comparing with the number
+ // of dispatched events.
+ // NOTE: If a composition event causes moving focus from the focused editor,
+ // this notification may not be notified as usual. Even in such case,
+ // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners
+ // should tread the blur notification as including this if there is
+ // pending composition events.
+ NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED,
+ // Position or size of focused element may be changed.
+ NOTIFY_IME_OF_POSITION_CHANGE,
+ // Mouse button event is fired on a character in focused editor
+ NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
+ // Request to commit current composition to IME
+ // (some platforms may not support)
+ REQUEST_TO_COMMIT_COMPOSITION,
+ // Request to cancel current composition to IME
+ // (some platforms may not support)
+ REQUEST_TO_CANCEL_COMPOSITION
+};
+
+// FYI: Implemented in nsBaseWidget.cpp
+const char* ToChar(IMEMessage aIMEMessage);
+
+struct IMENotification final
+{
+ IMENotification()
+ : mMessage(NOTIFY_IME_OF_NOTHING)
+ {
+ }
+
+ IMENotification(const IMENotification& aOther)
+ : mMessage(NOTIFY_IME_OF_NOTHING)
+ {
+ Assign(aOther);
+ }
+
+ ~IMENotification()
+ {
+ Clear();
+ }
+
+ MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
+ : mMessage(aMessage)
+ {
+ switch (aMessage) {
+ case NOTIFY_IME_OF_SELECTION_CHANGE:
+ mSelectionChangeData.mString = new nsString();
+ mSelectionChangeData.Clear();
+ break;
+ case NOTIFY_IME_OF_TEXT_CHANGE:
+ mTextChangeData.Clear();
+ break;
+ case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
+ mMouseButtonEventData.mEventMessage = eVoidEvent;
+ mMouseButtonEventData.mOffset = UINT32_MAX;
+ mMouseButtonEventData.mCursorPos.Set(nsIntPoint(0, 0));
+ mMouseButtonEventData.mCharRect.Set(nsIntRect(0, 0, 0, 0));
+ mMouseButtonEventData.mButton = -1;
+ mMouseButtonEventData.mButtons = 0;
+ mMouseButtonEventData.mModifiers = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Assign(const IMENotification& aOther)
+ {
+ bool changingMessage = mMessage != aOther.mMessage;
+ if (changingMessage) {
+ Clear();
+ mMessage = aOther.mMessage;
+ }
+ switch (mMessage) {
+ case NOTIFY_IME_OF_SELECTION_CHANGE:
+ if (changingMessage) {
+ mSelectionChangeData.mString = new nsString();
+ }
+ mSelectionChangeData.Assign(aOther.mSelectionChangeData);
+ break;
+ case NOTIFY_IME_OF_TEXT_CHANGE:
+ mTextChangeData = aOther.mTextChangeData;
+ break;
+ case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
+ mMouseButtonEventData = aOther.mMouseButtonEventData;
+ break;
+ default:
+ break;
+ }
+ }
+
+ IMENotification& operator=(const IMENotification& aOther)
+ {
+ Assign(aOther);
+ return *this;
+ }
+
+ void Clear()
+ {
+ if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) {
+ MOZ_ASSERT(mSelectionChangeData.mString);
+ delete mSelectionChangeData.mString;
+ mSelectionChangeData.mString = nullptr;
+ }
+ mMessage = NOTIFY_IME_OF_NOTHING;
+ }
+
+ bool HasNotification() const
+ {
+ return mMessage != NOTIFY_IME_OF_NOTHING;
+ }
+
+ void MergeWith(const IMENotification& aNotification)
+ {
+ switch (mMessage) {
+ case NOTIFY_IME_OF_NOTHING:
+ MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING);
+ Assign(aNotification);
+ break;
+ case NOTIFY_IME_OF_SELECTION_CHANGE:
+ MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
+ mSelectionChangeData.Assign(aNotification.mSelectionChangeData);
+ break;
+ case NOTIFY_IME_OF_TEXT_CHANGE:
+ MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
+ mTextChangeData += aNotification.mTextChangeData;
+ break;
+ case NOTIFY_IME_OF_POSITION_CHANGE:
+ case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
+ MOZ_ASSERT(aNotification.mMessage == mMessage);
+ break;
+ default:
+ MOZ_CRASH("Merging notification isn't supported");
+ break;
+ }
+ }
+
+ IMEMessage mMessage;
+
+ struct Point
+ {
+ int32_t mX;
+ int32_t mY;
+
+ void Set(const nsIntPoint& aPoint)
+ {
+ mX = aPoint.x;
+ mY = aPoint.y;
+ }
+ nsIntPoint AsIntPoint() const
+ {
+ return nsIntPoint(mX, mY);
+ }
+ };
+
+ struct Rect
+ {
+ int32_t mX;
+ int32_t mY;
+ int32_t mWidth;
+ int32_t mHeight;
+
+ void Set(const nsIntRect& aRect)
+ {
+ mX = aRect.x;
+ mY = aRect.y;
+ mWidth = aRect.width;
+ mHeight = aRect.height;
+ }
+ nsIntRect AsIntRect() const
+ {
+ return nsIntRect(mX, mY, mWidth, mHeight);
+ }
+ };
+
+ // NOTIFY_IME_OF_SELECTION_CHANGE specific data
+ struct SelectionChangeDataBase
+ {
+ // Selection range.
+ uint32_t mOffset;
+
+ // Selected string
+ nsString* mString;
+
+ // Writing mode at the selection.
+ uint8_t mWritingMode;
+
+ bool mReversed;
+ bool mCausedByComposition;
+ bool mCausedBySelectionEvent;
+ bool mOccurredDuringComposition;
+
+ void SetWritingMode(const WritingMode& aWritingMode);
+ WritingMode GetWritingMode() const;
+
+ uint32_t StartOffset() const
+ {
+ return mOffset + (mReversed ? Length() : 0);
+ }
+ uint32_t EndOffset() const
+ {
+ return mOffset + (mReversed ? 0 : Length());
+ }
+ const nsString& String() const
+ {
+ return *mString;
+ }
+ uint32_t Length() const
+ {
+ return mString->Length();
+ }
+ bool IsInInt32Range() const
+ {
+ return mOffset + Length() <= INT32_MAX;
+ }
+ bool IsCollapsed() const
+ {
+ return mString->IsEmpty();
+ }
+ void ClearSelectionData()
+ {
+ mOffset = UINT32_MAX;
+ mString->Truncate();
+ mWritingMode = 0;
+ mReversed = false;
+ }
+ void Clear()
+ {
+ ClearSelectionData();
+ mCausedByComposition = false;
+ mCausedBySelectionEvent = false;
+ mOccurredDuringComposition = false;
+ }
+ bool IsValid() const
+ {
+ return mOffset != UINT32_MAX;
+ }
+ void Assign(const SelectionChangeDataBase& aOther)
+ {
+ mOffset = aOther.mOffset;
+ *mString = aOther.String();
+ mWritingMode = aOther.mWritingMode;
+ mReversed = aOther.mReversed;
+ AssignReason(aOther.mCausedByComposition,
+ aOther.mCausedBySelectionEvent,
+ aOther.mOccurredDuringComposition);
+ }
+ void AssignReason(bool aCausedByComposition,
+ bool aCausedBySelectionEvent,
+ bool aOccurredDuringComposition)
+ {
+ mCausedByComposition = aCausedByComposition;
+ mCausedBySelectionEvent = aCausedBySelectionEvent;
+ mOccurredDuringComposition = aOccurredDuringComposition;
+ }
+ };
+
+ // SelectionChangeDataBase cannot have constructors because it's used in
+ // the union. Therefore, SelectionChangeData should only implement
+ // constructors. In other words, add other members to
+ // SelectionChangeDataBase.
+ struct SelectionChangeData final : public SelectionChangeDataBase
+ {
+ SelectionChangeData()
+ {
+ mString = &mStringInstance;
+ Clear();
+ }
+ explicit SelectionChangeData(const SelectionChangeDataBase& aOther)
+ {
+ mString = &mStringInstance;
+ Assign(aOther);
+ }
+ SelectionChangeData(const SelectionChangeData& aOther)
+ {
+ mString = &mStringInstance;
+ Assign(aOther);
+ }
+ SelectionChangeData& operator=(const SelectionChangeDataBase& aOther)
+ {
+ mString = &mStringInstance;
+ Assign(aOther);
+ return *this;
+ }
+ SelectionChangeData& operator=(const SelectionChangeData& aOther)
+ {
+ mString = &mStringInstance;
+ Assign(aOther);
+ return *this;
+ }
+
+ private:
+ // When SelectionChangeData is used outside of union, it shouldn't create
+ // nsString instance in the heap as far as possible.
+ nsString mStringInstance;
+ };
+
+ struct TextChangeDataBase
+ {
+ // mStartOffset is the start offset of modified or removed text in
+ // original content and inserted text in new content.
+ uint32_t mStartOffset;
+ // mRemovalEndOffset is the end offset of modified or removed text in
+ // original content. If the value is same as mStartOffset, no text hasn't
+ // been removed yet.
+ uint32_t mRemovedEndOffset;
+ // mAddedEndOffset is the end offset of inserted text or same as
+ // mStartOffset if just removed. The vlaue is offset in the new content.
+ uint32_t mAddedEndOffset;
+
+ // Note that TextChangeDataBase may be the result of merging two or more
+ // changes especially in e10s mode.
+
+ // mCausedOnlyByComposition is true only when *all* merged changes are
+ // caused by composition.
+ bool mCausedOnlyByComposition;
+ // mIncludingChangesDuringComposition is true if at least one change which
+ // is not caused by composition occurred during the last composition.
+ // Note that if after the last composition is finished and there are some
+ // changes not caused by composition, this is set to false.
+ bool mIncludingChangesDuringComposition;
+ // mIncludingChangesWithoutComposition is true if there is at least one
+ // change which did occur when there wasn't a composition ongoing.
+ bool mIncludingChangesWithoutComposition;
+
+ uint32_t OldLength() const
+ {
+ MOZ_ASSERT(IsValid());
+ return mRemovedEndOffset - mStartOffset;
+ }
+ uint32_t NewLength() const
+ {
+ MOZ_ASSERT(IsValid());
+ return mAddedEndOffset - mStartOffset;
+ }
+
+ // Positive if text is added. Negative if text is removed.
+ int64_t Difference() const
+ {
+ return mAddedEndOffset - mRemovedEndOffset;
+ }
+
+ bool IsInInt32Range() const
+ {
+ MOZ_ASSERT(IsValid());
+ return mStartOffset <= INT32_MAX &&
+ mRemovedEndOffset <= INT32_MAX &&
+ mAddedEndOffset <= INT32_MAX;
+ }
+
+ bool IsValid() const
+ {
+ return !(mStartOffset == UINT32_MAX &&
+ !mRemovedEndOffset && !mAddedEndOffset);
+ }
+
+ void Clear()
+ {
+ mStartOffset = UINT32_MAX;
+ mRemovedEndOffset = mAddedEndOffset = 0;
+ }
+
+ void MergeWith(const TextChangeDataBase& aOther);
+ TextChangeDataBase& operator+=(const TextChangeDataBase& aOther)
+ {
+ MergeWith(aOther);
+ return *this;
+ }
+
+#ifdef DEBUG
+ void Test();
+#endif // #ifdef DEBUG
+ };
+
+ // TextChangeDataBase cannot have constructors because they are used in union.
+ // Therefore, TextChangeData should only implement constructor. In other
+ // words, add other members to TextChangeDataBase.
+ struct TextChangeData : public TextChangeDataBase
+ {
+ TextChangeData() { Clear(); }
+
+ TextChangeData(uint32_t aStartOffset,
+ uint32_t aRemovedEndOffset,
+ uint32_t aAddedEndOffset,
+ bool aCausedByComposition,
+ bool aOccurredDuringComposition)
+ {
+ MOZ_ASSERT(aRemovedEndOffset >= aStartOffset,
+ "removed end offset must not be smaller than start offset");
+ MOZ_ASSERT(aAddedEndOffset >= aStartOffset,
+ "added end offset must not be smaller than start offset");
+ mStartOffset = aStartOffset;
+ mRemovedEndOffset = aRemovedEndOffset;
+ mAddedEndOffset = aAddedEndOffset;
+ mCausedOnlyByComposition = aCausedByComposition;
+ mIncludingChangesDuringComposition =
+ !aCausedByComposition && aOccurredDuringComposition;
+ mIncludingChangesWithoutComposition =
+ !aCausedByComposition && !aOccurredDuringComposition;
+ }
+ };
+
+ struct MouseButtonEventData
+ {
+ // The value of WidgetEvent::mMessage
+ EventMessage mEventMessage;
+ // Character offset from the start of the focused editor under the cursor
+ uint32_t mOffset;
+ // Cursor position in pixels relative to the widget
+ Point mCursorPos;
+ // Character rect in pixels under the cursor relative to the widget
+ Rect mCharRect;
+ // The value of WidgetMouseEventBase::button and buttons
+ int16_t mButton;
+ int16_t mButtons;
+ // The value of WidgetInputEvent::modifiers
+ Modifiers mModifiers;
+ };
+
+ union
+ {
+ // NOTIFY_IME_OF_SELECTION_CHANGE specific data
+ SelectionChangeDataBase mSelectionChangeData;
+
+ // NOTIFY_IME_OF_TEXT_CHANGE specific data
+ TextChangeDataBase mTextChangeData;
+
+ // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
+ MouseButtonEventData mMouseButtonEventData;
+ };
+
+ void SetData(const SelectionChangeDataBase& aSelectionChangeData)
+ {
+ MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
+ mSelectionChangeData.Assign(aSelectionChangeData);
+ }
+
+ void SetData(const TextChangeDataBase& aTextChangeData)
+ {
+ MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE);
+ mTextChangeData = aTextChangeData;
+ }
+};
+
+struct CandidateWindowPosition
+{
+ // Upper left corner of the candidate window if mExcludeRect is false.
+ // Otherwise, the position currently interested. E.g., caret position.
+ LayoutDeviceIntPoint mPoint;
+ // Rect which shouldn't be overlapped with the candidate window.
+ // This is valid only when mExcludeRect is true.
+ LayoutDeviceIntRect mRect;
+ // See explanation of mPoint and mRect.
+ bool mExcludeRect;
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // #ifndef mozilla_widget_IMEData_h_