forked from mirrors/gecko-dev
		
	 8821cd8445
			
		
	
	
		8821cd8445
		
	
	
	
	
		
			
			GBoard has image keyboard support, and spec issue is resolved [*1]. So this adds image keyboard support via GBoard and Android 7.1+. *1 https://github.com/w3c/input-events/issues/117 Differential Revision: https://phabricator.services.mozilla.com/D157713
		
			
				
	
	
		
			286 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
 | |
|  * 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_GeckoEditableSupport_h
 | |
| #define mozilla_widget_GeckoEditableSupport_h
 | |
| 
 | |
| #include "nsAppShell.h"
 | |
| #include "nsIWidget.h"
 | |
| #include "nsTArray.h"
 | |
| 
 | |
| #include "mozilla/java/GeckoEditableChildNatives.h"
 | |
| #include "mozilla/java/SessionTextInputWrappers.h"
 | |
| #include "mozilla/TextEventDispatcher.h"
 | |
| #include "mozilla/TextEventDispatcherListener.h"
 | |
| #include "mozilla/UniquePtr.h"
 | |
| 
 | |
| class nsWindow;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| class TextComposition;
 | |
| 
 | |
| namespace dom {
 | |
| class BrowserChild;
 | |
| }
 | |
| 
 | |
| namespace widget {
 | |
| 
 | |
| class GeckoEditableSupport final
 | |
|     : public TextEventDispatcherListener,
 | |
|       public java::GeckoEditableChild::Natives<GeckoEditableSupport> {
 | |
|   /*
 | |
|       Rules for managing IME between Gecko and Java:
 | |
| 
 | |
|       * Gecko controls the text content, and Java shadows the Gecko text
 | |
|          through text updates
 | |
|       * Gecko and Java maintain separate selections, and synchronize when
 | |
|          needed through selection updates and set-selection events
 | |
|       * Java controls the composition, and Gecko shadows the Java
 | |
|          composition through update composition events
 | |
|   */
 | |
| 
 | |
|   using EditableBase = java::GeckoEditableChild::Natives<GeckoEditableSupport>;
 | |
|   using EditableClient = java::SessionTextInput::EditableClient;
 | |
|   using EditableListener = java::SessionTextInput::EditableListener;
 | |
| 
 | |
|   enum FlushChangesFlag {
 | |
|     // Not retrying.
 | |
|     FLUSH_FLAG_NONE,
 | |
|     // Retrying due to IME text changes during flush.
 | |
|     FLUSH_FLAG_RETRY,
 | |
|     // Retrying due to IME sync exceptions during flush.
 | |
|     FLUSH_FLAG_RECOVER
 | |
|   };
 | |
| 
 | |
|   enum RemoveCompositionFlag { CANCEL_IME_COMPOSITION, COMMIT_IME_COMPOSITION };
 | |
| 
 | |
|   const bool mIsRemote;
 | |
|   jni::NativeWeakPtr<GeckoViewSupport> mWindow;  // Parent only
 | |
|   RefPtr<TextEventDispatcher> mDispatcher;
 | |
|   java::GeckoEditableChild::GlobalRef mEditable;
 | |
|   bool mEditableAttached;
 | |
|   InputContext mInputContext;
 | |
|   AutoTArray<UniquePtr<mozilla::WidgetEvent>, 4> mIMEKeyEvents;
 | |
|   IMENotification::TextChangeData mIMEPendingTextChange;
 | |
|   RefPtr<TextRangeArray> mIMERanges;
 | |
|   RefPtr<Runnable> mDisposeRunnable;
 | |
|   int32_t mIMEMaskEventsCount;         // Mask events when > 0.
 | |
|   int32_t mIMEFocusCount;              // We are focused when > 0.
 | |
|   bool mIMEDelaySynchronizeReply;      // We reply asynchronously when true.
 | |
|   int32_t mIMEActiveSynchronizeCount;  // The number of replies being delayed.
 | |
|   int32_t mIMEActiveCompositionCount;  // The number of compositions expected.
 | |
|   uint32_t mDisposeBlockCount;
 | |
|   bool mIMESelectionChanged;
 | |
|   bool mIMETextChangedDuringFlush;
 | |
|   bool mIMEMonitorCursor;
 | |
| 
 | |
|   // The cached selection data
 | |
|   struct Selection {
 | |
|     Selection() : mStartOffset(-1), mEndOffset(-1) {}
 | |
| 
 | |
|     void Reset() {
 | |
|       mStartOffset = -1;
 | |
|       mEndOffset = -1;
 | |
|     }
 | |
| 
 | |
|     bool IsValid() const { return mStartOffset >= 0 && mEndOffset >= 0; }
 | |
| 
 | |
|     int32_t mStartOffset;
 | |
|     int32_t mEndOffset;
 | |
|   } mCachedSelection;
 | |
| 
 | |
|   nsIWidget* GetWidget() const;
 | |
|   nsWindow* GetNsWindow() const;
 | |
| 
 | |
|   nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher) {
 | |
|     if (mIsRemote) {
 | |
|       return aDispatcher->BeginInputTransaction(this);
 | |
|     } else {
 | |
|       return aDispatcher->BeginNativeInputTransaction();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   virtual ~GeckoEditableSupport() {}
 | |
| 
 | |
|   RefPtr<TextComposition> GetComposition() const;
 | |
|   bool RemoveComposition(RemoveCompositionFlag aFlag = COMMIT_IME_COMPOSITION);
 | |
|   void SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg);
 | |
|   void AddIMETextChange(const IMENotification::TextChangeDataBase& aChange);
 | |
|   void PostFlushIMEChanges();
 | |
|   void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
 | |
|   void FlushIMEText(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
 | |
|   void AsyncNotifyIME(int32_t aNotification);
 | |
|   void UpdateCompositionRects();
 | |
|   bool DoReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);
 | |
|   bool DoUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);
 | |
|   void OnNotifyIMEOfCompositionEventHandled();
 | |
|   void NotifyIMEContext(const InputContext& aContext,
 | |
|                         const InputContextAction& aAction);
 | |
| 
 | |
|  public:
 | |
|   template <typename Functor>
 | |
|   static void OnNativeCall(Functor&& aCall) {
 | |
|     struct IMEEvent : nsAppShell::LambdaEvent<Functor> {
 | |
|       explicit IMEEvent(Functor&& l)
 | |
|           : nsAppShell::LambdaEvent<Functor>(std::move(l)) {}
 | |
| 
 | |
|       bool IsUIEvent() const override {
 | |
|         using GES = GeckoEditableSupport;
 | |
|         if (this->lambda.IsTarget(&GES::OnKeyEvent) ||
 | |
|             this->lambda.IsTarget(&GES::OnImeReplaceText) ||
 | |
|             this->lambda.IsTarget(&GES::OnImeUpdateComposition)) {
 | |
|           return true;
 | |
|         }
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       void Run() override {
 | |
|         if (NS_WARN_IF(!this->lambda.GetNativeObject())) {
 | |
|           // Ignore stale calls after disposal.
 | |
|           jni::GetGeckoThreadEnv()->ExceptionClear();
 | |
|           return;
 | |
|         }
 | |
|         nsAppShell::LambdaEvent<Functor>::Run();
 | |
|       }
 | |
|     };
 | |
|     nsAppShell::PostEvent(mozilla::MakeUnique<IMEEvent>(std::move(aCall)));
 | |
|   }
 | |
| 
 | |
|   static void SetOnBrowserChild(dom::BrowserChild* aBrowserChild);
 | |
| 
 | |
|   // Constructor for main process GeckoEditableChild.
 | |
|   GeckoEditableSupport(jni::NativeWeakPtr<GeckoViewSupport> aWindow,
 | |
|                        java::GeckoEditableChild::Param aEditableChild)
 | |
|       : mIsRemote(!aWindow.IsAttached()),
 | |
|         mWindow(aWindow),
 | |
|         mEditable(aEditableChild),
 | |
|         mEditableAttached(!mIsRemote),
 | |
|         mIMERanges(new TextRangeArray()),
 | |
|         mIMEMaskEventsCount(1),  // Mask IME events since there's no focus yet
 | |
|         mIMEFocusCount(0),
 | |
|         mIMEDelaySynchronizeReply(false),
 | |
|         mIMEActiveSynchronizeCount(0),
 | |
|         mDisposeBlockCount(0),
 | |
|         mIMESelectionChanged(false),
 | |
|         mIMETextChangedDuringFlush(false),
 | |
|         mIMEMonitorCursor(false) {}
 | |
| 
 | |
|   // Constructor for content process GeckoEditableChild.
 | |
|   explicit GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
 | |
|       : GeckoEditableSupport(nullptr, aEditableChild) {}
 | |
| 
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   // TextEventDispatcherListener methods
 | |
|   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
 | |
|                        const IMENotification& aNotification) override;
 | |
| 
 | |
|   NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
 | |
| 
 | |
|   NS_IMETHOD_(void)
 | |
|   OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
 | |
| 
 | |
|   NS_IMETHOD_(void)
 | |
|   WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher,
 | |
|                             WidgetKeyboardEvent& aKeyboardEvent,
 | |
|                             uint32_t aIndexOfKeypress, void* aData) override;
 | |
| 
 | |
|   void SetInputContext(const InputContext& aContext,
 | |
|                        const InputContextAction& aAction);
 | |
| 
 | |
|   InputContext GetInputContext();
 | |
| 
 | |
|   bool HasIMEFocus() const { return mIMEFocusCount != 0; }
 | |
| 
 | |
|   void AddBlocker() { mDisposeBlockCount++; }
 | |
| 
 | |
|   void ReleaseBlocker() {
 | |
|     mDisposeBlockCount--;
 | |
| 
 | |
|     if (!mDisposeBlockCount && mDisposeRunnable) {
 | |
|       if (HasIMEFocus()) {
 | |
|         // If we have IME focus, GeckoEditableChild is already attached again.
 | |
|         // So disposer is unnecessary.
 | |
|         mDisposeRunnable = nullptr;
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       RefPtr<GeckoEditableSupport> self(this);
 | |
|       RefPtr<Runnable> disposer = std::move(mDisposeRunnable);
 | |
| 
 | |
|       nsAppShell::PostEvent(
 | |
|           [self = std::move(self), disposer = std::move(disposer)] {
 | |
|             self->mEditableAttached = false;
 | |
|             disposer->Run();
 | |
|           });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool IsGeckoEditableUsed() const { return mDisposeBlockCount != 0; }
 | |
| 
 | |
|   // GeckoEditableChild methods
 | |
|   using EditableBase::AttachNative;
 | |
|   using EditableBase::DisposeNative;
 | |
| 
 | |
|   const java::GeckoEditableChild::Ref& GetJavaEditable() { return mEditable; }
 | |
| 
 | |
|   void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
 | |
|     RefPtr<GeckoEditableSupport> self(this);
 | |
|     nsAppShell::PostEvent(
 | |
|         [self = std::move(self), disposer = RefPtr<Runnable>(aDisposer)] {
 | |
|           if (self->IsGeckoEditableUsed()) {
 | |
|             // Current calling stack uses GeckoEditableChild, so we should
 | |
|             // not dispose it now.
 | |
|             self->mDisposeRunnable = disposer;
 | |
|             return;
 | |
|           }
 | |
|           self->mEditableAttached = false;
 | |
|           disposer->Run();
 | |
|         });
 | |
|   }
 | |
| 
 | |
|   // Transfer to a new parent.
 | |
|   void TransferParent(jni::Object::Param aEditableParent);
 | |
| 
 | |
|   // Handle an Android KeyEvent.
 | |
|   void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
 | |
|                   int32_t aMetaState, int32_t aKeyPressMetaState, int64_t aTime,
 | |
|                   int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
 | |
|                   int32_t aFlags, bool aIsSynthesizedImeKey,
 | |
|                   jni::Object::Param originalEvent);
 | |
| 
 | |
|   // Synchronize Gecko thread with the InputConnection thread.
 | |
|   void OnImeSynchronize();
 | |
| 
 | |
|   // Replace a range of text with new text.
 | |
|   void OnImeReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);
 | |
| 
 | |
|   // Add styling for a range within the active composition.
 | |
|   void OnImeAddCompositionRange(int32_t aStart, int32_t aEnd,
 | |
|                                 int32_t aRangeType, int32_t aRangeStyle,
 | |
|                                 int32_t aRangeLineStyle, bool aRangeBoldLine,
 | |
|                                 int32_t aRangeForeColor,
 | |
|                                 int32_t aRangeBackColor,
 | |
|                                 int32_t aRangeLineColor);
 | |
| 
 | |
|   // Update styling for the active composition using previous-added ranges.
 | |
|   void OnImeUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);
 | |
| 
 | |
|   // Set cursor mode whether IME requests
 | |
|   void OnImeRequestCursorUpdates(int aRequestMode);
 | |
| 
 | |
|   // Commit current composition to sync Gecko text state with Java.
 | |
|   void OnImeRequestCommit();
 | |
| 
 | |
|   // Insert image from software keyboard.
 | |
|   void OnImeInsertImage(jni::ByteArray::Param aData,
 | |
|                         jni::String::Param aMimeType);
 | |
| };
 | |
| 
 | |
| }  // namespace widget
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif  // mozilla_widget_GeckoEditableSupport_h
 |