gecko-dev/widget/IMEData.h
Makoto Kato e82e9e223d Bug 1425291 - Part 3. Add autocapitalize to InputContext. r=masayuki
Autocapitalize isn't applied if type is url, email or password. If these types,
truncate autocapitalize value in InputContext not to pass it to widget.

Differential Revision: https://phabricator.services.mozilla.com/D86676
2020-09-15 14:27:00 +00:00

840 lines
29 KiB
C++

/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/EventForwards.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "Units.h"
class nsIWidget;
namespace mozilla {
class WritingMode;
namespace widget {
/**
* 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 IMENotificationRequests 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,
NOTIFY_ALL = NOTIFY_TEXT_CHANGE | NOTIFY_POSITION_CHANGE |
NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR,
};
IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING) {}
explicit IMENotificationRequests(Notifications aWantUpdates)
: mWantUpdates(aWantUpdates) {}
IMENotificationRequests operator|(
const IMENotificationRequests& aOther) const {
return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates);
}
IMENotificationRequests& operator|=(const IMENotificationRequests& aOther) {
mWantUpdates |= aOther.mWantUpdates;
return *this;
}
bool operator==(const IMENotificationRequests& aOther) const {
return mWantUpdates == aOther.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.
*/
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() : mRawNativeIMEContext(0), mOriginProcessID(0) {
Init(nullptr);
}
explicit NativeIMEContext(nsIWidget* aWidget)
: mRawNativeIMEContext(0), mOriginProcessID(0) {
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),
mHasHandledUserInput(false),
mInPrivateBrowsing(false) {}
// If InputContext instance is a static variable, any heap allocated stuff
// of its members need to be deleted at XPCOM shutdown. Otherwise, it's
// detected as memory leak.
void ShutDown() {
mHTMLInputType.Truncate();
mHTMLInputInputmode.Truncate();
mActionHint.Truncate();
mAutocapitalize.Truncate();
}
bool IsPasswordEditor() const {
return mHTMLInputType.LowerCaseEqualsLiteral("password");
}
// https://html.spec.whatwg.org/dev/interaction.html#autocapitalization
bool IsAutocapitalizeSupported() const {
return !mHTMLInputType.EqualsLiteral("password") &&
!mHTMLInputType.EqualsLiteral("url") &&
!mHTMLInputType.EqualsLiteral("email");
}
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;
/* A hint for autocapitalize */
nsString mAutocapitalize;
/**
* 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;
/**
* True if the document has ever received user input
*/
bool mHasHandledUserInput;
/* Whether the owning document of the input element has been loaded
* in private browsing mode. */
bool mInPrivateBrowsing;
bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; }
bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; }
bool IsOriginCurrentProcess() const {
if (XRE_IsParentProcess()) {
return IsOriginMainProcess();
}
return IsOriginContentProcess();
}
};
// FYI: Implemented in nsBaseWidget.cpp
const char* ToChar(InputContext::Origin aOrigin);
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,
// The cause is users' long press operation.
CAUSE_LONGPRESS,
// The cause is unknown but it occurs during user input except keyboard
// input. E.g., an event handler of a user input event moves focus.
CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
// The cause is unknown but it occurs during keyboard input.
CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
};
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,
// The widget is created. When a widget is crated, it may need to notify
// IME module to initialize its native IME context. In such case, this is
// used. I.e., this isn't used by IMEStateManager.
WIDGET_CREATED
};
FocusChange mFocusChange;
bool ContentGotFocusByTrustedCause() const {
return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN);
}
bool UserMightRequestOpenVKB() const {
// If focus is changed, user must not request to open VKB.
if (mFocusChange != FOCUS_NOT_CHANGED) {
return false;
}
switch (mCause) {
// If user clicks or touches focused editor, user must request to open
// VKB.
case CAUSE_MOUSE:
case CAUSE_TOUCH:
// If script does something during a user input and that causes changing
// input context, user might request to open VKB. E.g., user clicks
// dummy editor and JS moves focus to an actual editable node. However,
// this should return false if the user input is a keyboard event since
// physical keyboard operation shouldn't cause opening VKB.
case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
return true;
default:
return false;
}
}
/**
* IsHandlingUserInput() returns true if it's caused by a user action directly
* or it's caused by script or something but it occurred while we're handling
* a user action. E.g., when it's caused by Element.focus() in an event
* handler of a user input, this returns true.
*/
static bool IsHandlingUserInput(Cause aCause) {
switch (aCause) {
case CAUSE_KEY:
case CAUSE_MOUSE:
case CAUSE_TOUCH:
case CAUSE_LONGPRESS:
case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
return true;
default:
return false;
}
}
bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); }
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), mSelectionChangeData() {}
IMENotification(const IMENotification& aOther)
: mMessage(NOTIFY_IME_OF_NOTHING) {
Assign(aOther);
}
~IMENotification() { Clear(); }
MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
: mMessage(aMessage), mSelectionChangeData() {
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) {
aRect.GetRect(&mX, &mY, &mWidth, &mHeight);
}
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;
};
std::ostream& operator<<(std::ostream& aStream,
const IMEState::Enabled& aEnabled);
std::ostream& operator<<(std::ostream& aStream, const IMEState::Open& aOpen);
std::ostream& operator<<(std::ostream& aStream, const IMEState& aState);
std::ostream& operator<<(std::ostream& aStream,
const InputContext::Origin& aOrigin);
std::ostream& operator<<(std::ostream& aStream, const InputContext& aContext);
std::ostream& operator<<(std::ostream& aStream,
const InputContextAction::Cause& aCause);
std::ostream& operator<<(std::ostream& aStream,
const InputContextAction::FocusChange& aFocusChange);
std::ostream& operator<<(std::ostream& aStream,
const IMENotification::SelectionChangeDataBase& aData);
std::ostream& operator<<(std::ostream& aStream,
const IMENotification::TextChangeDataBase& aData);
} // namespace widget
} // namespace mozilla
#endif // #ifndef mozilla_widget_IMEData_h_