forked from mirrors/gecko-dev
		
	 6cf432407d
			
		
	
	
		6cf432407d
		
	
	
	
	
		
			
			I have to consider that we have to set input-purpose even if inputmode only is changed. Differential Revision: https://phabricator.services.mozilla.com/D125209
		
			
				
	
	
		
			983 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			983 lines
		
	
	
	
		
			34 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/CheckedInt.h"
 | |
| #include "mozilla/EventForwards.h"
 | |
| #include "mozilla/ToString.h"
 | |
| 
 | |
| #include "nsPoint.h"
 | |
| #include "nsRect.h"
 | |
| #include "nsString.h"
 | |
| #include "nsXULAppAPI.h"
 | |
| #include "Units.h"
 | |
| 
 | |
| class nsIWidget;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| class WritingMode;
 | |
| 
 | |
| // Helper class to logging string which may contain various Unicode characters
 | |
| // and/or may be too long string for logging.
 | |
| class MOZ_STACK_CLASS PrintStringDetail : public nsAutoCString {
 | |
|  public:
 | |
|   static constexpr uint32_t kMaxLengthForCompositionString = 8;
 | |
|   static constexpr uint32_t kMaxLengthForSelectedString = 12;
 | |
|   static constexpr uint32_t kMaxLengthForEditor = 20;
 | |
| 
 | |
|   PrintStringDetail() = delete;
 | |
|   explicit PrintStringDetail(const nsAString& aString,
 | |
|                              uint32_t aMaxLength = UINT32_MAX);
 | |
| 
 | |
|  private:
 | |
|   static nsCString PrintCharData(char32_t aChar);
 | |
| };
 | |
| 
 | |
| // StartAndEndOffsets represents a range in flat-text.
 | |
| template <typename IntType>
 | |
| class StartAndEndOffsets {
 | |
|  protected:
 | |
|   static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
 | |
| 
 | |
|  public:
 | |
|   StartAndEndOffsets() = delete;
 | |
|   explicit StartAndEndOffsets(IntType aStartOffset, IntType aEndOffset)
 | |
|       : mStartOffset(aStartOffset),
 | |
|         mEndOffset(aStartOffset <= aEndOffset ? aEndOffset : aStartOffset) {
 | |
|     MOZ_ASSERT(aStartOffset <= mEndOffset);
 | |
|   }
 | |
| 
 | |
|   IntType StartOffset() const { return mStartOffset; }
 | |
|   IntType Length() const { return mEndOffset - mStartOffset; }
 | |
|   IntType EndOffset() const { return mEndOffset; }
 | |
| 
 | |
|   bool IsOffsetInRange(IntType aOffset) const {
 | |
|     return aOffset >= mStartOffset && aOffset < mEndOffset;
 | |
|   }
 | |
|   bool IsOffsetInRangeOrEndOffset(IntType aOffset) const {
 | |
|     return aOffset >= mStartOffset && aOffset <= mEndOffset;
 | |
|   }
 | |
| 
 | |
|   void MoveTo(IntType aNewStartOffset) {
 | |
|     auto delta = static_cast<int64_t>(mStartOffset) - aNewStartOffset;
 | |
|     mStartOffset += delta;
 | |
|     mEndOffset += delta;
 | |
|   }
 | |
|   void SetOffsetAndLength(IntType aNewOffset, IntType aNewLength) {
 | |
|     mStartOffset = aNewOffset;
 | |
|     CheckedInt<IntType> endOffset(aNewOffset + aNewLength);
 | |
|     mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
 | |
|   }
 | |
|   void SetEndOffset(IntType aEndOffset) {
 | |
|     MOZ_ASSERT(mStartOffset <= aEndOffset);
 | |
|     mEndOffset = std::max(aEndOffset, mStartOffset);
 | |
|   }
 | |
|   void SetStartAndEndOffsets(IntType aStartOffset, IntType aEndOffset) {
 | |
|     MOZ_ASSERT(aStartOffset <= aEndOffset);
 | |
|     mStartOffset = aStartOffset;
 | |
|     mEndOffset = aStartOffset <= aEndOffset ? aEndOffset : aStartOffset;
 | |
|   }
 | |
|   void SetLength(IntType aNewLength) {
 | |
|     CheckedInt<IntType> endOffset(mStartOffset + aNewLength);
 | |
|     mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
 | |
|   }
 | |
| 
 | |
|   friend std::ostream& operator<<(
 | |
|       std::ostream& aStream,
 | |
|       const StartAndEndOffsets<IntType>& aStartAndEndOffsets) {
 | |
|     aStream << "{ mStartOffset=" << aStartAndEndOffsets.mStartOffset
 | |
|             << ", mEndOffset=" << aStartAndEndOffsets.mEndOffset
 | |
|             << ", Length()=" << aStartAndEndOffsets.Length() << " }";
 | |
|     return aStream;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   IntType mStartOffset;
 | |
|   IntType mEndOffset;
 | |
| };
 | |
| 
 | |
| // OffsetAndData class is designed for storing composition string and its
 | |
| // start offset.  Length() and EndOffset() return only valid length or
 | |
| // offset.  I.e., if the string is too long for inserting at the offset,
 | |
| // the length is shrunken.  However, the string itself is not shrunken.
 | |
| // Therefore, moving it to where all of the string can be contained,
 | |
| // they will return longer/bigger value.
 | |
| enum class OffsetAndDataFor {
 | |
|   CompositionString,
 | |
|   SelectedString,
 | |
|   EditorString,
 | |
| };
 | |
| template <typename IntType>
 | |
| class OffsetAndData {
 | |
|  protected:
 | |
|   static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
 | |
| 
 | |
|  public:
 | |
|   OffsetAndData() = delete;
 | |
|   explicit OffsetAndData(
 | |
|       IntType aStartOffset, const nsAString& aData,
 | |
|       OffsetAndDataFor aFor = OffsetAndDataFor::CompositionString)
 | |
|       : mData(aData), mOffset(aStartOffset), mFor(aFor) {}
 | |
| 
 | |
|   IntType StartOffset() const { return mOffset; }
 | |
|   IntType Length() const {
 | |
|     CheckedInt<IntType> endOffset(mOffset + mData.Length());
 | |
|     return endOffset.isValid() ? mData.Length() : MaxOffset() - mOffset;
 | |
|   }
 | |
|   IntType EndOffset() const { return mOffset + Length(); }
 | |
|   StartAndEndOffsets<IntType> CreateStartAndEndOffsets() const {
 | |
|     return StartAndEndOffsets<IntType>(StartOffset(), EndOffset());
 | |
|   }
 | |
|   const nsString& DataRef() const {
 | |
|     // In strictly speaking, we should return substring which may be shrunken
 | |
|     // for rounding to the max offset.  However, it's unrealistic edge case,
 | |
|     // and creating new string is not so cheap job in a hot path.  Therefore,
 | |
|     // this just returns the data as-is.
 | |
|     return mData;
 | |
|   }
 | |
|   bool IsDataEmpty() const { return mData.IsEmpty(); }
 | |
| 
 | |
|   bool IsOffsetInRange(IntType aOffset) const {
 | |
|     return aOffset >= mOffset && aOffset < EndOffset();
 | |
|   }
 | |
|   bool IsOffsetInRangeOrEndOffset(IntType aOffset) const {
 | |
|     return aOffset >= mOffset && aOffset <= EndOffset();
 | |
|   }
 | |
| 
 | |
|   void MoveTo(IntType aNewOffset) { mOffset = aNewOffset; }
 | |
|   void SetOffsetAndData(IntType aStartOffset, const nsAString& aData) {
 | |
|     mOffset = aStartOffset;
 | |
|     mData = aData;
 | |
|   }
 | |
|   void SetData(const nsAString& aData) { mData = aData; }
 | |
|   void TruncateData(uint32_t aLength = 0) { mData.Truncate(aLength); }
 | |
|   void ReplaceData(nsAString::size_type aCutStart,
 | |
|                    nsAString::size_type aCutLength,
 | |
|                    const nsAString& aNewString) {
 | |
|     mData.Replace(aCutStart, aCutLength, aNewString);
 | |
|   }
 | |
| 
 | |
|   friend std::ostream& operator<<(
 | |
|       std::ostream& aStream, const OffsetAndData<IntType>& aOffsetAndData) {
 | |
|     const auto maxDataLength =
 | |
|         aOffsetAndData.mFor == OffsetAndDataFor::CompositionString
 | |
|             ? PrintStringDetail::kMaxLengthForCompositionString
 | |
|             : (aOffsetAndData.mFor == OffsetAndDataFor::SelectedString
 | |
|                    ? PrintStringDetail::kMaxLengthForSelectedString
 | |
|                    : PrintStringDetail::kMaxLengthForEditor);
 | |
|     aStream << "{ mOffset=" << aOffsetAndData.mOffset << ", mData="
 | |
|             << PrintStringDetail(aOffsetAndData.mData, maxDataLength).get()
 | |
|             << ", Length()=" << aOffsetAndData.Length()
 | |
|             << ", EndOffset()=" << aOffsetAndData.EndOffset() << " }";
 | |
|     return aStream;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   nsString mData;
 | |
|   IntType mOffset;
 | |
|   OffsetAndDataFor mFor;
 | |
| };
 | |
| 
 | |
| 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;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * IME enabled states.
 | |
|  *
 | |
|  * WARNING: If you change these values, you also need to edit:
 | |
|  *   nsIDOMWindowUtils.idl
 | |
|  */
 | |
| enum class IMEEnabled {
 | |
|   /**
 | |
|    * '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,
 | |
|   /**
 | |
|    * 'Unknown' is useful when you cache this enum.  So, this shouldn't be
 | |
|    * used with nsIWidget::SetInputContext().
 | |
|    */
 | |
|   Unknown,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Contains IMEStatus plus information about the current
 | |
|  * input context that the IME can use as hints if desired.
 | |
|  */
 | |
| 
 | |
| struct IMEState final {
 | |
|   IMEEnabled 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(IMEEnabled::Enabled), mOpen(DONT_CHANGE_OPEN_STATE) {}
 | |
| 
 | |
|   explicit IMEState(IMEEnabled 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 == IMEEnabled::Enabled || mEnabled == IMEEnabled::Password;
 | |
|   }
 | |
| };
 | |
| 
 | |
| // 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");
 | |
|   }
 | |
| 
 | |
|   bool IsInputAttributeChanged(const InputContext& aOldContext) const {
 | |
|     return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled ||
 | |
| #if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
 | |
|            // input type and inputmode are supported by Windows IME API, GTK
 | |
|            // IME API and Android IME API
 | |
|            mHTMLInputType != aOldContext.mHTMLInputType ||
 | |
|            mHTMLInputInputmode != aOldContext.mHTMLInputInputmode ||
 | |
| #endif
 | |
| #if defined(ANDROID) || defined(MOZ_WIDGET_GTK)
 | |
|            // autocapitalize is supported by Android IME API and GTK IME API
 | |
|            mAutocapitalize != aOldContext.mAutocapitalize ||
 | |
| #endif
 | |
| #if defined(ANDROID)
 | |
|            // enterkeyhint is only supported by Android IME API.
 | |
|            mActionHint != aOldContext.mActionHint ||
 | |
| #endif
 | |
|            false;
 | |
|   }
 | |
| 
 | |
|   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.MoveTo(0, 0);
 | |
|         mMouseButtonEventData.mCharRect.SetRect(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;
 | |
| 
 | |
|   // 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
 | |
|     LayoutDeviceIntPoint mCursorPos;
 | |
|     // Character rect in pixels under the cursor relative to the widget
 | |
|     LayoutDeviceIntRect 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 IMEEnabled& 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_
 |