1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_widget_GeckoEditableSupport_h
7 #define mozilla_widget_GeckoEditableSupport_h
9 #include "nsAppShell.h"
10 #include "nsIWidget.h"
13 #include "mozilla/java/GeckoEditableChildNatives.h"
14 #include "mozilla/java/SessionTextInputWrappers.h"
15 #include "mozilla/TextEventDispatcher.h"
16 #include "mozilla/TextEventDispatcherListener.h"
17 #include "mozilla/UniquePtr.h"
23 class TextComposition
;
31 class GeckoEditableSupport final
32 : public TextEventDispatcherListener
,
33 public java::GeckoEditableChild::Natives
<GeckoEditableSupport
> {
35 Rules for managing IME between Gecko and Java:
37 * Gecko controls the text content, and Java shadows the Gecko text
39 * Gecko and Java maintain separate selections, and synchronize when
40 needed through selection updates and set-selection events
41 * Java controls the composition, and Gecko shadows the Java
42 composition through update composition events
45 using EditableBase
= java::GeckoEditableChild::Natives
<GeckoEditableSupport
>;
46 using EditableClient
= java::SessionTextInput::EditableClient
;
47 using EditableListener
= java::SessionTextInput::EditableListener
;
49 enum FlushChangesFlag
{
52 // Retrying due to IME text changes during flush.
54 // Retrying due to IME sync exceptions during flush.
58 enum RemoveCompositionFlag
{ CANCEL_IME_COMPOSITION
, COMMIT_IME_COMPOSITION
};
61 jni::NativeWeakPtr
<GeckoViewSupport
> mWindow
; // Parent only
62 RefPtr
<TextEventDispatcher
> mDispatcher
;
63 java::GeckoEditableChild::GlobalRef mEditable
;
64 bool mEditableAttached
;
65 InputContext mInputContext
;
66 AutoTArray
<UniquePtr
<mozilla::WidgetEvent
>, 4> mIMEKeyEvents
;
67 IMENotification::TextChangeData mIMEPendingTextChange
;
68 RefPtr
<TextRangeArray
> mIMERanges
;
69 RefPtr
<Runnable
> mDisposeRunnable
;
70 int32_t mIMEMaskEventsCount
; // Mask events when > 0.
71 int32_t mIMEFocusCount
; // We are focused when > 0.
72 bool mIMEDelaySynchronizeReply
; // We reply asynchronously when true.
73 int32_t mIMEActiveSynchronizeCount
; // The number of replies being delayed.
74 int32_t mIMEActiveCompositionCount
; // The number of compositions expected.
75 uint32_t mDisposeBlockCount
;
76 bool mIMESelectionChanged
;
77 bool mIMETextChangedDuringFlush
;
78 bool mIMEMonitorCursor
;
80 nsIWidget
* GetWidget() const;
81 nsWindow
* GetNsWindow() const;
83 nsresult
BeginInputTransaction(TextEventDispatcher
* aDispatcher
) {
85 return aDispatcher
->BeginInputTransaction(this);
87 return aDispatcher
->BeginNativeInputTransaction();
91 virtual ~GeckoEditableSupport() {}
93 RefPtr
<TextComposition
> GetComposition() const;
94 bool RemoveComposition(RemoveCompositionFlag aFlag
= COMMIT_IME_COMPOSITION
);
95 void SendIMEDummyKeyEvent(nsIWidget
* aWidget
, EventMessage msg
);
96 void AddIMETextChange(const IMENotification::TextChangeDataBase
& aChange
);
97 void PostFlushIMEChanges();
98 void FlushIMEChanges(FlushChangesFlag aFlags
= FLUSH_FLAG_NONE
);
99 void FlushIMEText(FlushChangesFlag aFlags
= FLUSH_FLAG_NONE
);
100 void AsyncNotifyIME(int32_t aNotification
);
101 void UpdateCompositionRects();
102 bool DoReplaceText(int32_t aStart
, int32_t aEnd
, jni::String::Param aText
);
103 bool DoUpdateComposition(int32_t aStart
, int32_t aEnd
, int32_t aFlags
);
104 void OnNotifyIMEOfCompositionEventHandled();
105 void NotifyIMEContext(const InputContext
& aContext
,
106 const InputContextAction
& aAction
);
109 template <typename Functor
>
110 static void OnNativeCall(Functor
&& aCall
) {
111 struct IMEEvent
: nsAppShell::LambdaEvent
<Functor
> {
112 explicit IMEEvent(Functor
&& l
)
113 : nsAppShell::LambdaEvent
<Functor
>(std::move(l
)) {}
115 bool IsUIEvent() const override
{
116 using GES
= GeckoEditableSupport
;
117 if (this->lambda
.IsTarget(&GES::OnKeyEvent
) ||
118 this->lambda
.IsTarget(&GES::OnImeReplaceText
) ||
119 this->lambda
.IsTarget(&GES::OnImeUpdateComposition
)) {
125 void Run() override
{
126 if (NS_WARN_IF(!this->lambda
.GetNativeObject())) {
127 // Ignore stale calls after disposal.
128 jni::GetGeckoThreadEnv()->ExceptionClear();
131 nsAppShell::LambdaEvent
<Functor
>::Run();
134 nsAppShell::PostEvent(mozilla::MakeUnique
<IMEEvent
>(std::move(aCall
)));
137 static void SetOnBrowserChild(dom::BrowserChild
* aBrowserChild
);
139 // Constructor for main process GeckoEditableChild.
140 GeckoEditableSupport(jni::NativeWeakPtr
<GeckoViewSupport
> aWindow
,
141 java::GeckoEditableChild::Param aEditableChild
)
142 : mIsRemote(!aWindow
.IsAttached()),
144 mEditable(aEditableChild
),
145 mEditableAttached(!mIsRemote
),
146 mIMERanges(new TextRangeArray()),
147 mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet
149 mIMEDelaySynchronizeReply(false),
150 mIMEActiveSynchronizeCount(0),
151 mDisposeBlockCount(0),
152 mIMESelectionChanged(false),
153 mIMETextChangedDuringFlush(false),
154 mIMEMonitorCursor(false) {}
156 // Constructor for content process GeckoEditableChild.
157 explicit GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild
)
158 : GeckoEditableSupport(nullptr, aEditableChild
) {}
162 // TextEventDispatcherListener methods
163 NS_IMETHOD
NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
164 const IMENotification
& aNotification
) override
;
166 NS_IMETHOD_(IMENotificationRequests
) GetIMENotificationRequests() override
;
169 OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) override
;
172 WillDispatchKeyboardEvent(TextEventDispatcher
* aTextEventDispatcher
,
173 WidgetKeyboardEvent
& aKeyboardEvent
,
174 uint32_t aIndexOfKeypress
, void* aData
) override
;
176 void SetInputContext(const InputContext
& aContext
,
177 const InputContextAction
& aAction
);
179 InputContext
GetInputContext();
181 bool HasIMEFocus() const { return mIMEFocusCount
!= 0; }
183 void AddBlocker() { mDisposeBlockCount
++; }
185 void ReleaseBlocker() {
186 mDisposeBlockCount
--;
188 if (!mDisposeBlockCount
&& mDisposeRunnable
) {
190 // If we have IME focus, GeckoEditableChild is already attached again.
191 // So disposer is unnecessary.
192 mDisposeRunnable
= nullptr;
196 RefPtr
<GeckoEditableSupport
> self(this);
197 RefPtr
<Runnable
> disposer
= std::move(mDisposeRunnable
);
199 nsAppShell::PostEvent(
200 [self
= std::move(self
), disposer
= std::move(disposer
)] {
201 self
->mEditableAttached
= false;
207 bool IsGeckoEditableUsed() const { return mDisposeBlockCount
!= 0; }
209 // GeckoEditableChild methods
210 using EditableBase::AttachNative
;
211 using EditableBase::DisposeNative
;
213 const java::GeckoEditableChild::Ref
& GetJavaEditable() { return mEditable
; }
215 void OnWeakNonIntrusiveDetach(already_AddRefed
<Runnable
> aDisposer
) {
216 RefPtr
<GeckoEditableSupport
> self(this);
217 nsAppShell::PostEvent(
218 [self
= std::move(self
), disposer
= RefPtr
<Runnable
>(aDisposer
)] {
219 if (self
->IsGeckoEditableUsed()) {
220 // Current calling stack uses GeckoEditableChild, so we should
221 // not dispose it now.
222 self
->mDisposeRunnable
= disposer
;
225 self
->mEditableAttached
= false;
230 // Transfer to a new parent.
231 void TransferParent(jni::Object::Param aEditableParent
);
233 // Handle an Android KeyEvent.
234 void OnKeyEvent(int32_t aAction
, int32_t aKeyCode
, int32_t aScanCode
,
235 int32_t aMetaState
, int32_t aKeyPressMetaState
, int64_t aTime
,
236 int32_t aDomPrintableKeyValue
, int32_t aRepeatCount
,
237 int32_t aFlags
, bool aIsSynthesizedImeKey
,
238 jni::Object::Param originalEvent
);
240 // Synchronize Gecko thread with the InputConnection thread.
241 void OnImeSynchronize();
243 // Replace a range of text with new text.
244 void OnImeReplaceText(int32_t aStart
, int32_t aEnd
, jni::String::Param aText
);
246 // Add styling for a range within the active composition.
247 void OnImeAddCompositionRange(int32_t aStart
, int32_t aEnd
,
248 int32_t aRangeType
, int32_t aRangeStyle
,
249 int32_t aRangeLineStyle
, bool aRangeBoldLine
,
250 int32_t aRangeForeColor
,
251 int32_t aRangeBackColor
,
252 int32_t aRangeLineColor
);
254 // Update styling for the active composition using previous-added ranges.
255 void OnImeUpdateComposition(int32_t aStart
, int32_t aEnd
, int32_t aFlags
);
257 // Set cursor mode whether IME requests
258 void OnImeRequestCursorUpdates(int aRequestMode
);
260 // Commit current composition to sync Gecko text state with Java.
261 void OnImeRequestCommit();
264 } // namespace widget
265 } // namespace mozilla
267 #endif // mozilla_widget_GeckoEditableSupport_h